Node Cache: Cache Operations#

This page covers the normal application-facing Cache facade: reads, writes, tags, deferred items, and source-data invalidation patterns.

Normal cache operations#

NodeCache::create() returns Infocyph\CacheLayer\Cache\Cache. Do not reach into its SQLite adapter for ordinary application work. Use the facade.

Simple read and write#

$cache->set('product.42', ['id' => 42, 'name' => 'Keyboard'], 300);

$product = $cache->get('product.42');
$missing = $cache->get('does-not-exist', 'fallback');

$cache->delete('product.42');
$cache->clear(); // clears this namespace on this node only

set() accepts an integer TTL, a DateInterval, or null. null uses no explicit expiry; prefer bounded TTLs for data that can change. A TTL of zero expires immediately. Valid keys contain only letters, digits, _, ., and -.

Read-through caching with remember()#

Use remember() for the normal cache-aside read path. It reads first, then uses the configured local lock to prevent many concurrent requests on the same node from doing the same expensive work.

$product = $cache->remember(
    'product.42',
    function ($item) use ($repository) {
        $item->expiresAfter(300);

        return $repository->find(42);
    },
    tags: ['products', 'product.42'],
);

The lock is node-local. Two different servers may both recompute a missing value; that is expected for Node Cache. Use short, suitable TTLs and make the resolver safe to run more than once.

Tags and invalidation#

Tags group related cached values. CacheLayer invalidates them by changing local tag versions rather than scanning every cached key.

$cache->setTagged(
    'category.10.products',
    $products,
    ['products', 'category.10'],
    300,
);

$cache->invalidateTag('category.10');
$cache->invalidateTags(['products', 'search-results']);

Tag invalidation affects this node only. In a multi-node deployment, invalidateTag() on a Node Cache does not notify other machines; call ClusterRuntime::invalidateTag() instead when Cluster Cache is configured.

PSR-6 items and deferred writes#

The facade also acts as a PSR-6 pool. Deferred items are held in memory until commit() and are then written to SQLite in a batch before APCu is updated.

$first = $cache->getItem('product.42')
    ->set($product)
    ->expiresAfter(300);
$second = $cache->getItem('product.43')
    ->set($otherProduct)
    ->expiresAfter(300);

$cache->saveDeferred($first);
$cache->saveDeferred($second);
$cache->commit();

For simple batches, use the PSR-16 methods instead:

$cache->setMultiple([
    'product.42' => $product,
    'product.43' => $otherProduct,
], 300);

$products = $cache->getMultiple(['product.42', 'product.43']);
$cache->deleteMultiple(['product.42', 'product.43']);

saveDeferred() is for batching in one process. It does not survive a process crash before commit() and is not an asynchronous queue.

Common source-data workflows#

Create or update a record#

After the authoritative database transaction succeeds, either delete the exact key or invalidate the business tag. Avoid writing a speculative database value to cache before its transaction commits.

$database->transaction(function () use ($repository, $input): void {
    $repository->updateProduct(42, $input);
});

$cache->delete('product.42');
$cache->invalidateTags(['products', 'category.10']);

The next reader rebuilds entries through remember(). This approach avoids having cache state become the source of truth.

Delete a record#

Delete the exact record key and any aggregate tags after the source deletion is committed:

$repository->deleteProduct(42);
$cache->delete('product.42');
$cache->invalidateTag('products');

When a direct key and its aggregate/list views may be cached, invalidating both is normal and safe.