Answer
In Laminas‑Cache the clearByTag method is implemented as a sequence of Redis commands: read the tag set, delete each key, delete the tag set. Each command is atomic, but the whole sequence is not, so two concurrent calls can interleave and leave stale keys. The only way to guarantee indivisibility inside Redis is to wrap the whole operation in a Lua script that runs on the server side.
Atomic Lua‑script approach
- Load the script once. Create a Lua script that:
- Fetches all members of the tag set (using
SMEMBERS for small sets or a SSCAN loop for very large sets).
- Iterates over the members and issues
DEL for each key.
- Deletes the tag set itself (e.g.,
DEL tagKey).
- Register the script. Send the script to Redis with
SCRIPT LOAD and keep the returned SHA1.
- Execute with
EVALSHA. For every tag invalidation call the adapter to run EVALSHA <sha1> 1 tagKey. The script runs atomically, so no other client can modify the tag set while it is being processed.
Integrating with Laminas‑Cache
The Redis adapter allows passing a custom script via the clearByTagScript option (or by subclassing the adapter). Example configuration:
$cache = new Laminas\Cache\Storage\Adapter\Redis([
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
],
'clearByTagScript' => 'sha1-of-your-lua-script',
]);
When clearByTag is called, the adapter will automatically invoke the script via EVALSHA. No external locking is required.
Handling very large tag sets (O(n) key mapping)
- If a tag can contain thousands of keys, the Lua script may hit the maximum execution time or memory limits. In that case use a
SSCAN loop inside the script to delete keys in batches.
- Alternatively, split the tag into sub‑tags or use a time‑to‑live on keys so that stale entries expire naturally.
When to ask for more information
To fine‑tune the script you need to know the typical size of a tag set. If your tags regularly hold more than a few thousand keys, consider a chunking strategy or a background deletion job. If the set size is small, the simple SMEMBERS + DEL loop is sufficient.
Summary
Wrap the entire clearByTag logic in a Lua script executed with EVALSHA. Configure the Redis adapter to use that script. For large tag sets, employ SSCAN or batch deletions to stay within Redis limits. No external distributed lock is needed.