Merge branch 'cassandra-4.1' into cassandra-5.0

* cassandra-4.1:
  Rename conflicting nodetool import --copy-data short option from -p to -cd
This commit is contained in:
Maxim Muzafarov 2026-06-19 22:04:38 +02:00
commit 3fa111c70f
No known key found for this signature in database
GPG Key ID: 7FEC714D84388C16
4 changed files with 71 additions and 20 deletions

View File

@ -8,6 +8,7 @@
Merged from 4.1: Merged from 4.1:
* Add Paxos v2 option and informatin in cassandra.yaml (CASSANDRA-21316) * Add Paxos v2 option and informatin in cassandra.yaml (CASSANDRA-21316)
Merged from 4.0: Merged from 4.0:
* Rename conflicting nodetool import --copy-data short option from -p to -cd (CASSANDRA-20214)
* Fix PasswordObfuscator failing to obfuscate certain passwords (CASSANDRA-21113) * Fix PasswordObfuscator failing to obfuscate certain passwords (CASSANDRA-21113)
* Fix negative memtable allocator ownership when an update is shadowed by an existing row deletion (CASSANDRA-21469) * Fix negative memtable allocator ownership when an update is shadowed by an existing row deletion (CASSANDRA-21469)
* Consider first token of SSTable when calculating SSTable intersection in LeveledScanner (CASSANDRA-21369) * Consider first token of SSTable when calculating SSTable intersection in LeveledScanner (CASSANDRA-21369)

View File

@ -469,16 +469,16 @@ public class SSTableImporter
public static class Options public static class Options
{ {
private final Set<String> srcPaths; final Set<String> srcPaths;
private final boolean resetLevel; final boolean resetLevel;
private final boolean clearRepaired; final boolean clearRepaired;
private final boolean verifySSTables; final boolean verifySSTables;
private final boolean verifyTokens; final boolean verifyTokens;
private final boolean invalidateCaches; final boolean invalidateCaches;
private final boolean extendedVerify; final boolean extendedVerify;
private final boolean copyData; final boolean copyData;
private final boolean failOnMissingIndex; final boolean failOnMissingIndex;
public final boolean validateIndexChecksum; final boolean validateIndexChecksum;
public Options(Set<String> srcPaths, boolean resetLevel, boolean clearRepaired, public Options(Set<String> srcPaths, boolean resetLevel, boolean clearRepaired,
boolean verifySSTables, boolean verifyTokens, boolean invalidateCaches, boolean verifySSTables, boolean verifyTokens, boolean invalidateCaches,

View File

@ -76,7 +76,7 @@ public class Import extends NodeToolCmd
private boolean extendedVerify = false; private boolean extendedVerify = false;
@Option(title = "copy_data", @Option(title = "copy_data",
name = {"-p", "--copy-data"}, name = {"-cd", "--copy-data"},
description = "Copy data from source directories instead of moving them") description = "Copy data from source directories instead of moving them")
private boolean copyData = false; private boolean copyData = false;

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -31,8 +32,8 @@ import java.util.Set;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.apache.cassandra.Util; import org.apache.cassandra.Util;
@ -58,20 +59,32 @@ import org.apache.cassandra.locator.TokenMetadata;
import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.service.CacheService; import org.apache.cassandra.service.CacheService;
import org.apache.cassandra.service.StorageService; import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.tools.ToolRunner;
import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.FBUtilities;
import static org.apache.lucene.codecs.CodecUtil.FOOTER_MAGIC; import static org.apache.lucene.codecs.CodecUtil.FOOTER_MAGIC;
import static org.apache.lucene.codecs.CodecUtil.writeBEInt; import static org.apache.lucene.codecs.CodecUtil.writeBEInt;
import static org.apache.lucene.codecs.CodecUtil.writeBELong; import static org.apache.lucene.codecs.CodecUtil.writeBELong;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/**
* @see org.apache.cassandra.tools.nodetool.Import
*/
public class ImportTest extends CQLTester public class ImportTest extends CQLTester
{ {
@After @BeforeClass
public void afterTest() public static void setupClass() throws Throwable
{
startJMXServer();
}
@Override
public void afterTest() throws Throwable
{ {
SSTableReader.resetTidying(); SSTableReader.resetTidying();
} }
@ -93,7 +106,7 @@ public class ImportTest extends CQLTester
// copy is true - so importing will be done by copying // copy is true - so importing will be done by copying
importSSTables(SSTableImporter.Options.options(backupDir.toString()).copyData(true).build(), 10); importSSTables(SSTableImporter.Options.options(backupDir.toString()).copyData(true).build(), 10);
// files are left there as they were just copied // files are left there as they were just copied
Assert.assertNotEquals(0, countFiles(backupDir)); assertNotEquals(0, countFiles(backupDir));
} }
private File prepareBasicImporting() throws Throwable private File prepareBasicImporting() throws Throwable
@ -364,9 +377,9 @@ public class ImportTest extends CQLTester
// then we moved out 1 sstable, a correct one (in backupdirCorrect). // then we moved out 1 sstable, a correct one (in backupdirCorrect).
// now import should fail import on backupdir, but import the one in backupdirCorrect. // now import should fail import on backupdir, but import the one in backupdirCorrect.
SSTableImporter.Options options = SSTableImporter.Options.options(Sets.newHashSet(backupdir.toString(), backupdirCorrect.toString())).copyData(copy).verifySSTables(verify).build(); SSTableImporter.Options options = SSTableImporter.Options.options(Sets.newHashSet(backupdir.toString(), backupdirCorrect.toString())).copyData(copy).verifySSTables(verify).build();
SSTableImporter importer = new SSTableImporter(getCurrentColumnFamilyStore()); ToolRunner.ToolResult result = assertImportFailed(getCurrentColumnFamilyStore(), options);
List<String> failedDirectories = importer.importNewSSTables(options); assertThat(result.getStderr()).contains("Some directories failed to import, check server logs for details:");
assertEquals(Collections.singletonList(backupdir.toString()), failedDirectories); assertThat(result.getStderr()).contains(backupdir.toString());
UntypedResultSet res = execute("SELECT * FROM %s"); UntypedResultSet res = execute("SELECT * FROM %s");
for (UntypedResultSet.Row r : res) for (UntypedResultSet.Row r : res)
{ {
@ -724,7 +737,7 @@ public class ImportTest extends CQLTester
assertEquals(10, execute(String.format("select * from %s.%s", KEYSPACE, table)).size()); assertEquals(10, execute(String.format("select * from %s.%s", KEYSPACE, table)).size());
// files are left there as they were just copied // files are left there as they were just copied
Assert.assertNotEquals(0, countFiles(backupDir)); assertNotEquals(0, countFiles(backupDir));
} }
finally finally
{ {
@ -768,7 +781,7 @@ public class ImportTest extends CQLTester
assertEquals(10, execute(String.format("select * from %s.%s", KEYSPACE, table)).size()); assertEquals(10, execute(String.format("select * from %s.%s", KEYSPACE, table)).size());
// files are left there as they were just copied // files are left there as they were just copied
Assert.assertNotEquals(0, countFiles(backupDir)); assertNotEquals(0, countFiles(backupDir));
} }
finally finally
{ {
@ -985,6 +998,43 @@ public class ImportTest extends CQLTester
} }
} }
private static ToolRunner.ToolResult assertImportFailed(ColumnFamilyStore cfs, SSTableImporter.Options options)
{
ToolRunner.ToolResult result = doImportWithNodetool(cfs, options);
assertNotEquals(0, result.getExitCode());
return result;
}
private static ToolRunner.ToolResult doImportWithNodetool(ColumnFamilyStore cfs, SSTableImporter.Options options)
{
List<String> args = new ArrayList<>();
args.add("import");
if (!options.resetLevel)
args.add("--keep-level");
if (!options.clearRepaired)
args.add("--keep-repaired");
if (!options.verifySSTables)
args.add("--no-verify");
if (!options.verifyTokens)
args.add("--no-tokens");
if (!options.invalidateCaches)
args.add("--no-invalidate-caches");
if (options.extendedVerify)
args.add("--extended-verify");
if (options.copyData)
args.add("-cd");
if (options.failOnMissingIndex)
args.add("--require-index-components");
if (!options.validateIndexChecksum)
args.add("--no-index-validation");
args.add(cfs.keyspace.getName());
args.add(cfs.getTableName());
args.addAll(options.srcPaths);
return ToolRunner.invokeNodetool(args.toArray(new String[0]));
}
private static class MockCFS extends ColumnFamilyStore private static class MockCFS extends ColumnFamilyStore
{ {
public MockCFS(ColumnFamilyStore cfs, Directories dirs) public MockCFS(ColumnFamilyStore cfs, Directories dirs)