Skip to content

CASSANDRA-21216/CASSANDRA-21260: Clear savedBuffer and savedNextKey in BTree.FastBuilder.reset()#4746

Open
andresbeckruiz wants to merge 2 commits intoapache:cassandra-4.1from
andresbeckruiz:abeckruiz/CASSANDRA-21216/cassandra-4.1
Open

CASSANDRA-21216/CASSANDRA-21260: Clear savedBuffer and savedNextKey in BTree.FastBuilder.reset()#4746
andresbeckruiz wants to merge 2 commits intoapache:cassandra-4.1from
andresbeckruiz:abeckruiz/CASSANDRA-21216/cassandra-4.1

Conversation

@andresbeckruiz
Copy link
Copy Markdown

Summary

BTree.FastBuilder.reset() does not clear savedBuffer or savedNextKey, allowing stale ColumnMetadata objects to leak when a FastBuilder is reused from the thread-local pool after an exception during message deserialization.

During a schema disagreement, a READ_REQ deserialization failure on a replica leaves a FastBuilder in a dirty state with savedBuffer and savedNextKey populated from the source table's ColumnMetadata. When the same thread reuses that FastBuilder for a subsequent BTree construction, the stale entries leak into the new BTree, causing:

  1. ClassCastException (CASSANDRA-21216): ColumnMetadata objects from the source table end up in a Row BTree, causing ClassCastException: ColumnMetadata cannot be cast to Row during mutations, reads, or flushes. This occurs on the large-message path where messages exceeding ~64KB are deserialized on SEPWorker threads that also service mutation tasks.
  2. SSTable header corruption (CASSANDRA-21260): Stale columns from the source table's savedBuffer leak into a victim table's SerializationHeader via deletion-only mutations, writing foreign column entries into the SSTable metadata on disk. This can also occur on the small-message path via Netty event loop thread reuse, lowering the trigger threshold to tables with more than 31 columns.

More context regarding these bugs can be found in this discussion thread.

Fix

Null out savedBuffer and savedNextKey in FastBuilder.reset() for both leaf and branch BTree nodes. Also add savedNextKey = null to AbstractUpdater.reset() for consistency.

Test plan

  • JVM Dtest BTreeFastBuilderContaminationTest:
    • testSchemaDisagreementCorruptsPartitionViaFastBuilder: Wide table (4200 columns) triggers large-message deserialization on SEPWorker threads, verifies no ClassCastException occurs after schema disagreement.
    • testSmallMessageContaminatesSSTableHeaderViaNettyEventLoop: Small-message scenario (150 columns) triggers deserialization on Netty event loop, verifies no foreign columns appear in victim SSTable headers.
  • Unit test BTreeTest.testFastBuilderResetClearsSavedState: Verifies FastBuilder.reset() clears savedBuffer/savedNextKey when a builder is abandoned without calling build().
  • All existing BTreeTest tests pass (12/12).

Patch by Andrés Beck-Ruiz, Runtian Liu, reviewed by <> for CASSANDRA-21216, CASSANDRA-21260

Co-authored-by: Runtian Liu runtian@uber.com

UberRuntian and others added 2 commits April 16, 2026 10:54
…-21260)

Two distributed tests that reproduce CASSANDRA-21260 purely through CQL
operations and controlled schema disagreement:

1. testSchemaDisagreementCorruptsPartitionViaFastBuilder: Reproduces
   in-memory BTree corruption where stale ColumnMetadata from a failed
   READ_REQ deserialization leaks into a Row BTree during mutation,
   causing ClassCastException. Uses wide tables (~4200 columns) so
   READ_REQ exceeds the 64KB large-message threshold and is deserialized
   on SEPWorker threads.

2. testSmallMessageContaminatesSSTableHeaderViaNettyEventLoop: Reproduces
   SSTable header contamination via small messages on the Netty event
   loop. Uses a 150-column source table and 2000-column victim table,
   both under the large-message threshold. Thread reuse is guaranteed
   by Netty's channel-to-EventLoop binding.

Both tests block schema propagation to create a disagreement window,
then trigger the FastBuilder pooling bug where reset() fails to clear
savedBuffer/savedNextKey, allowing stale state to leak across tables.
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