feat(kv): add DiscardPolicy knob (default New, opt-in Old) — v0.7.2#12
Merged
Conversation
…kets NATS KV buckets are created `discard:new` (hardcoded) — reject writes when full. That's correct for config buckets (certs/configs read as source of truth), where silently dropping a live value is unacceptable. But it FREEZES writers at capacity (err 10077), which is wrong for high-churn LOG buckets (e.g. routing origins) whose consumers hold the durable fold: there the bucket is a bounded change-feed, not the source of truth, so it should bound by SIZE and evict the oldest, never reject. - StoreConfig gains `discard: DiscardPolicy` (New | Old), default New (back-compat: every `..Default::default()` caller is unchanged). - async-nats' `kv::Config` exposes no discard field, so the normal create path can only produce `discard:new`. For `discard:old`, `store_with_config` skips it and uses the raw stream API (the only place the policy is expressible). - New integration test asserts a created `Old` bucket reads back `discard:old` and the default stays `discard:new`. Bumps 0.7.1 -> 0.7.2 (additive; stays within consumers' ^0.7 requirement). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a
DiscardPolicyknob toStoreConfigso a bucket can choose what it does atmax_bytes:New(default, unchanged) — reject writes when full (discard:new). Right for config buckets (certs/configs read as the source of truth) where silently dropping a live value is unacceptable. But it freezes writers at capacity (err 10077), so pair withmax_age.Old(opt-in) — evict the oldest when full (discard:old): a hard size ceiling that never rejects. Right for high-churn log buckets whose consumers hold the durable fold (routing origins) — the bucket is a bounded change-feed, not the source of truth, so an evicted entry is recovered from the fold +CursorExpiredresync.Why
The routing-origins buckets (
edge-origins/tunnel-origins) are high-churn logs: every deploy/evacuation writes a new route key and removes the old, so distinct keys (and their delete markers) grow monotonically. With the hardcodeddiscard:newand no trim, they grow tomax_bytesand then reject all writes → traffic-routing writes freeze fleet-wide.max_ageis the wrong lever (bucket size becomes a function of churn rate). The right fix is a size-bounded, evict-oldest log — whichdiscard:oldprovides, and which is safe precisely because slipstream consumers keep the durable fold.How
StoreConfig.discard: DiscardPolicy, defaultNew— every..Default::default()caller is unchanged.kv::Confighas no discard field, so the normal create path can only producediscard:new. ForOld,store_with_configskips it and uses the raw stream API (the only place the policy is expressible).discard_policy_old_evicts_default_rejects) asserts a createdOldbucket reads backdiscard:oldand the default staysdiscard:new. All 45 integration tests pass against a real nats-server; clippy clean.Versioning
0.7.1 -> 0.7.2. Additive (new field withDefault, new enum, new export) — stays within consumers'^0.7requirement so they pick it up automatically.🤖 Generated with Claude Code