ArrayIndexOutOfBoundsException with repaired data tracking and counters

Patch by marcuse; reviewed by Sam Tunnicliffe for CASSANDRA-20871
This commit is contained in:
Marcus Eriksson 2025-08-29 09:11:41 +02:00
parent 8d2c11ed2e
commit 76a7e43613
3 changed files with 55 additions and 0 deletions

View File

@ -1,4 +1,5 @@
4.0.20
* ArrayIndexOutOfBoundsException with repaired data tracking and counters (CASSANDRA-20871)
* Fix cleanup of old incremental repair sessions in case of owned token range changes or a table deleting (CASSANDRA-20877)
* Fix memory leak in BufferPoolAllocator when a capacity needs to be extended (CASSANDRA-20753)
* Leveled Compaction doesn't validate maxBytesForLevel when the table is altered/created (CASSANDRA-20570)

View File

@ -69,6 +69,11 @@ public class Digest
// for the purposes of repaired data tracking on the read path, exclude
// contexts with legacy shards as these may be irrevocably different on
// different replicas
// see super.updateWithCounterContext + CountersTest.testEmptyContext - counter context can be empty
if (accessor.isEmpty(context))
return this;
if (CounterContext.instance().hasLegacyShards(context, accessor))
return this;

View File

@ -18,11 +18,17 @@
package org.apache.cassandra.distributed.test;
import java.io.IOException;
import java.util.Iterator;
import org.junit.Test;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.distributed.Cluster;
import org.apache.cassandra.distributed.api.ConsistencyLevel;
import org.apache.cassandra.distributed.api.ICoordinator;
import org.apache.cassandra.io.sstable.Descriptor;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import static org.apache.cassandra.distributed.api.Feature.GOSSIP;
import static org.apache.cassandra.distributed.api.Feature.NATIVE_PROTOCOL;
@ -75,4 +81,47 @@ public class CountersTest extends TestBaseImpl
}
}
}
@Test
public void testEmptyContext() throws IOException
{
try (Cluster cluster = init(Cluster.build(3)
.withConfig(c -> c.with(GOSSIP, NATIVE_PROTOCOL)
.set("repaired_data_tracking_for_partition_reads_enabled", true)
.set("repaired_data_tracking_for_range_reads_enabled", true))
.start()))
{
cluster.schemaChange(withKeyspace("CREATE TABLE %s.t (a ascii, b ascii, c counter, d counter, PRIMARY KEY(a, b))"));
cluster.get(1).executeInternal(withKeyspace("UPDATE %s.t SET c = c + 1, d = d + 1 WHERE a = 'a1' AND b = 'b1'"));
cluster.get(2).executeInternal(withKeyspace("UPDATE %s.t SET c = c + 2, d = d + 2 WHERE a = 'a1' AND b = 'b1'"));
cluster.get(3).executeInternal(withKeyspace("UPDATE %s.t SET c = c + 3, d = d + 3 WHERE a = 'a1' AND b = 'b1'"));
cluster.forEach(i -> i.flush(KEYSPACE));
cluster.forEach(i -> i.runOnInstance(() -> {
Iterator<SSTableReader> sstables = Keyspace.open(KEYSPACE)
.getColumnFamilyStore("t")
.getLiveSSTables()
.iterator();
while (sstables.hasNext())
{
SSTableReader sstable = sstables.next();
Descriptor descriptor = sstable.descriptor;
try
{
descriptor.getMetadataSerializer()
.mutateRepairMetadata(descriptor, System.currentTimeMillis(), null, false);
sstable.reloadSSTableMetadata();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}));
cluster.coordinator(1).execute(withKeyspace("select a,d from %s.t where a = 'a1'"), ConsistencyLevel.ALL);
cluster.coordinator(2).execute(withKeyspace("select a,d from %s.t where a = 'a1'"), ConsistencyLevel.QUORUM);
cluster.coordinator(3).execute(withKeyspace("select a,d from %s.t where a = 'a1'"), ConsistencyLevel.QUORUM);
}
}
}