Skip to content

CASSSIDECAR-484: Fix the spurious oldest segment age in CdcRawDirectorySpaceCleaner - #371

Open
Klose6 wants to merge 4 commits into
apache:trunkfrom
Klose6:CASSSIDECAR-484
Open

CASSSIDECAR-484: Fix the spurious oldest segment age in CdcRawDirectorySpaceCleaner#371
Klose6 wants to merge 4 commits into
apache:trunkfrom
Klose6:CASSSIDECAR-484

Conversation

@Klose6

@Klose6 Klose6 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

CdcRawDirectorySpaceCleaner re-reads segment metadata via File.lastModified() after the initial listFiles() scan. If Cassandra (or any other actor) reclaims a segment in the interim, the File.lastModified() will return 0, and nowInMillis - 0 collapses to wall-clock milliseconds.

@jyothsnakonisa jyothsnakonisa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the patch @Klose6 left some minor comments

Comment on lines 214 to 215
.map(CdcRawSegmentFile::new)
.filter(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"segment might already be gone" is represented by lastModified == 0. There are two if conditions that you are adding on the lastModified == 0 condition to fix the bug. This works but what do you think about this alternative?

In the listing segment files pipeline, why don't you filter out segment files which are no longer present, so that you don't need to have if conditions later on?

Suggested change
.map(CdcRawSegmentFile::createSegmentFile)
.filter(Objects::nonNull)
.filter(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

CdcRawSegmentFile.createSegmentFile could be a static method to create segment files by reading file attributes atomically to avoid race conditions. Could be something like the following.

static CdcRawSegmentFile createSegmentFile(File logFile)
        {
            try
            {
                BasicFileAttributes attrs = Files.readAttributes(logFile.toPath(), BasicFileAttributes.class);
                return new CdcRawSegmentFile(logFile, attrs.size(), attrs.lastModifiedTime().toMillis());
            }
            catch (IOException e)
            {
                // most commonly NoSuchFileException: segment was reclaimed concurrently
                LOGGER.warn("Skipping cdc segment that disappeared before its attributes could be read path={}",
                            logFile, e);
                return null;
            }
        }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi, thanks for the review! Here the issue we found is sometime the oldestSegmentAge metrics emitted is a very large and unrealistic value(like~50 years) which indicates the segmentFiles.get(0).lastModified()) is 0, this is caused by the segment file was deleted between the listing and deletion, and add the filter in the listing should not help here because the segment can still be gone later before this deletion, so we would like to add the check to fix the spurious value in the metrics here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the reply — but I think this is already handled now that CdcRawSegmentFile reads attributes atomically (from my other comment). Nothing reads the file again after that — the age check and delete loop both use the saved value, not a fresh read. So a segment disappearing after listing can't cause lastModified == 0 anymore.

The only way lastModified is still 0 is if reading attributes fails during listing itself — which is exactly what my filter suggestion catches.

And if a segment gets deleted after listing but before we delete it, that's already safe — Files.deleteIfExists() just no-ops.

So instead of the three lastModified > 0 checks, we could filter out unreadable segments at listing time. That also avoids 0 meaning two things — "file is gone" vs. a genuinely real epoch timestamp, which right now would never get cleaned up.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One more thing worth calling out: filtering also removes the need for the directorySizeBytes -= length special-casing in the delete loop. Right now, when lastModified == 0, the loop assumes the file's already gone and discounts its size from the local budget using the stale cached length — but if that ever turns out to be a false positive (a file with a real epoch mtime that's still on disk), the loop would wrongly believe space was freed and could stop cleaning up too early.

With filtering, that branch goes away entirely. A file that fails readAttributes() just isn't in the list, so it's never counted in the first place. And a file with a genuinely real epoch mtime stays in the list and gets treated like any other old segment — normal age/delete logic applies, no special-casing needed.

@Klose6
Klose6 force-pushed the CASSSIDECAR-484 branch from 37257ab to 473a695 Compare July 27, 2026 17:32
public long lastModified()
{
return file.lastModified();
return lastModified;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

indexLength() still calls File.length() live, unlike length()/lastModified() which are now frozen at construction. Probably fine since idx files aren't reclaimed by Cassandra the same way — just flagging for consistency.

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.

3 participants