Skip to content

CEP-45: Prevent dropping journal segments referenced by partially rec…#4904

Open
frankgh wants to merge 1 commit into
apache:cep-45-mutation-trackingfrom
frankgh:CASSANDRA-21406
Open

CEP-45: Prevent dropping journal segments referenced by partially rec…#4904
frankgh wants to merge 1 commit into
apache:cep-45-mutation-trackingfrom
frankgh:CASSANDRA-21406

Conversation

@frankgh

@frankgh frankgh commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

…onciled sstables

Prevent dropping of any segment we might still need in the future to rebuild a partially reconciled sstable, even if every mutation in that segment has been fully reconciled.

Segments are droppable once:

  1. segment doesn't need replay
  2. and no partially reconciled sstables reference any mutations in the segment

patch by Francisco Guerrero; reviewed by TBD for CASSANDRA-21406

…onciled sstables

Prevent dropping of any segment we might still need in
the future to rebuild a partially reconciled sstable,
even if every mutation in that segment has been fully
reconciled.

Segments are droppable once:
1. segment doesn't need replay
2. and no partially reconciled sstables reference any
   mutations in the segment

patch by Francisco Guerrero; reviewed by TBD for CASSANDRA-21406
@iamaleksey iamaleksey self-requested a review July 6, 2026 10:59

@iamaleksey iamaleksey left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Most changes look alright to me. I've left a few small comments in-line. The rest follow below:

  1. Both migrations to and from tracked will need to be handled. And it's annoyingly nuanced. But I think the naive ref-counting approach won't work. I expect that instead of keeping a single refcount per segment we'll want to keep a set of referrer-SSTables per segment, so that we can have sufficient information to be able to conditionally drop referrer-SSTables from these sets.
  2. In that vein, we may want to subscribe to SSTable notifications unconditionally on keyspace being tracked. Then base the increment/decrement decision based on an SSTable's coordinatorLogOffsets being empty or not.
  3. LogStatePersister#run() is no longer the logical place to trigger journal truncation. The logical places to trigger it would be: (1) SSTable being flipped from unrepaired to repaired -> refcount reaching 0 and (2) a segment's needsReplay() flipping from false to true.
  4. On that note, both of these conditions (!segment.metadata().needsReplay() && !segmentReferenceTracker.isReferenced(segment.id()) can prevent a segment from becoming droppable for unbounded lengths of time under normal operation. Depending on compaction strategy and on coldness of the coldest table with a reference to a segment, its memtable can take a long time to flush (pre-existing issue) and its SSTables can take a long time to get compacted - and compaction is now where we flip unrepaired to repaired. This latter one is a new issue to this PR, and the solution to it would be to have some process to trigger out of band promotion to repaired (and possibly flush low-traffic long-lived memtables). Maybe not quite a periodic one, but one triggered by some total journal size threshold?
  5. The logic, as implemented, won't quite work for witnesses (including witness-only nodes).
  6. Lastly, it would be useful to add a column to the mutation journal vtable that lists what sstables are holding segments from being released (or, if we keep refcount as a simple int forever or for now, the SSTable refcount at least).

lock.lock();
try
{
return refsBySegment.get(segmentId) > NO_REF;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: in count comparisons cleaner and more obvious to just use the literal 0.

private void decrementRef(long segmentId)
{
refsBySegment.compute(segmentId, (k, prev) -> {
Invariants.require(prev > NO_REF, "Refcount underflow for segment %d", segmentId);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit (duplicate): in count comparisons cleaner and more obvious to just use the literal 0.

{
CommitLogPosition start = startIt.next();
CommitLogPosition end = endIt.next();
for (long s = start.segmentId; s <= end.segmentId; s++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this logic double-counts (or, potentially, n-times-counts) the same segment if multiple non-overlapping intervals are contained in the same segment (but different ranges of position).

Currently it's symmetrical, affecting both increments and decrements in equal measure, so not causing a problem - I think, but I would at least document this, if not fix.

@iamaleksey

Copy link
Copy Markdown
Member

And, as more of a note to self rather than a reviewer request, we should eventually add some mutation journal metrics for how long it takes for segments to become truncate-able.

private void decrementRef(long segmentId)
{
refsBySegment.compute(segmentId, (k, prev) -> {
Invariants.require(prev > NO_REF, "Refcount underflow for segment %d", segmentId);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

And one other small thing: if you move out the invariant out of compute() call you won't need a capturing lambda here.

@iamaleksey

Copy link
Copy Markdown
Member

One more thing - the tracker probably shouldn't be engaged for SSTables streamed from other hosts (where originatingHostId is not local host id). I don't think it outright breaks anything in this PR, but it certainly goes against the purpose of segment ref tracking (which is purely local).

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