Skip to content

QUEUE-130: Release parked store after StoreAppender construction#1714

Open
james-mcsherry wants to merge 5 commits into
developfrom
issue/QUEUE-130-release-parked-appender-store
Open

QUEUE-130: Release parked store after StoreAppender construction#1714
james-mcsherry wants to merge 5 commits into
developfrom
issue/QUEUE-130-release-parked-appender-store

Conversation

@james-mcsherry

@james-mcsherry james-mcsherry commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

QUEUE-130: Release parked store after StoreAppender construction

Issue

When a StoreAppender is constructed, it runs an EOF back-scan that walks cycles from the last existing cycle down to the first, and parks the appender on a store (the newest EOF-ed cycle, or the first cycle). This parked store — its mapped-file reservation, wires, and open file descriptor — was held open even though the appender had not written anything yet.

An appender created against a queue that already has older roll cycles therefore pinned an old .cq4 file open. Because FileUtil.removableRollFileCandidates(...) scans oldest-first and stops at the first still-open file, a single pinned old file blocked removal of that file and every file after it. A parked appender that never wrote could thus prevent old roll files from being cleaned up.

Fix

StoreAppender now calls a new releaseParkedStore() at the end of the back-scan. This drops the store, its wires, and the FD, returning the appender to the same "never written" state as one created against an empty queue. The next write re-acquires the current cycle as normal. Nothing on disk is changed — only the in-memory reservation and open descriptor are released.

A scannedCycle local was added so the "took Nms to find first open cycle" perf log still reports the cycle the scan landed on, since cycle is now reset by the release.

New test

AppenderReleasesParkedStoreTest (extends IndexingTestCommon) proves the behaviour at the symptom level via FileUtil.removableRollFileCandidates(...):

  • writerAloneLeavesEveryEarlierCycleRemovable — control: with only the writer active, all earlier cycles are removable.
  • parkedAppenderDoesNotPinAnOldCycle — a fresh appender parks at construction and never writes; after the writer rolls past it, every earlier cycle must still be removable.

removableRollFileCandidates inspects open descriptors via /proc/self/fd, so both tests are gated on getAllOpenFilesIsSupportedOnOS() and skip on platforms where that is unsupported (macOS/Windows). Commenting out the releaseParkedStore() call turns parkedAppenderDoesNotPinAnOldCycle red while the control stays green.

Why existing tests needed changing

CreateAtIndexTest and NotCompleteTest compare queue dumps before/after an append. With the fix, the appender's first write now performs EOF normalisation, which writes a benign normalisedEOFsTo metadata record and shifts byte/modCount counters. These are expected, harmless deltas, not behavioural changes, so the dump-comparison helpers were extended to normalise them out:

  • Strip the normalisedEOFsTo metadata record.
  • Mask modCount values.
  • Mask # N bytes remaining counts.

IndexingTestCommon.timeProvider was made protected so the new test in the same package hierarchy can advance time to force rolls.

@james-mcsherry james-mcsherry changed the title Issue/queue 130 release parked appender store QUEUE-130: Release parked store after StoreAppender construction Jul 6, 2026
@james-mcsherry james-mcsherry marked this pull request as ready for review July 6, 2026 13:30
@sonarqubecloud

Copy link
Copy Markdown

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