Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/java/org/apache/cassandra/db/ReadCommandVerbHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.slf4j.LoggerFactory;

import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.filter.TombstoneOverwhelmingException;
import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator;
import org.apache.cassandra.dht.AbstractBounds;
import org.apache.cassandra.dht.Token;
Expand Down Expand Up @@ -88,7 +89,7 @@ public void doVerb(Message<ReadCommand> message)
}
catch (RejectException e)
{
if (!command.isTrackingWarnings())
if (!command.isTrackingWarnings() || e instanceof TombstoneOverwhelmingException)
throw e;

Comment on lines 90 to 94
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The new special-casing for TombstoneOverwhelmingException is covered by tests, but there is still no unit test asserting the opposite branch: when TRACK_WARNINGS is enabled and a different RejectException (e.g., LocalReadSizeTooLargeException / QueryReferencingTooManyIndexesException) is thrown, doVerb should swallow it and send an empty success response with the corresponding MessageParams populated. Adding a test for this would protect against future regressions in the warning-tracking path.

Copilot uses AI. Check for mistakes.
// make sure to log as the exception is swallowed
Expand Down
50 changes: 50 additions & 0 deletions test/unit/org/apache/cassandra/db/ReadCommandVerbHandlerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.apache.cassandra.db.filter.ColumnFilter;
import org.apache.cassandra.db.filter.DataLimits;
import org.apache.cassandra.db.filter.RowFilter;
import org.apache.cassandra.db.filter.TombstoneOverwhelmingException;
import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.locator.InetAddressAndPort;
Expand Down Expand Up @@ -146,6 +147,27 @@ public void rejectsRequestWithNonMatchingTransientness()
.build());
}

@Test(expected = TombstoneOverwhelmingException.class)
public void tombstoneExceptionPropagatesWithoutWarningTracking()
{
ReadCommand command = new TombstoneThrowingReadCommand(metadata);
handler.doVerb(Message.builder(READ_REQ, command)
.from(peer())
.withId(messageId())
.build());
}

@Test(expected = TombstoneOverwhelmingException.class)
public void tombstoneExceptionPropagatesWithWarningTracking()
{
ReadCommand command = new TombstoneThrowingReadCommand(metadata);
handler.doVerb(Message.builder(READ_REQ, command)
.from(peer())
.withFlag(MessageFlag.TRACK_WARNINGS)
.withId(messageId())
.build());
}

private static int messageId()
{
return random.nextInt();
Expand Down Expand Up @@ -199,6 +221,34 @@ public boolean isTrackingRepairedData()
}
}

private static class TombstoneThrowingReadCommand extends SinglePartitionReadCommand
{
TombstoneThrowingReadCommand(TableMetadata metadata)
{
super(metadata.epoch,
false,
0,
false,
PotentialTxnConflicts.DISALLOW,
metadata,
FBUtilities.nowInSeconds(),
ColumnFilter.all(metadata),
RowFilter.none(),
DataLimits.NONE,
KEY,
new ClusteringIndexSliceFilter(Slices.ALL, false),
null,
false,
null);
}

@Override
public UnfilteredPartitionIterator executeLocally(ReadExecutionController executionController)
{
throw new TombstoneOverwhelmingException(1000, "test_query", metadata(), KEY, Clustering.EMPTY);
}
}

private static DecoratedKey key(TableMetadata metadata, int key)
{
return metadata.partitioner.decorateKey(ByteBufferUtil.bytes(key));
Expand Down
Loading