Skip to content

client cache: None and KeyspaceNotifications modes, and Default falling back to keyspace notifications - #3305

Open
mgravell wants to merge 3 commits into
v4from
marc/cache-invalidation-strategies
Open

mgravell wants to merge 3 commits into
v4from
marc/cache-invalidation-strategies

Conversation

@mgravell

Copy link
Copy Markdown
Collaborator

Adds two client-side cache invalidation strategies that don't use CLIENT TRACKING, and has CacheTrackingMode.Default fall back to one of them instead of failing to connect.

New modes (CacheTrackingMode)

  • None (now the zero value): nothing is negotiated; entries are retired by TimeToLive and by writes through this multiplexer. Works over RESP2 and against any server. Prefixes, if given, only bound what is cached.
  • KeyspaceNotifications: the cache subscribes to __keyspace@*__:{prefix}* per prefix, or __keyspace@*__:* without prefixes, and invalidates the named key on any event. These are the existing multi-node keyspace channels, so in a cluster the subscription is made on every node (notifications are raised only by the node owning the key). Works over RESP2. If the server has notifications off, the subscription is silent and it behaves as None; that's documented rather than enforced. Prefixes are a hard boundary on what is cached, and must not contain *, ? or [.

Renumbered (all unshipped): None = 0, Default = 1, then the existing modes. CacheOptions.TrackingMode is initialised to Default, so an unset mode is still the library's choice; only a zeroed enum is None.

Fallback under Default

When server-assisted tracking is unavailable - no RESP3, CLIENT disabled in the command map, or any server error answering CLIENT TRACKING - a cache left at Default switches to KeyspaceNotifications instead of failing the connection. A named mode still fails loudly, as before.

  • The switch is cache-wide, one-way, and happens at most once. The mode-dependent state (the CLIENT CACHING frames, the opt-in / by-prefix gates) is one immutable object swapped by CAS, so frames that a non-tracking server would reject stop immediately.
  • It flushes the cache, starts the keyspace subscription (not awaited inside the handshake; the flush covers the gap) and logs a warning (event 139).
  • When CONFIG GET notify-keyspace-events is permitted and shows nothing usable, a second warning says so (event 140). Diagnostic only, never a refusal.
  • ClientCache.EffectiveTrackingMode (via GetClientCache()) reports the mode in force.

The practical effect: Default over RESP2, which used to refuse to connect, now connects on keyspace notifications.

Tests

RespCacheInvalidationStrategyTests, all over RESP2 except the toy-server case:

  • None serves another client's write stale but sees its own;
  • keyspace invalidation on standalone and on the cluster (keys across slots), with and without prefixes;
  • Default over RESP2 falls back and invalidates;
  • a toy server refusing CLIENT TRACKING makes Default fall back and a named mode fail, with the log assertions for both warnings across notify-keyspace-events values;
  • unit coverage for validation, the zero value, CLIENT disabled at construction, and the one-way switch dropping the CLIENT CACHING frames.

RespInProcTrackingTests.Resp2WithACacheRefusesLoudly becomes Resp2WithANamedTrackingModeRefusesLoudly, since that is now the case that refuses.

Cache, tracking, RESP3, group and keyspace-notification suites pass on net10.0 (750 passed; the 44 skips are the existing sub-key notification tests). The new tests also pass on net481.

…tifications

Two invalidation strategies that do not use CLIENT TRACKING, and so do not
need RESP3:

- None: nothing is negotiated; entries are retired by TimeToLive and by
  local writes alone. Prefixes, if given, only bound what is cached.
- KeyspaceNotifications: the cache subscribes to __keyspace@*__:{prefix}*
  per prefix (or __keyspace@*__:* without prefixes) and invalidates the
  named key on any event. Multi-node channels, so cluster subscriptions go
  to every node, as keyspace notifications require. If the server has
  notifications disabled the subscription is silent and the cache behaves
  as None; that is documented, not checked. Prefixes are a hard boundary on
  what is cached (as under Broadcast) and must not contain glob characters.

The handshake skips CLIENT TRACKING (and its RESP3 requirement) for both.
The subscription is made after connect in both connect paths; a failure to
establish it is logged rather than failing the connect, since it is
registered first and restored with the subscription connection.

Claude-Session: https://claude.ai/code/session_01DUpQ8hTYNcGjwf1eA7R6Ss
None = 0 and Default = 1, the rest shifted up one (all unshipped).
CacheOptions.TrackingMode is initialised to Default, so an unconfigured
cache still gets the library's choice; only a zeroed enum is None.

Claude-Session: https://claude.ai/code/session_01DUpQ8hTYNcGjwf1eA7R6Ss
…ing is unavailable

Under CacheTrackingMode.Default only, a cache that cannot get CLIENT TRACKING
switches to KeyspaceNotifications rather than failing the connection: no
RESP3, CLIENT disabled in the command map (known at construction), or any
server error answering CLIENT TRACKING. A named mode still fails loudly.

The switch is cache-wide, one-way and at most once: the mode-dependent state
(CLIENT CACHING frames, opt-in/by-prefix gates) is one immutable object
swapped by CAS, so the frames a non-tracking server would reject stop at
once. It flushes, starts the keyspace subscription (now idempotent and keyed
off the mode in force), and logs a warning (event 139). When CONFIG GET is
permitted and notify-keyspace-events raises nothing usable, a second warning
says so (event 140) - a diagnostic, never a refusal.

ClientCache.EffectiveTrackingMode (via GetClientCache()) reports the mode in
force. Resp2WithACacheRefusesLoudly now names Tracking explicitly, since an
unset mode over RESP2 falls back instead.

Claude-Session: https://claude.ai/code/session_01DUpQ8hTYNcGjwf1eA7R6Ss

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant