Events
- Overview
- Listening for flushes
- Listening for invalidations
- Multitenancy limitations
- Client-side invalidation
- Manual event dispatching
Overview
Relay’s in-memory cache is exceptionally fast and reliable, however some scenarios such as long-running processes or highly concurrent applications can use Relay’s event listeners to avoid race conditions and stale caches.
After invalidating its in-memory cache, Relay will call any registered event listeners. There is a delay of 100μs
(or 0.1ms
) for Redis to inform Relay about invalidations, plus whatever the network latency is.
Relay has two event types:
- The
flushed
event is dispatched when the connection’s database was flushed, right after Relay wiped it’s in-memory cache - The
invalidated
event is dispatched when a key that the current process previously interacted with was altered in any way, right after Relay removed the key from it’s in-memory cache
Event listeners can be any PHP callable
type and are registered using the listen()
method:
$relay->listen(function (\Relay\Event $event) {
match ($event->type) {
$event::Flushed => flushCache(),
$event::Invalidated => deleteFromCache($event->key),
};
});
It’s worth noting that event listeners are connection specific, which means:
- a connection must be opened before registering any event listeners
- switching databases with
select()
will detach all event listeners - opening a divergent connection with
connect()
on the same object will detach all event listeners
Listening for flushes
In some cases the application might want to register a distinct flush listener. This can also be accomplished using the onFlushed()
method:
$relay->onFlushed(callable $callback);
Listening for invalidations
In some cases the application might want to register one or more distinct invalidation listeners. This can also be accomplished using the onInvalidated()
method:
$relay->onInvalidated(callable $callback);
However, note that database flushes will not dispatch individual invalidation events and must be handled separately using onFlushed()
.
Furthermore, invalidation event listeners can be restricted to a wildcard match:
$relay->onInvalidated($callback, 'api:*');
// named arguments for readability
$relay->onInvalidated(
match: 'api:*',
callback: fn ($event) => deleteApiCacheKey($event->key)
);
Multitenancy limitations
Redis’ invalidation system is not designed for use with multiple databases and will cause unnecessary flushing of Relay’s memory when more than one database is used.
To bypass most of these limitations, it is vital to set a unique prefix for each database connection. We suggest using the format db0:
, db1:
and so on.
$relay = new Relay(host: '127.0.0.1', database: 4);
$relay->setOption(Relay::OPT_PREFIX, 'db4:');
Unfortunately, due to Redis’ design, Relay isn’t aware of the database index when FLUSHDB
is called and will flush it’s entire memory. If you’re curious, read about the technical details.
Client-side invalidation
By default Relay will perform instantaneous client-side invalidation when a key is changed without waiting for Redis to send us an INVALIDATE
message. The invalidation occurs only in the same FPM pool.
If you want to disable this behavior and wait for TCP round trips, you can disable this behavior:
$relay->setOption(Relay::OPT_CLIENT_INVALIDATIONS, false);
Manual event dispatching
Despite of PHP’s synchronous nature, Relay will dispatch registered event listeners at regular checkpoints during the execution of userland code.
In some cases you may wish to trigger event callbacks manually, this can be done by calling the dispatchEvents()
method and is extremely fast and memory efficient.
$relay->dispatchEvents();
$relay->get('users:count');