Skip to content

[eno][tombstone-recovery 7/7] Garbage collect stale inventory Secrets - #667

Open
Ruinan Liu (ruinan-liu) wants to merge 4 commits into
mainfrom
users/ruinanliu/backup-7-garbage-collection
Open

Ruinan Liu (ruinan-liu) wants to merge 4 commits into
mainfrom
users/ruinanliu/backup-7-garbage-collection

Conversation

@ruinan-liu

Copy link
Copy Markdown
Collaborator

Summary

Add a background garbage collector to the tombstone recovery operator. While the reconciler is running, it periodically deletes stale inventory Secrets from the downstream cluster's kube-system namespace.

Behavior

  • Registered with the controller manager when tombstone recovery is enabled, runs only on the leader replica, and starts a cycle every 3 to 4.5 minutes.
  • A failed or panicking cycle is logged; the next cycle starts fresh with no in-memory progress.
  • Each cycle lists inventory Secrets with the eno.azure.io/inventory-lineage label in one call, then lists Compositions and Symphonies in the reconciler's namespace with the uncached API reader. Any read failure skips the cycle.
  • Runs only when the namespace's Symphony exists and is not deleting:
    • Composition is gone: delete every Secret whose lineage matches no Composition.
    • Composition exists: delete Secrets from older synthesis UUIDs, only once the current synthesis UUID has a complete snapshot.
  • Deleting Compositions and Compositions without a current synthesis keep all their Secrets.
  • Logs each deleted Secret and a per-cycle summary of scanned, deleted, and failed Secrets. A failed delete is retried in the next cycle.

Safety

  • Symphony deletion orphans downstream resources and keeps their inventory on purpose, so nothing is deleted while the Symphony is deleting or missing.
  • Compositions are listed before the Symphony, so a Composition missing from the list was gone before any observed Symphony deletion.
  • Older snapshots stay until the current snapshot is complete, so recovery keeps its history during recreation and resynthesis.
  • Deletes use UID preconditions, and NotFound counts as deleted.
  • No finalizers, no Composition status writes, and no downstream resource deletion.

Refactor

  • Add deleteInventorySecret, shared by inventory recording rotation, deletion cleanup, and garbage collection. Deletion cleanup now also uses a UID precondition.

Assumptions and Requirements

  • Only this reconciler writes inventory Secrets to its downstream kube-system.
  • Each namespace has one Symphony, and each Composition has a Symphony owner.
  • The reconciler needs upstream list permission on symphonies.

@ruinan-liu
Ruinan Liu (ruinan-liu) force-pushed the users/ruinanliu/backup-6-inventory-deletion branch from 4ad869a to a753aed Compare October 13, 2026 20:16
@ruinan-liu
Ruinan Liu (ruinan-liu) force-pushed the users/ruinanliu/backup-7-garbage-collection branch from 24ccd3b to a09dd6c Compare October 13, 2026 20:17
@ruinan-liu
Ruinan Liu (ruinan-liu) force-pushed the users/ruinanliu/backup-6-inventory-deletion branch from a753aed to 97ac26a Compare October 16, 2026 05:16
@ruinan-liu
Ruinan Liu (ruinan-liu) force-pushed the users/ruinanliu/backup-7-garbage-collection branch from a09dd6c to 4d8e62b Compare October 21, 2026 22:29
@ruinan-liu
Ruinan Liu (ruinan-liu) changed the base branch from users/ruinanliu/backup-6-inventory-deletion to main October 21, 2026 22:29
@ruinan-liu
Ruinan Liu (ruinan-liu) marked this pull request as ready for review October 21, 2026 22:30
The inventory garbage collector treated a lineage without a Composition as
orphaned. During AKS start the active Symphony exists before its Compositions
are recreated, so the first GC cycle could delete inventory needed for
tombstone recovery. Lineages listed by the active Symphony's variations are
now kept.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8e985e7f-ffae-45b3-837c-a41cd34ec8e0
GC deleted older inventory once the current snapshot's chunks were complete,
even if a current chunk was terminating. Recording already rejects terminating
replacements. Both paths now share isCompleteSnapshot, so GC keeps the older
snapshot until a non-terminating replacement exists.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8e985e7f-ffae-45b3-837c-a41cd34ec8e0
Comment thread internal/controllers/tombstonerecovery/gc.go Outdated
Comment thread internal/controllers/tombstonerecovery/gc.go Outdated
Comment thread internal/controllers/tombstonerecovery/gc.go
@xiazhan

xiazhan commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

A comment not specific to this pr: Do each Eno controllers/reconcilers well support graceful termination?

@ruinan-liu Ruinan Liu (ruinan-liu) left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REspond to comments

@ruinan-liu

Copy link
Copy Markdown
Collaborator Author

A comment not specific to this pr: Do each Eno controllers/reconcilers well support graceful termination?

Both Eno binaries use controller-runtime's single handler, so SIGTERM cancels the manager ctx and stops all controllers and runnables including the new inventory gc. Leader election releases the lease on cancel. I will confirm that every controller drains the in-flight work within the pods' termination grace period.

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