Repository navigation
Set-member expiration (Redis 8.12): SADDEX / SEXPIRE / SPTTL / SPERSIST on IDatabase - #3340
Conversation
|
Reworked per review: the add-with-expiry surface is now two Two deliberate calls, both documented in the XML remarks:
The |
The hash-field-TTL idea (7.4's HEXPIRE) brought to sets (redis/redis#15938, 8.12-rc2), mirroring the HashField* family: - SetAddAndSetExpiry (SADDEX): add members and/or (re)set their expiration in one atomic call; When.NotExists/Exists map to MNX/MXX, which are all-or-nothing across the batch, exactly as HSETEX's FNX/FXX; the bare form discards an existing TTL, KEEPTTL keeps it - SetMemberExpire (SEXPIRE/SPEXPIRE/SEXPIREAT/SPEXPIREAT by precision) -> ExpireResult[], per member - SetMemberGetTimeToLive (SPTTL) and SetMemberGetExpireDateTime (SPEXPIRETIME) -> long[] - SetMemberPersist (SPERSIST) -> PersistResult[] - KeyNotificationType.SExpired ('sexpired', side-effect block), so the 8.12 notification - including on the subkey channels, whose payload names the lapsed member - parses as a known type; GetSubKeys() already handles the payload Gated [Experimental] behind SER011/Experiments.Server_8_12 and RedisFeatures.v8_12_0 (8.11.241), both shared with #3261/#3315; the test docker image moves to client-libs-test:8.12-rc2 as in those PRs. Round-trip tests pin the exact wire bytes; the live tests (HashFieldTests mirror) pass against the 8.12-rc2 topology on both protocols. Plan document in notes/set-member-expiry/.
…ity SetAddAndSetExpiry surface Review feedback (see the PR): Expiration already encodes EX/PX/EXAT/PXAT/KEEPTTL - exactly SADDEX's expiration block - so the TimeSpan?/DateTime/keepTtl splits collapse and the surface is SetAdd(key, value, Expiration, When, flags) and SetAdd(key, values, Expiration, When, flags), both bool (+async): 8 members become 4, with no new method name. A required Expiration keeps overload resolution away from the existing SetAdd members. - Expiration.Persist is rejected (SADDEX has no PERSIST; SetMemberPersist is the way to remove member expirations) - previously it would have been sent as a bogus token. - The overloads always send SADDEX: plain SADD is not an equivalent fallback for the no-expiry form, because bare SADDEX discards an existing member's TTL where SADD leaves existing members untouched; the XML docs call this out, and that the multi-value form reports only whether anything changed, not a count. - The SetMemberExpire/GetTimeToLive/GetExpireDateTime/Persist family is unchanged (ExpireWhen's GT/LT cannot ride Expiration, and it mirrors the HashField shape). - plan.md carries a revision note; SER011.md updated; all 50 SetMemberExpiry tests pass live against 8.12-rc2, 525 KeyPrefixed tests pass.
…d-anything reply and the MNX/NX conditions make residue from a previous run (or the other protocol) look like a failure
428f12e to
d10a538
Compare
Initial
IDatabasesupport for Redis 8.12's set-member expiration (redis/redis#15938, in 8.12-rc2) — the hash-field-TTL idea brought to sets — mirroring theHashField*family throughout. Plan document (server facts, requirement coverage, API grounding, open questions) innotes/set-member-expiry/plan.md.Surface
SetAddAndSetExpiry(single/array × TimeSpan?/DateTime, mirror ofHashFieldSetAndSetExpiry)SADDEXbool— at least one member added/updated/deletedSetMemberExpire(TimeSpan/DateTime,ExpireWhen)SEXPIRE/SPEXPIRE/SEXPIREAT/SPEXPIREATby precisionExpireResult[]per memberSetMemberGetTimeToLiveSPTTLlong[](ms; −1 none, −2 missing)SetMemberGetExpireDateTimeSPEXPIRETIMElong[](Unix ms)SetMemberPersistSPERSISTPersistResult[]Notable mappings, each with the sibling that grounds it:
When.NotExists/When.Exists→MNX/MXX: all-or-nothing across the batch (one offender vetoes the whole call), which is exactlyHSETEX'sFNX/FXXsemantics, so the same enum carries it; the XML docs spell out the batch veto.SADDEXdiscards an existing member TTL;KEEPTTLkeeps it — same contract asSET/HSETEX, soExpiration.CreateOrKeepTtlis reused unchanged.ExpireResult(2/1/0/−2) andPersistResult(1/−1/−2) match the per-member reply codes exactly, so no new enums or processors;SEXPIRE ... GTloses to a member with no expiry (no expiry = infinite), covered in docs and tests.STTL/SEXPIRETIME) are not exposed, as the hash family does withHTTL(CommandCoverage.mdre-markednot-applicable).Keyspace notifications
8.12 fires
sexpiredwhen a member lapses, including on the subkey channels where the payload names the member (__subkeyevent@0__:sexpired,14:presence:lobby|5:alice). No new parsing was needed:KeyNotification.GetSubKeys()onmainalready handles every subkey channel kind including the length-prefixed member list. The only gap was the enum entry —KeyNotificationType.SExpired = 1006(sexpiredis a side-effect event likeexpired, so it sits in the side-effect block, which also keeps it clear of #3261's 66/67). Unit tests cover the parse row and aGetSubKeyscase on thesexpiredchannel.Shared 8.12 plumbing (overlaps #3261 / #3315)
Experiments.Server_8_12 = "SER011",docs/exp/SER011.md, theNoWarnentry,RedisFeatures.v8_12_0 = 8.11.241and theclient-libs-test:8.12-rc2Dockerfile/compose bump are textually identical to those PRs, so whichever lands first wins and the rest merge clean (the SER011.md feature bullets are the only real merge, and it's additive). All new public members are[Experimental(Experiments.Server_8_12)].Tests
RoundTripUnitTests/SetMemberExpiryRoundTrip— 16 exact-bytes tests (every token shape: EX/PX/EXAT/PXAT/KEEPTTL/bare, MNX/MXX, NX-style conditions, precision picking, theMEMBERS nblock), all passing.SetMemberExpiryTests— theHashFieldTestsmirror plus the set specifics (MNX batch veto, bare-form TTL discard, GT vs pinned, past-expiry delete, pinned-but-removable,WithKeyPrefixisolation): 34/34 passing (17 × RESP2/RESP3) against the rebuilt 8.12-rc2 topology (redis_version:8.11.241); they skip with the version message on older servers.ExpireResultArray/PersistResultArray/Int64Array/Boolean— no new processors.MULTIshape (SADD+KeyExpireexpires the key); reasoning recorded in the plan.dotnet build Build.csproj -c Release /p:CI=trueis clean apart from the pre-existing NU1903 ILCompiler advisory intoys/AotRig(fails identically onmain).