Skip to content

fix(tsi1): series delete deadlocks against in-flight index compaction - #27696

Open
goingforstudying-ctrl wants to merge 1 commit into
influxdata:main-2.xfrom
goingforstudying-ctrl:fix/tsi1-delete-compaction-deadlock
Open

goingforstudying-ctrl wants to merge 1 commit into
influxdata:main-2.xfrom
goingforstudying-ctrl:fix/tsi1-delete-compaction-deadlock

Conversation

@goingforstudying-ctrl

Copy link
Copy Markdown

Required checklist

  • Sample config files updated (both /etc folder and NewDemoConfig methods) (influxdb and plutonium)
  • openapi swagger.yml updated (if modified API) - link openapi PR
  • Signed CLA (if not already signed)

Description

A DELETE that lands while a tsi1 index compaction is in flight can wedge the shard for good. The delete path builds its series iterator first, which retains the current file set (including the log file mid-compaction), then calls Partition.Wait() for in-progress compactions to drain. The compaction, meanwhile, finishes writing the new index file and blocks in LogFile.Close() waiting for exactly that reference to drop. Delete waits on compaction, compaction waits on delete. With log compactions firing every so often this is basically guaranteed to happen eventually on a busy instance.

Context

Two changes, both in tsdb/index/tsi1/partition.go:

  1. Once the manifest swap commits a compaction, the swapped-out files are closed and removed off the compaction goroutine (tracked by a new closeWg that Partition.Close() drains). At that point the old files are no longer reachable from the file set, so no new references can appear, and waiting for the existing readers to finish is pure cleanup. This breaks the circular wait for both compactLogFile and compactToLevel, regardless of who holds references or why.

  2. DisableCompactions() had a dead branch: it increments compactionsDisabled and then checks == 0, so the compactionInterrupt channel was never closed and in-flight compactions were never actually asked to abort. Fixed to fire on the first disabler (== 1), matching how the tsm engine aborts compactions for deletes. This keeps Wait() from sitting behind a whole multi-minute CompactTo when a delete shows up mid-compaction.

Risks: compaction completion no longer implies the old files are already unlinked, only that they're out of the manifest. Startup already cleans up non-manifest files via deleteNonManifestFiles, so an interrupted process just leaves a stray file for the next open rather than corrupting anything. Partition.Close() waits for pending closes, so test and shutdown behavior is unchanged.

Affected areas (delete section if not relevant):

Internal only: tsi1 index compaction lifecycle. No user-visible API, CLI, or config changes.

Severity (delete section if not relevant)

Recommend upgrade for anyone running periodic DELETE/DROP workloads on 2.x — the deadlock freezes the affected shard (and can stall the whole engine) until restart.

Note for reviewers:

Reproduced the deadlock shape with a regression test (TestPartition_CompactLogFile_HeldReferenceDoesNotBlockWait): hold a file-set reference like a delete's iterator does, compact, and assert Wait() returns anyway, then assert the old log file is removed once the reference drops. On current code that test hangs until the watchdog fires. Also added TestPartition_DisableCompactions_InterruptsInflight for the interrupt channel semantics. go test -race ./tsdb/index/tsi1/ passes.

A delete retains the index file set through its series iterator and then
blocks in Partition.Wait() for compactions to drain, while the finishing
compaction blocks in LogFile.Close()/IndexFile.Close() waiting for the
iterator's reference. Neither can proceed, wedging the shard.

After the manifest swap commits a compaction, swapped-out files are no
longer reachable from the file set, so closing and removing them off the
compaction goroutine is safe and breaks the circular wait.
Partition.Close() drains the deferred closes.

Also fix DisableCompactions never firing the compaction interrupt: the
check ran after the increment and compared against 0, so in-flight
compactions were never asked to abort when a delete arrived.
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