Merge branch 'cassandra-4.0' into cassandra-4.1

This commit is contained in:
Stefan Miklosovic 2025-08-27 19:00:25 +02:00
commit 792e823af0
No known key found for this signature in database
GPG Key ID: 32F35CB2F546D93E
11 changed files with 83 additions and 15 deletions

View File

@ -5,6 +5,7 @@
* IntrusiveStack.accumulate is not accumulating correctly (CASSANDRA-20670)
* Add nodetool get/setguardrailsconfig commands (CASSANDRA-19552)
Merged from 4.0:
* Fix IndexOutOfBoundsException in sstablemetadata tool when a range tombstone is a max clustering value (CASSANDRA-20855)
* Update Jackson to 2.19.2 (CASSANDRA-20848)
* Update commons-lang3 to 3.18.0 (CASSANDRA-20849)
* Add NativeTransportMaxConcurrentConnectionsPerIp to StorageProxyMBean (CASSANDRA-20642)

View File

@ -356,21 +356,9 @@ public class SSTableMetadataViewer
if (validation != null && header != null)
printMinMaxToken(descriptor, FBUtilities.newPartitioner(descriptor), header.getKeyType());
if (header != null && header.getClusteringTypes().size() == stats.minClusteringValues.size())
{
List<AbstractType<?>> clusteringTypes = header.getClusteringTypes();
List<ByteBuffer> minClusteringValues = stats.minClusteringValues;
List<ByteBuffer> maxClusteringValues = stats.maxClusteringValues;
String[] minValues = new String[clusteringTypes.size()];
String[] maxValues = new String[clusteringTypes.size()];
for (int i = 0; i < clusteringTypes.size(); i++)
{
minValues[i] = clusteringTypes.get(i).getString(minClusteringValues.get(i));
maxValues[i] = clusteringTypes.get(i).getString(maxClusteringValues.get(i));
}
field("minClusteringValues", Arrays.toString(minValues));
field("maxClusteringValues", Arrays.toString(maxValues));
}
printClusteringValues(header, "minClusteringValues", stats.minClusteringValues);
printClusteringValues(header, "maxClusteringValues", stats.maxClusteringValues);
field("Estimated droppable tombstones",
stats.getEstimatedDroppableTombstoneRatio((int) (currentTimeMillis() / 1000) - this.gc));
field("SSTable Level", stats.sstableLevel);
@ -437,6 +425,21 @@ public class SSTableMetadataViewer
}
}
private void printClusteringValues(SerializationHeader.Component header, String name, List<ByteBuffer> clusteringValues)
{
if (header != null && header.getClusteringTypes().size() >= clusteringValues.size())
{
List<AbstractType<?>> clusteringTypes = header.getClusteringTypes();
int size = Math.min(clusteringTypes.size(), clusteringValues.size());
String[] values = new String[size];
for (int i = 0; i < size; i++)
{
values[i] = clusteringTypes.get(i).getString(clusteringValues.get(i));
}
field(name, Arrays.toString(values));
}
}
private void field(String field, Object value)
{
field(field, value, null);

View File

@ -0,0 +1 @@
3617993137

View File

@ -0,0 +1,8 @@
Data.db
TOC.txt
Digest.crc32
Summary.db
Filter.db
Statistics.db
CompressionInfo.db
Index.db

View File

@ -18,16 +18,23 @@
package org.apache.cassandra.tools;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import com.google.common.base.CharMatcher;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.tools.ToolRunner.ToolResult;
import org.assertj.core.api.Assertions;
import org.hamcrest.CoreMatchers;
@ -225,4 +232,52 @@ public class SSTableMetadataViewerTest extends OfflineToolUtils
assertKeyspaceNotLoaded();
assertServerNotLoaded();
}
@Test
public void testLoadingOfSStableWithRangeTombstoneAsMaxClustering() throws IOException
{
String sstableWithTombstones = copySSTables("sstable_metadata", "CASSANDRA_20855");
ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class, sstableWithTombstones);
assertTrue(tool.getStdout(), CharMatcher.ascii().matchesAllOf(tool.getStdout()));
Assertions.assertThat(tool.getStdout()).contains(sstableWithTombstones.replaceAll("-Data\\.db$", ""));
Assertions.assertThat(tool.getStdout()).contains("minClusteringValues: [clust_key_1_1, clust_key_2_1]");
// Range tombstone end as a max clustering value, only the first clustering key is stored in metadata
Assertions.assertThat(tool.getStdout()).contains("maxClusteringValues: [clust_key_1_2]");
assertTrue(tool.getStderr(), tool.getStderr().isEmpty());
assertEquals(0, tool.getExitCode());
assertGoodEnvPostTest();
}
@Test
@Ignore
// the logic is used only to generate test data for testLoadingOfSStableWithRangeTombstoneAsMaxClustering
// the result of the generation is committed to SCM
public void generateSSTableWithRangeTombstone() throws IOException
{
SchemaLoader.prepareServer();
StorageService.instance.initServer();
Keyspace.setInitialized();
QueryProcessor.executeInternal("CREATE KEYSPACE sstable_metadata WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}");
QueryProcessor.executeInternal("CREATE TABLE sstable_metadata.CASSANDRA_20855 (pk text, ck_1 text, ck_2 text, val text, PRIMARY KEY (pk, ck_1, ck_2))");
QueryProcessor.executeInternal(String.format("INSERT INTO sstable_metadata.CASSANDRA_20855 (pk, ck_1, ck_2, val) VALUES ('%s', '%s', '%s', '%s')",
"part_key_1", "clust_key_1_1", "clust_key_2_1", "value"));
QueryProcessor.executeInternal(String.format("DELETE FROM sstable_metadata.CASSANDRA_20855 WHERE pk = '%s' AND ck_1 = '%s'",
"part_key_1", "clust_key_1_2"));
StorageService.instance.forceKeyspaceFlush("sstable_metadata");
}
private static String copySSTables(String keyspace, String table) throws IOException
{
File targetKeyspaceDir = new File("build/test/cassandra/data/", keyspace);
targetKeyspaceDir.mkdirs();
File targetTableDir = new File(targetKeyspaceDir, table);
File srcKeyspaceDir = new File("test/data/" + keyspace);
FileUtils.copyDirectory(new File(srcKeyspaceDir, table), targetTableDir);
File[] sstableFiles = targetTableDir.listFiles((file) -> file.isFile() && file.getName().endsWith("-Data.db"));
return sstableFiles[0].getAbsolutePath();
}
}