KV per-key TTL (parity with nats.go #1864)#1000
Conversation
Bring nats.c to parity with nats.go #1864 ("KeyValue per key TTL support and
limit markers") by adding a KV-level per-key TTL write surface on top of the
existing per-message TTL primitives (nats-io#863):
- kvConfig.LimitMarkerTTL: at bucket create, enable AllowMsgTTL +
SubjectDeleteMarkerTTL on the backing stream.
- kvStore_CreateWithTTL / kvStore_CreateStringWithTTL: per-key TTL on create,
reusing the marker-aware re-create retry (a re-create over a DEL/PURGE/MaxAge
tombstone succeeds and keeps the TTL).
- kvStore_UpdateWithTTL / kvStore_UpdateStringWithTTL: per-key TTL on a CAS update.
- PurgeTTL: kvPurgeOptions.TTL threads a per-message TTL onto the purge marker
so the purge tombstone auto-expires.
- kvStore_Create / kvStore_Update delegate to the *WithTTL entry points with
ttl=0, so existing behavior is unchanged.
- test_KeyValueCreateWithTTL (test/test.c + test/list_test.txt): create-with-TTL,
get-before-expiry, create-over-live-key-fails, server-side expiry, re-create
over the expired marker, update-with-TTL (binary + string), and Purge+PurgeTTL.
JS_SETUP(2,11,0) skips it on < 2.11; verified green on nats-server 2.11.10,
2.12.11 & 2.14.2.
AdamPayzant
left a comment
There was a problem hiding this comment.
Hi Norm. Thank you for the contribution! I've added some notes
|
@AdamPayzant Thanks for the quick review. Will take care of the first three comments. For the last one, will make it public. Added kvStore_UpdateStringWithTTL to mirror the CreateString* pair A couple of things I ran into while expanding the tests:
|
3c33759 to
baf603c
Compare
…eature/kv-per-key-ttl
|
@AdamPayzant Any thoughts on the two items I mentioned above? TTL granularity and Propagating errors to the caller? |
|
I thought it was fine without, personally, but it would be a pretty quick PR if you want to add them |
Follow-ups to the KV per-key TTL work (PR nats-io#1000): - Validate the per-key TTL client-side at the kvStore_*WithTTL chokepoint: reject 0 < ttl < 1000ms with NATS_INVALID_ARG (kvErrInvalidTTL) before any publish, mirroring the server's 1s minimum (JSMessageTTLInvalidErr). This covers create, update, their String variants, and the purge marker TTL (kvPurgeOptions.TTL). - Preserve the create-path error through the marker-aware retry: short-circuit a client-side NATS_INVALID_ARG, and snapshot/restore the original failure around the _getEntry tombstone re-check (which overwrites the thread-local last error). A rejected create no longer returns an empty error string -- the server message (e.g. "invalid per-message TTL", "per-message TTL is disabled") now reaches the caller. - Tests: sub-second create/update with preserved messages, boundary cases (999ms/1ms reject, 1000ms/1500ms accept), sub-second PurgeTTL, and a TTL-disabled-bucket create that exercises the snapshot/restore branch. - Docs: public-API docs note the client-side 1s validation and that the server stores TTL with whole-second granularity (fractional values are truncated, so 1500ms is applied as 1s).
Follow-ups to the KV per-key TTL work (PR nats-io#1000): - Validate the per-key TTL client-side at the kvStore_*WithTTL chokepoint: reject 0 < ttl < 1000ms with NATS_INVALID_ARG (kvErrInvalidTTL) before any publish, mirroring the server's 1s minimum (JSMessageTTLInvalidErr). This covers create, update, their String variants, and the purge marker TTL (kvPurgeOptions.TTL). - Preserve the create-path error through the marker-aware retry: short-circuit a client-side NATS_INVALID_ARG, and snapshot/restore the original failure around the _getEntry tombstone re-check (which overwrites the thread-local last error). A rejected create no longer returns an empty error string -- the server message (e.g. "invalid per-message TTL", "per-message TTL is disabled") now reaches the caller. - Tests: sub-second create/update with preserved messages, boundary cases (999ms/1ms reject, 1000ms/1500ms accept), sub-second PurgeTTL, and a TTL-disabled-bucket create that exercises the snapshot/restore branch. - Docs: public-API docs note the client-side 1s validation and that the server stores TTL with whole-second granularity (fractional values are truncated, so 1500ms is applied as 1s).
…re retry Follow-up to the KV per-key TTL work (PR nats-io#1000). Per review, TTL granularity is left entirely to the server (no client-side check), so a future server-side change to the minimum needs no client update. - kvStore_CreateWithTTL: the marker-aware retry calls _getEntry, which overwrites the thread-local last error, so a failed create returned an empty error string. This is pre-existing behavior inherited from kvStore_Create. Suppress error-stack updates across the _getEntry probe (nats_doNotUpdateErrStack) and restore the original failure on return; re-enable and clear only when we actually re-create over a tombstone, so that path reports its own error. The server message (e.g. "invalid per-message TTL", "per-message TTL is disabled") now reaches the caller. - Remove the client-side TTL granularity check (kvStore_*WithTTL and the purge marker TTL) and kvErrInvalidTTL; the server enforces the 1s minimum. - Tests: sub-second create/update now assert the server's error survives the retry, plus a TTL-disabled-bucket create that exercises the same path. - Docs: note the server's 1s minimum without asserting a client-side rejection.
Bring nats.c to parity with nats.go #1864 ("KeyValue per key TTL support and limit markers") by adding a KV-level per-key TTL write surface on top of the existing per-message TTL primitives (#863):