Post-review amendments to the versioned CAS (lib-data-store-state-redis 1.2.1) [release] - #108
Conversation
…is 1.2.1) [release] Amendments from the post-merge review of #107: - Refuse a versioned replace against a foreign frame-looking head whose digit run overruns the inspected head window (22+ digits): previously it fell through to version 0 like an unframed entry, so a version-0 witness could overwrite it. Store-written frames (at most 26 bytes) are unaffected. Covered on both providers and at store level. - Make VersionedStateStore serialize/deserialize final: an override dropping the frame would silently turn every CAS into a blind version-0 write. - Restore the plain-store coverage narrowed by #107: AbstractStateStoreTest exercises AbstractStateStore again; versioned features moved to the new VersionedStateStoreTest. - Document that no write returns the stamped version - a successful replaceIf invalidates the caller's own witness too, re-read before chaining - and scope the README retry comment to every cause of a false return. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
LGTM. All four points of #107 (comment) have been addressed. Only one gap spotted: the re-read rule landed in the Point 1 — documented rather than changed in the API. Agreed, given no caller needs Point 2 — fixed. Plain-store coverage is back: TTL expiry, custom TTL, both Point 3 — fixed. Point 4 — fixed. Swept 17–30 digits on both providers: refused at every count. Also confirmed the guard doesn't over-refuse anything legitimate: 55 tests green in the module, plus cmd-queue and pairing. |
jonmarti
left a comment
There was a problem hiding this comment.
LGTM. Approving. Only the replaceIf javadoc line from my review above is left
Amendments from the post-merge review of #107 (#107 (comment)).
Refuse a window-overrunning frame-looking head (point 4)
A foreign entry whose head starts
{"@v":with a digit run of 22+ digits leaves no terminator inside the 28-byte head peek, so the frame pattern fell through to version 0 like an unframed entry — and a version-0 witness (the legacy-adoption flow) could blindly overwrite it. Store-written frames are never affected: a TSID is a positivelong, so their head is at most 26 bytes.The fix is confined to the fallthrough: one guard in the Lua (
if not ver and string.match(head, '^{"@v":%d') then return 0 end) and the mirrored branch inLocalStateProvider.versionOfreturningnull, which the existing comparison already treats as never-match —replaceIfitself is unchanged on both providers. The new 22-digit test reproduced the blind overwrite before the fix and passes after, on both providers and at store level. TheVersionProvider/Lua docs now state the refusal guarantee instead of the window caveat.Restore plain-store test coverage (point 2)
AbstractStateStoreTestexercises the plainAbstractStateStoreagain (TTL expiry, custom TTL, bothputIfAbsentforms, request-id mapping — still published contract, consumed bylib-pairingandlib-cmd-queue-redis); the versioned features moved to the newVersionedStateStoreTest. The duplicatedshould get and put a valuefeature is deduped; the pre-#107 test of the removedreplaceIf(key, expected, value)API is not resurrected.Make
serialize/deserializefinal (point 3)An override dropping the frame would silently turn every CAS into a blind version-0 write. Non-breaking: nothing overrides them; payload customization goes through the encoding strategy.
Document the re-read rule (point 1)
No write returns the stamped version — a successful
replaceIfinvalidates the caller's own witness too. The README retry comment now names every cause of afalsereturn, and the re-read-before-chaining rule is spelled out. Per review discussion, noreplaceIfAndGetis added: no caller needs it today.Releases
lib-data-store-state-redis1.2.1.checkgreen for the module and both in-repo consumers.🤖 Generated with Claude Code