Skip to content

Cleanup drains a compaction group's staging ticket mid-rewrite: one wasted group and a false 'absent' per sweep #174

Description

@jghoman

Every cleanup sweep that lands while a compaction group is mid-rewrite drains that group's compaction_staging ticket as absent and the group aborts at commit as SkippedConflict. The rewrite's IO is wasted and the cleanup ledger records an absence that is not one.

Evidence (dev, 2026-09-23, maintenance runs API)

Cleanup runs every 30 min (:04, :34). Three of the four sweeps in a two-hour window coincided with an in-flight group; each produced exactly one missing and one skipped group, in the same catalog, seconds apart:

catalog compaction run (skip) cleanup run cleanup result
stream-events-v2 05:03:39 → 05:04:31, skipped_conflicts: 1 05:04:10 missing: 1
stream-events 05:33:44 → 05:35:03, skipped_conflicts: 1 05:34:10 missing: 1
millpond-dev 06:34:08 → 06:34:39, skipped_conflicts: 1 06:34:13 missing: 1

No other skipped groups and no other absences occurred in the window (400 runs). The 06:04 sweep found no group in flight.

Mechanism

stageOutputPath inserts the undrained hog_file_removal row for the output path before the rewrite starts (a claim ticket, so an abandoned upload is reclaimed). The drain has no notion of a ticket's age: it HEADs the path, finds nothing because the multipart upload has not completed, and settles the row absent. At commit, CompactionService.kt ~:1296-1320 detects the drained ticket, re-stages the path, aborts the group, and returns SkippedConflict. The code comment there describes the window ("the drain window is the entire rewrite") and chooses safety over the wasted group, which is right, but the cost is one lost group per cleanup sweep on any catalog that is compacting continuously. On production that is two groups an hour at the current cadence, and every one shows up as an absent in the ledger, which is the signal an operator would otherwise read as an object that vanished.

Fix

Give the ticket a lease the drain respects. Cleanup skips compaction_staging rows whose scheduled_at is younger than the compaction group budget (or a dedicated lease_until column, the shape hog_upload.expires_at already has for Trino claims from #162). An abandoned rewrite is still reclaimed after the lease; a live one is never touched. MaintenanceSummarySampler and the compaction plan are unaffected.

Also worth splitting in the cleanup outcome: absent because the object never existed (a ticket) versus absent because a registered file is gone (a real problem). Today both are missing.

Related: #146 (streaming uploads made the object invisible until Complete), the removal ledger comment in schema.sql on compaction_staging.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions