Skip to content

feat(cli): add kafscale-cli prune for deleted topic data - #271

Merged
PaxMachinaOne merged 4 commits into
KafScale:mainfrom
novatechflow:feat/prune-deleted-topics
Oct 30, 2026
Merged

PaxMachinaOne merged 4 commits into
KafScale:mainfrom
novatechflow:feat/prune-deleted-topics

Conversation

@novatechflow

@novatechflow novatechflow commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds kafscale-cli prune, which lists the S3 data kept for deleted topics and deletes it with --force. This is the prune side of the "keep data" design from #262 and #269.

Design

Kafka has no prune API, and KafScale has no extension pattern for its own admin operations over the Kafka protocol: request decoding goes through kmsg.RequestForKey and rejects unknown API keys. Prune therefore runs next to kafscale-cli restore and talks to etcd and S3 directly through the same helpers. Access is controlled by the etcd and S3 credentials. KAFSCALE_ALLOW_ADMIN_APIS and Kafka ACLs do not apply.

Behavior

  • Dry run by default. --force deletes. --topic and --older-than (for example 7d or 24h) filter the incarnations.
  • One line per deleted incarnation with topic, generation, prefix, object count, bytes and deleted_at, then totals. A dry run says nothing was deleted.
  • Prefixes are exact with a trailing slash, so orders@1/ never matches orders@10/ and orders/ never matches orders-x/. A record whose prefix does not match its topic and generation is rejected.
  • Before each delete, prune reads the current generation and the topic snapshot from etcd again, and refuses the current generation or a prefix that belongs to a live topic, including legacy names such as x@1.
  • S3 objects are deleted first. The /kafscale/deleted-topics record is removed only after that succeeds, so a rerun after a partial failure finishes the job.
  • Only partition segment and index files (<prefix><partition>/segment-<offset>.kfs and .index) are counted and deleted. Other keys, including LFS objects under <prefix>lfs/ that the proxy writes for every generation, are skipped.
  • A list or delete error on one incarnation keeps its record and does not stop the others. The run exits with "rerun to finish".
  • Prune checks the bucket with HeadBucket first and fails if it is missing, instead of creating it. Brokers and other callers are unchanged.
  • On versioned buckets prune only adds delete markers; a lifecycle rule must expire old versions.
  • Safe while brokers run, because brokers only write to the current generation's prefix.
  • Not covered: LFS objects, and topics deleted before the upgrade, which have no record.

Tests

cmd/kafscale-cli/prune_test.go uses embedded etcd and the in-memory S3 client:

  • dry run deletes nothing and prints the summary
  • force deletes only the deleted prefixes and keeps orders@2, orders@10 and orders-x
  • the current generation and a live legacy topic are refused
  • mismatched and sibling prefixes in records are rejected
  • --topic and --older-than filters, alone and together
  • a partial S3 failure keeps the record, and a rerun finishes
  • LFS objects and unknown keys under a generation 0 prefix are not counted and are kept while a later generation is live
  • a list failure on one incarnation continues with the others, and a rerun finishes
  • a failed bucket check stops prune before it lists anything, and CheckBucket never creates the bucket
  • the CLI wrapper through run, including flag and connection errors

Docs

docs/operations.md (new "Pruning Deleted Topic Data" section), docs/ops-api.md and docs/releases/unreleased.md.

Refs #262

Lists the S3 data kept for deleted topic incarnations and deletes it with --force. Filters by --topic and --older-than. Each prefix is rechecked against the live generation and topic snapshot before it is deleted, and the deleted-topics record is removed only after the S3 delete succeeds, so a rerun finishes a partial run.

Refs KafScale#262
Prune deleted every key under a generation 0 prefix, including LFS objects that the proxy writes there for the live topic. It now counts and deletes only <partition>/segment-<offset>.kfs and .index keys. A list error on one incarnation no longer stops the run, and prune fails when the bucket does not exist instead of creating it.

Refs KafScale#262
@PaxMachinaOne
PaxMachinaOne merged commit 3a5d6dd into KafScale:main Oct 30, 2026
12 checks passed
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.

2 participants