diff --git a/CHANGES.txt b/CHANGES.txt
index 7297b3d8f9..9abbbf54aa 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
3.11.14
+ * Document usage of closed token intervals in manual compaction (CASSANDRA-17575)
* Creating of a keyspace on insufficient number of replicas should filter out gosspping-only members (CASSANDRA-17759)
* Only use statically defined subcolumns when determining column definition for supercolumn cell (CASSANDRA-14113)
Merged from 3.0:
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java b/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
index e7b191743e..07201c5ab5 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
@@ -49,7 +49,12 @@ public interface ColumnFamilyStoreMBean
public void forceMajorCompaction(boolean splitOutput) throws ExecutionException, InterruptedException;
/**
- * force a major compaction of specified key range in this column family
+ * Forces a major compaction of specified token ranges in this column family.
+ *
+ * The token ranges will be interpreted as closed intervals to match the closed interval defined by the first and
+ * last keys of a sstable, even though the {@link Range} class is suppossed to be half-open by definition.
+ *
+ * @param tokenRanges The token ranges to be compacted, interpreted as closed intervals.
*/
public void forceCompactionForTokenRange(Collection> tokenRanges) throws ExecutionException, InterruptedException;
/**
diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
index 76f6a98833..82e0bf2f5e 100644
--- a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
+++ b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
@@ -812,6 +812,15 @@ public class CompactionManager implements CompactionManagerMBean
return futures;
}
+ /**
+ * Forces a major compaction of specified token ranges of the specified column family.
+ *
+ * The token ranges will be interpreted as closed intervals to match the closed interval defined by the first and
+ * last keys of a sstable, even though the {@link Range} class is suppossed to be half-open by definition.
+ *
+ * @param cfStore The column family store to be compacted.
+ * @param ranges The token ranges to be compacted, interpreted as closed intervals.
+ */
public void forceCompactionForTokenRange(ColumnFamilyStore cfStore, Collection> ranges)
{
Callable> taskCreator = () -> {
@@ -849,6 +858,12 @@ public class CompactionManager implements CompactionManagerMBean
FBUtilities.waitOnFuture(executor.submitIfRunning(runnable, "force compaction for token range"));
}
+ /**
+ * Returns the sstables of the specified column family store that intersect with the specified token ranges.
+ *
+ * The token ranges will be interpreted as closed intervals to match the closed interval defined by the first and
+ * last keys of a sstable, even though the {@link Range} class is suppossed to be half-open by definition.
+ */
private static Collection sstablesInBounds(ColumnFamilyStore cfs, Collection> tokenRangeCollection)
{
final Set sstables = new HashSet<>();
diff --git a/src/java/org/apache/cassandra/service/StorageServiceMBean.java b/src/java/org/apache/cassandra/service/StorageServiceMBean.java
index 233673ad30..f456d4eb59 100644
--- a/src/java/org/apache/cassandra/service/StorageServiceMBean.java
+++ b/src/java/org/apache/cassandra/service/StorageServiceMBean.java
@@ -265,8 +265,14 @@ public interface StorageServiceMBean extends NotificationEmitter
@Deprecated
public int relocateSSTables(String keyspace, String ... cfnames) throws IOException, ExecutionException, InterruptedException;
public int relocateSSTables(int jobs, String keyspace, String ... cfnames) throws IOException, ExecutionException, InterruptedException;
+
/**
- * Forces major compaction of specified token range in a single keyspace
+ * Forces major compaction of specified token range in a single keyspace.
+ *
+ * @param keyspaceName the name of the keyspace to be compacted
+ * @param startToken the token at which the compaction range starts (inclusive)
+ * @param endToken the token at which compaction range ends (inclusive)
+ * @param tableNames the names of the tables to be compacted
*/
public void forceKeyspaceCompactionForTokenRange(String keyspaceName, String startToken, String endToken, String... tableNames) throws IOException, ExecutionException, InterruptedException;
diff --git a/src/java/org/apache/cassandra/tools/NodeProbe.java b/src/java/org/apache/cassandra/tools/NodeProbe.java
index c63f2a1d87..821b8a3bd6 100644
--- a/src/java/org/apache/cassandra/tools/NodeProbe.java
+++ b/src/java/org/apache/cassandra/tools/NodeProbe.java
@@ -397,6 +397,14 @@ public class NodeProbe implements AutoCloseable
ssProxy.relocateSSTables(jobs, keyspace, cfnames);
}
+ /**
+ * Forces major compaction of specified token range in a single keyspace.
+ *
+ * @param keyspaceName the name of the keyspace to be compacted
+ * @param startToken the token at which the compaction range starts (inclusive)
+ * @param endToken the token at which compaction range ends (inclusive)
+ * @param tableNames the names of the tables to be compacted
+ */
public void forceKeyspaceCompactionForTokenRange(String keyspaceName, final String startToken, final String endToken, String... tableNames) throws IOException, ExecutionException, InterruptedException
{
ssProxy.forceKeyspaceCompactionForTokenRange(keyspaceName, startToken, endToken, tableNames);
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Compact.java b/src/java/org/apache/cassandra/tools/nodetool/Compact.java
index ef10a836b2..d4731902a6 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Compact.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Compact.java
@@ -41,10 +41,10 @@ public class Compact extends NodeToolCmd
@Option(title = "user-defined", name = {"--user-defined"}, description = "Use --user-defined to submit listed files for user-defined compaction")
private boolean userDefined = false;
- @Option(title = "start_token", name = {"-st", "--start-token"}, description = "Use -st to specify a token at which the compaction range starts")
+ @Option(title = "start_token", name = {"-st", "--start-token"}, description = "Use -st to specify a token at which the compaction range starts (inclusive)")
private String startToken = EMPTY;
- @Option(title = "end_token", name = {"-et", "--end-token"}, description = "Use -et to specify a token at which compaction range ends")
+ @Option(title = "end_token", name = {"-et", "--end-token"}, description = "Use -et to specify a token at which compaction range ends (inclusive)")
private String endToken = EMPTY;
diff --git a/test/unit/org/apache/cassandra/db/compaction/LeveledCompactionStrategyTest.java b/test/unit/org/apache/cassandra/db/compaction/LeveledCompactionStrategyTest.java
index 91b9b3b7d3..9b59e73a2c 100644
--- a/test/unit/org/apache/cassandra/db/compaction/LeveledCompactionStrategyTest.java
+++ b/test/unit/org/apache/cassandra/db/compaction/LeveledCompactionStrategyTest.java
@@ -385,12 +385,10 @@ public class LeveledCompactionStrategyTest
assertFalse(repaired.manifest.getLevel(1).contains(sstable2));
}
-
-
@Test
public void testTokenRangeCompaction() throws Exception
{
- // Remove any existing data so we can start out clean with predictable number of sstables
+ // Remove any existing data, so we can start out clean with predictable number of sstables
cfs.truncateBlocking();
// Disable auto compaction so cassandra does not compact
@@ -400,15 +398,17 @@ public class LeveledCompactionStrategyTest
DecoratedKey key1 = Util.dk(String.valueOf(1));
DecoratedKey key2 = Util.dk(String.valueOf(2));
- List keys = new ArrayList<>(Arrays.asList(key1, key2));
+ List keys = Arrays.asList(key1, key2);
int numIterations = 10;
int columns = 2;
// Add enough data to trigger multiple sstables.
// create 10 sstables that contain data for both key1 and key2
- for (int i = 0; i < numIterations; i++) {
- for (DecoratedKey key : keys) {
+ for (int i = 0; i < numIterations; i++)
+ {
+ for (DecoratedKey key : keys)
+ {
UpdateBuilder update = UpdateBuilder.create(cfs.metadata, key);
for (int c = 0; c < columns; c++)
update.newRow("column" + c).add("val", value);
@@ -418,8 +418,10 @@ public class LeveledCompactionStrategyTest
}
// create 20 more sstables with 10 containing data for key1 and other 10 containing data for key2
- for (int i = 0; i < numIterations; i++) {
- for (DecoratedKey key : keys) {
+ for (int i = 0; i < numIterations; i++)
+ {
+ for (DecoratedKey key : keys)
+ {
UpdateBuilder update = UpdateBuilder.create(cfs.metadata, key);
for (int c = 0; c < columns; c++)
update.newRow("column" + c).add("val", value);
@@ -431,13 +433,14 @@ public class LeveledCompactionStrategyTest
// We should have a total of 30 sstables by now
assertEquals(30, cfs.getLiveSSTables().size());
- // Compact just the tables with key2
- // Bit hackish to use the key1.token as the prior key but works in BytesToken
+ // Compact just the tables with key2. The token ranges for compaction are interpreted as closed intervals,
+ // so we can use [token, token] to select a single token.
Range tokenRange = new Range<>(key2.getToken(), key2.getToken());
- Collection> tokenRanges = new ArrayList<>(Arrays.asList(tokenRange));
+ Collection> tokenRanges = singleton(tokenRange);
cfs.forceCompactionForTokenRange(tokenRanges);
- while(CompactionManager.instance.isCompacting(Arrays.asList(cfs))) {
+ while (CompactionManager.instance.isCompacting(singleton(cfs)))
+ {
Thread.sleep(100);
}
@@ -446,11 +449,11 @@ public class LeveledCompactionStrategyTest
// Compact just the tables with key1. At this point all 11 tables should have key1
Range tokenRange2 = new Range<>(key1.getToken(), key1.getToken());
- Collection> tokenRanges2 = new ArrayList<>(Arrays.asList(tokenRange2));
+ Collection> tokenRanges2 = new ArrayList<>(singleton(tokenRange2));
cfs.forceCompactionForTokenRange(tokenRanges2);
-
- while(CompactionManager.instance.isCompacting(Arrays.asList(cfs))) {
+ while (CompactionManager.instance.isCompacting(singleton(cfs)))
+ {
Thread.sleep(100);
}