Skip to content

chore: release v0.8.0 - #23

Merged
jaredLunde merged 1 commit into
mainfrom
jared/release-0.8.0
Oct 2, 2026
Merged

jaredLunde merged 1 commit into
mainfrom
jared/release-0.8.0

Conversation

@jaredLunde

@jaredLunde jaredLunde commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Release 0.8.0: cursor-expiry repair for bounded logs (#21), the replica-of-record cleanup (#22), and the drift cleanup (#24). Merging this publishes to crates.io via the Release workflow.

Breaking changes

  • watch_applied's reader argument is now repair (impl Into<ExpiryRepair<S>>). None and Some(reader) still compile: Some(reader) means ExpiryRepair::Relist.
  • ExpiryRepair::Relist is refused on buckets whose retention evicts current values (max_age, per-message TTL, discard: old under a limit). The watch fails instead of deleting keys that merely aged out. Use ExpiryRepair::Auto { reader, restore } with an ArtifactRestore.
  • ExpiryRepair::Restore / ::Auto require a store, and are refused at start without one.
  • Artifact manifest schema 2 records the exporting watch's key scope (ExportManifest::scope). Builds older than 0.8 refuse schema-2 manifests.
  • delete_with_version tombstones (empty-value writes) reach watchers and folds as KvUpdate::Delete, the same rule get/scan/keys apply. entry() still exposes the raw tombstone.
  • The live floor guard ends a resumed All-scope watch with KvError::CursorExpired (was WatchError) when retention overruns it. watch_applied repairs it in process.
  • Snapshot::stale_keys(current_keys, retention) takes the bucket's retention and returns Option<Vec<&str>>: None when retention says the key listing isn't the truth, the same rule as ExpiryRepair::Relist. It used to hand raw-API callers the very deletes Repair expired cursors from artifacts on buckets that evict current values #21 fixed.
  • KvWatcher::retention() and SnapshotStore::has_entries() are new provided methods. A custom SnapshotStore whose fold can be large should override has_entries to read at most one entry.
  • protocol gains the repair kernels the production code and the models now share: listing_is_truth, plan_repair, cursorless_start_needs_repair, restore_key, restore_ahead, and their types.

Fixes

  • The cursor-expired resync deleted every key NATS no longer listed, so on evicting buckets it dropped valid keys that had merely aged out, and missed gap writes that aged out. Expired cursors are now repaired from the newest artifact.
  • A transient store failure just before a repair left a batch invisible to the repair's diff, which could resurrect a deleted key. This affected the old resync too.
  • A fold with data but no cursor (a torn first checkpoint) was re-listed blind on start.
  • A restore from an artifact exported mid catch-up could transiently delete live keys or move values backward.

Rollout

  1. Upgrade importing nodes before exporting nodes (schema 2 manifests).
  2. On evicting buckets, wire ExpiryRepair::Auto { reader, restore: ArtifactRestore::new(..) }.
  3. Trigger an export round right after rollout. Artifacts without a recorded scope are refused for automatic restore.
  4. Run exports well inside max_age (or the discard: old turnover); on small hot max_age buckets, export more often.
  5. With a store, resume from the store's cursor, not one persisted from on_applied.

Dependencies

fjall / lsm-tree 3.1.4 → 3.1.10 (3.1.3–3.1.5 are yanked); chacha20 0.10.0 → 0.10.2; spin 0.9.8 → 0.9.9. The full suite passes on the new versions.

🤖 Generated with Claude Code

https://claude.ai/code/session_0152kQKDdP8XRhoeqisJYpWr

Cursor-expiry repair for bounded logs (#21) and the replica-of-record
cleanup (#22). Breaking: ExpiryRepair replaces the reader argument (the old
Option<reader> still compiles), Relist is refused on buckets that evict
current values, Restore/Auto require a store, artifact manifests move to
schema 2 (upgrade importers before exporters), delete_with_version
tombstones reach watchers as deletes, and the floor guard reports
CursorExpired.

Also moves the lockfile off yanked fjall/lsm-tree 3.1.4 to 3.1.10 (and
chacha20, spin), so the suite runs on what dependents resolve, and
refreshes the README install snippets to 0.8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152kQKDdP8XRhoeqisJYpWr
@jaredLunde
jaredLunde force-pushed the jared/release-0.8.0 branch from 353cec6 to 461afba Compare October 2, 2026 02:19
@jaredLunde
jaredLunde merged commit b627170 into main Oct 2, 2026
1 check passed
@jaredLunde
jaredLunde deleted the jared/release-0.8.0 branch October 2, 2026 02:37
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