Node Cache: Maintenance and Production#
This page covers scheduled SQLite maintenance, deployment, observability, and failure diagnosis.
Maintenance lifecycle#
Run maintenance independently on every node. Reads correctly ignore expired rows, but pruning prevents the local SQLite file from accumulating them.
use Infocyph\CacheLayer\Node\NodeCache;
$maintenance = NodeCache::maintenance($config);
$deleted = $maintenance->pruneExpired(5_000);
$maintenance->checkpoint();
$maintenance->optimize();
pruneExpired($limit)Deletes at most
$limitexpired rows in the configured namespace. It returns the number deleted. Run it periodically and keep the limit bounded.checkpoint()Requests a passive SQLite WAL checkpoint. It is suitable for regular maintenance because it does not force active writers to stop.
optimize()Runs SQLite
PRAGMA optimize. Schedule it less frequently than pruning, for example as part of a daily maintenance job.
A simple scheduled task can perform a small bounded prune every few minutes
and checkpoint periodically. Do not run VACUUM on the request path; plan it
separately if disk-space reclamation is required.
Example cron schedule#
CacheLayer does not install a cron entry or start a scheduler. Wire the maintenance API into your application’s CLI bootstrap, then schedule that command with the mechanism your deployment already uses.
// bin/cache-node-maintenance.php
$maintenance = NodeCache::maintenance($config);
$maintenance->pruneExpired(5_000);
$maintenance->checkpoint();
// Run optimize less frequently, for example from a separate daily command.
For a traditional cron-based deployment, a conservative starting point is:
# Every five minutes: bounded expired-row cleanup and passive WAL checkpoint.
*/5 * * * * www-data /usr/bin/php /srv/application/bin/cache-node-maintenance.php
# Daily: bootstrap a command that calls $maintenance->optimize().
17 3 * * * www-data /usr/bin/php /srv/application/bin/cache-node-optimize.php
Use your framework scheduler, Kubernetes CronJob, systemd timer, or another
platform scheduler instead when that is how application jobs are operated.
Only one maintenance task is needed per local SQLite file; it is safe to use a
bounded prune limit if overlap cannot be ruled out.
Deployment and process guidance#
Create the cache directory with ownership restricted to the application account before starting PHP-FPM/workers.
Persist the SQLite file on the node if warm cache across process restarts is useful. Treat it as disposable: deleting it only causes cold misses.
In containers, mount a node-local writable volume. Do not mount the same SQLite cache volume into multiple nodes.
Configure APCu for the PHP SAPI that actually serves traffic. A CLI command may not have the same APCu settings as PHP-FPM.
Use the same namespace for processes that should share a node-local cache; use distinct namespaces to isolate applications or domains.
Keep cache TTLs shorter than the maximum period during which stale data would be tolerable.
Observability and failures#
exportMetrics() exposes adapter counters, including cache hits/misses and
Node Cache layer events. Export it through your application’s telemetry path:
$metrics = $cache->exportMetrics();
Layer failures are represented by metrics such as APCu/SQLite failures when
failOpen is enabled. Alert on sustained failures, a sharp fall in hit rate,
rapid SQLite file growth, and repeated lock contention. With failOpen
disabled, also handle the propagated cache exception according to the request’s
availability requirements.
Troubleshooting#
APCu is not being usedCheck that
apcuEnabledis true, the APCu extension is loaded, and APCu is enabled for the active SAPI. SQLite-only operation is an intended fallback.SQLite database is lockedConfirm that the database is on a local filesystem, inspect concurrent local writers, then consider a modest
busyTimeoutMsincrease. Do not solve this by placing the file on a shared filesystem.Data looks stale on another serverThis is expected with Node Cache alone. Add Cluster Cache and consume its invalidation stream on every node, or rely on an appropriately short TTL.
The SQLite file keeps growingSchedule
pruneExpired()and periodic checkpoints. Expired rows are not removed by reads. Review TTL choices and cache cardinality as well.A cache failure should fail the requestCreate the configuration with
failOpen: falseand ensure the application handles the resulting exception at the appropriate boundary.