This is an automated email from the ASF dual-hosted git repository.
adelapena pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new b72da02611 Add guardrail for partition tombstones and deprecate
compaction_tombstone_warning_threshold
b72da02611 is described below
commit b72da02611b62436100fa3fd66537c68461bcaf5
Author: Andrés de la Peña <[email protected]>
AuthorDate: Wed May 24 19:20:35 2023 +0100
Add guardrail for partition tombstones and deprecate
compaction_tombstone_warning_threshold
patch by Andrés de la Peña; reviewed by Berenguer Blasi and Maxwell Guo for
CASSANDRA-17194
---
CHANGES.txt | 1 +
NEWS.txt | 4 +
conf/cassandra.yaml | 12 +-
src/java/org/apache/cassandra/config/Config.java | 3 +
.../cassandra/config/DatabaseDescriptor.java | 2 +
.../apache/cassandra/config/GuardrailsOptions.java | 26 +++++
.../apache/cassandra/db/guardrails/Guardrails.java | 32 +++++-
.../cassandra/db/guardrails/GuardrailsConfig.java | 10 ++
.../cassandra/db/guardrails/GuardrailsMBean.java | 26 ++++-
.../io/sstable/format/SortedTableWriter.java | 22 ++--
.../apache/cassandra/service/StorageService.java | 2 +
.../cassandra/service/StorageServiceMBean.java | 6 +-
.../guardrails/GuardrailPartitionSizeTest.java | 7 +-
....java => GuardrailPartitionTombstonesTest.java} | 88 ++++++--------
.../GuardrailPartitionTombstonesTest.java | 127 +++++++++++++++++++++
15 files changed, 298 insertions(+), 70 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 99a53404b0..0882bbaa77 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
5.0
+ * Add guardrail for partition tombstones and deprecate
compaction_tombstone_warning_threshold (CASSANDRA-17194)
* Print header and statistics for cassandra-stress output with arbitrary
frequency (CASSANDRA-12972)
* CEP-25: Trie-indexed SSTable format (CASSANDRA-18398)
* Make cassandra-stress able to read all credentials from a file
(CASSANDRA-18544)
diff --git a/NEWS.txt b/NEWS.txt
index 2129ead915..294e57d1f8 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -83,6 +83,7 @@ New features
- Whether DROP KEYSPACE commands are allowed.
- Column value size
- Partition size
+ - Partition tombstones
- It is possible to list ephemeral snapshots by nodetool listsnaphots
command when flag "-e" is specified.
- Added a new flag to `nodetool profileload` and JMX endpoint to set up
recurring profile load generation on specified
intervals (see CASSANDRA-17821)
@@ -193,6 +194,9 @@ Deprecation
- The config property `compaction_large_partition_warning_threshold` has
been deprecated in favour of the new
guardrail for partition size. That guardrail is based on the properties
`partition_size_warn_threshold` and
`partition_size_fail_threshold`. The warn threshold has a very similar
behaviour to the old config property.
+ - The config property `compaction_tombstone_warning_threshold` has been
deprecated in favour of the new guardrail
+ for partition tombstones. That guardrail is based on the properties
`partition_tombstones_warn_threshold` and
+ `partition_tombstones_fail_threshold`. The warn threshold has a very
similar behaviour to the old config property.
4.1
===
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index ca865b19da..ce39c019ee 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -1564,7 +1564,8 @@ unlogged_batch_across_partitions_warn_threshold: 10
# As of Cassandra 5.0, this property is deprecated in favour of
partition_size_warn_threshold.
compaction_large_partition_warning_threshold: 100MiB
-# Log a warning when writing more tombstones than this value to a partition
+# Log a warning when writing more tombstones than this value to a partition.
+# As of Cassandra 5.0, this property is deprecated in favour of
partition_tombstones_warn_threshold.
compaction_tombstone_warning_threshold: 100000
# GC Pauses greater than 200 ms will be logged at INFO level
@@ -1842,6 +1843,15 @@ drop_compact_storage_enabled: false
# partition_size_warn_threshold:
# partition_size_fail_threshold:
#
+# Guardrail to warn or fail when writing partitions with more tombstones than
threshold.
+# The guardrail is only checked when writing sstables (flush and compaction),
and exceeding the fail threshold on that
+# moment will only log an error message, without interrupting the operation.
+# This operates on a per-sstable basis, so it won't detect a large partition
if it is spread across multiple sstables.
+# The warning threshold replaces the deprecated config property
compaction_tombstone_warning_threshold.
+# The two thresholds default to -1 to disable.
+# partition_tombstones_warn_threshold: -1
+# partition_tombstones_fail_threshold: -1
+#
# Guardrail to warn or fail when writing column values larger than threshold.
# This guardrail is only applied to the values of regular columns because both
the serialized partitions keys and the
# values of the components of the clustering key already have a fixed,
relatively small size limit of 65535 bytes, which
diff --git a/src/java/org/apache/cassandra/config/Config.java
b/src/java/org/apache/cassandra/config/Config.java
index 6e5f8739f7..56ecb199d4 100644
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@ -332,6 +332,7 @@ public class Config
public volatile DataStorageSpec.IntMebibytesBound
compaction_large_partition_warning_threshold = new
DataStorageSpec.IntMebibytesBound("100MiB");
@Replaces(oldName = "min_free_space_per_drive_in_mb", converter =
Converters.MEBIBYTES_DATA_STORAGE_INT, deprecated = true)
public DataStorageSpec.IntMebibytesBound min_free_space_per_drive = new
DataStorageSpec.IntMebibytesBound("50MiB");
+ @Deprecated
public volatile Integer compaction_tombstone_warning_threshold = 100000;
// fraction of free disk space available for compaction after min free
space is subtracted
@@ -875,6 +876,8 @@ public class Config
public volatile boolean simplestrategy_enabled = true;
public volatile DataStorageSpec.LongBytesBound
partition_size_warn_threshold = null;
public volatile DataStorageSpec.LongBytesBound
partition_size_fail_threshold = null;
+ public volatile long partition_tombstones_warn_threshold = -1;
+ public volatile long partition_tombstones_fail_threshold = -1;
public volatile DataStorageSpec.LongBytesBound
column_value_size_warn_threshold = null;
public volatile DataStorageSpec.LongBytesBound
column_value_size_fail_threshold = null;
public volatile DataStorageSpec.LongBytesBound
collection_size_warn_threshold = null;
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 2531e5c6e4..424ac67cf0 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -2178,11 +2178,13 @@ public class DatabaseDescriptor
return
conf.compaction_large_partition_warning_threshold.toBytesInLong();
}
+ @Deprecated
public static int getCompactionTombstoneWarningThreshold()
{
return conf.compaction_tombstone_warning_threshold;
}
+ @Deprecated
public static void setCompactionTombstoneWarningThreshold(int count)
{
conf.compaction_tombstone_warning_threshold = count;
diff --git a/src/java/org/apache/cassandra/config/GuardrailsOptions.java
b/src/java/org/apache/cassandra/config/GuardrailsOptions.java
index 7affe6f7cf..f58408e8b2 100644
--- a/src/java/org/apache/cassandra/config/GuardrailsOptions.java
+++ b/src/java/org/apache/cassandra/config/GuardrailsOptions.java
@@ -77,6 +77,7 @@ public class GuardrailsOptions implements GuardrailsConfig
config.write_consistency_levels_warned =
validateConsistencyLevels(config.write_consistency_levels_warned,
"write_consistency_levels_warned");
config.write_consistency_levels_disallowed =
validateConsistencyLevels(config.write_consistency_levels_disallowed,
"write_consistency_levels_disallowed");
validateSizeThreshold(config.partition_size_warn_threshold,
config.partition_size_fail_threshold, false, "partition_size");
+ validateMaxLongThreshold(config.partition_tombstones_warn_threshold,
config.partition_tombstones_fail_threshold, "partition_tombstones", false);
validateSizeThreshold(config.column_value_size_warn_threshold,
config.column_value_size_fail_threshold, false, "column_value_size");
validateSizeThreshold(config.collection_size_warn_threshold,
config.collection_size_fail_threshold, false, "collection_size");
validateMaxIntThreshold(config.items_per_collection_warn_threshold,
config.items_per_collection_fail_threshold, "items_per_collection");
@@ -567,6 +568,31 @@ public class GuardrailsOptions implements GuardrailsConfig
x -> config.partition_size_fail_threshold =
x);
}
+ @Override
+ public long getPartitionTombstonesWarnThreshold()
+ {
+ return config.partition_tombstones_warn_threshold;
+ }
+
+ @Override
+ public long getPartitionTombstonesFailThreshold()
+ {
+ return config.partition_tombstones_fail_threshold;
+ }
+
+ public void setPartitionTombstonesThreshold(long warn, long fail)
+ {
+ validateMaxLongThreshold(warn, fail, "partition_tombstones", false);
+ updatePropertyWithLogging("partition_tombstones_warn_threshold",
+ warn,
+ () ->
config.partition_tombstones_warn_threshold,
+ x ->
config.partition_tombstones_warn_threshold = x);
+ updatePropertyWithLogging("partition_tombstones_fail_threshold",
+ fail,
+ () ->
config.partition_tombstones_fail_threshold,
+ x ->
config.partition_tombstones_fail_threshold = x);
+ }
+
@Override
@Nullable
public DataStorageSpec.LongBytesBound getColumnValueSizeWarnThreshold()
diff --git a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java
b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java
index d2289b4bb6..4f736337e8 100644
--- a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java
+++ b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java
@@ -316,13 +316,25 @@ public final class Guardrails implements GuardrailsMBean
*/
public static final MaxThreshold partitionSize =
new MaxThreshold("partition_size",
- "Too large partitions can cause performance problems. ",
+ "Too large partitions can cause performance problems.",
state ->
sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getPartitionSizeWarnThreshold()),
state ->
sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getPartitionSizeFailThreshold()),
(isWarning, what, value, threshold) ->
format("Partition %s has size %s, this exceeds
the %s threshold of %s.",
what, value, isWarning ? "warning" :
"failure", threshold));
+ /**
+ * Guardrail on the number of rows of a partition.
+ */
+ public static final MaxThreshold partitionTombstones =
+ new MaxThreshold("partition_tombstones",
+ "Partitions with too many tombstones can cause
performance problems.",
+ state ->
CONFIG_PROVIDER.getOrCreate(state).getPartitionTombstonesWarnThreshold(),
+ state ->
CONFIG_PROVIDER.getOrCreate(state).getPartitionTombstonesFailThreshold(),
+ (isWarning, what, value, threshold) ->
+ format("Partition %s has %s tombstones, this
exceeds the %s threshold of %s.",
+ what, value, isWarning ? "warning" :
"failure", threshold));
+
/**
* Guardrail on the size of a collection.
*/
@@ -823,6 +835,24 @@ public final class Guardrails implements GuardrailsMBean
DEFAULT_CONFIG.setPartitionSizeThreshold(sizeFromString(warnSize),
sizeFromString(failSize));
}
+ @Override
+ public long getPartitionTombstonesWarnThreshold()
+ {
+ return DEFAULT_CONFIG.getPartitionTombstonesWarnThreshold();
+ }
+
+ @Override
+ public long getPartitionTombstonesFailThreshold()
+ {
+ return DEFAULT_CONFIG.getPartitionTombstonesFailThreshold();
+ }
+
+ @Override
+ public void setPartitionTombstonesThreshold(long warn, long fail)
+ {
+ DEFAULT_CONFIG.setPartitionTombstonesThreshold(warn, fail);
+ }
+
@Override
@Nullable
public String getColumnValueSizeWarnThreshold()
diff --git a/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java
b/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java
index af1b5b48c0..9639066595 100644
--- a/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java
+++ b/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java
@@ -250,6 +250,16 @@ public interface GuardrailsConfig
@Nullable
DataStorageSpec.LongBytesBound getPartitionSizeFailThreshold();
+ /**
+ * @return The threshold to warn when writing partitions with more
tombstones than threshold.
+ */
+ long getPartitionTombstonesWarnThreshold();
+
+ /**
+ * @return The threshold to fail when writing partitions with more
tombstones than threshold.
+ */
+ long getPartitionTombstonesFailThreshold();
+
/**
* @return The threshold to warn when writing column values larger than
threshold.
*/
diff --git a/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java
b/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java
index cabbe0c59e..0c8ff344ab 100644
--- a/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java
+++ b/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java
@@ -372,13 +372,13 @@ public interface GuardrailsMBean
* @return The threshold to warn when an IN query creates a cartesian
product with a size exceeding threshold.
* -1 means disabled.
*/
- public int getInSelectCartesianProductWarnThreshold();
+ int getInSelectCartesianProductWarnThreshold();
/**
* @return The threshold to prevent IN queries creating a cartesian
product with a size exceeding threshold.
* -1 means disabled.
*/
- public int getInSelectCartesianProductFailThreshold();
+ int getInSelectCartesianProductFailThreshold();
/**
* @param warn The threshold to warn when an IN query creates a cartesian
product with a size exceeding threshold.
@@ -386,7 +386,7 @@ public interface GuardrailsMBean
* @param fail The threshold to prevent IN queries creating a cartesian
product with a size exceeding threshold.
* -1 means disabled.
*/
- public void setInSelectCartesianProductThreshold(int warn, int fail);
+ void setInSelectCartesianProductThreshold(int warn, int fail);
/**
* @return consistency levels that are warned about when reading.
@@ -495,6 +495,26 @@ public interface GuardrailsMBean
*/
void setPartitionSizeThreshold(@Nullable String warnSize, @Nullable String
failSize);
+ /**
+ * @return The threshold to warn when encountering partitions with more
tombstones than threshold. -1 means disabled.
+ */
+ long getPartitionTombstonesWarnThreshold();
+
+ /**
+ * @return The threshold to fail when encountering partitions with more
tombstones than threshold. -1 means disabled.
+ * Triggering a failure emits a log message and a diagnostic event, but it
doesn't throw an exception interrupting
+ * the offending sstable write.
+ */
+ long getPartitionTombstonesFailThreshold();
+
+ /**
+ * @param warn The threshold to warn when encountering partitions with
more tombstones than threshold. -1 means disabled.
+ * @param fail The threshold to fail when encountering partitions with
more tombstones than threshold. -1 means disabled.
+ * Triggering a failure emits a log message and a diagnostic
event, but it desn't throw an exception
+ * interrupting the offending sstable write.
+ */
+ void setPartitionTombstonesThreshold(long warn, long fail);
+
/**
* @return The threshold to warn when encountering column values larger
than threshold, as a string formatted as
* in, for example, {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code
40B}. A {@code null} value means disabled.
diff --git
a/src/java/org/apache/cassandra/io/sstable/format/SortedTableWriter.java
b/src/java/org/apache/cassandra/io/sstable/format/SortedTableWriter.java
index 377169faf0..2deda2a6f7 100644
--- a/src/java/org/apache/cassandra/io/sstable/format/SortedTableWriter.java
+++ b/src/java/org/apache/cassandra/io/sstable/format/SortedTableWriter.java
@@ -32,6 +32,7 @@ import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.db.DeletionPurger;
import org.apache.cassandra.db.DeletionTime;
import org.apache.cassandra.db.guardrails.Guardrails;
+import org.apache.cassandra.db.guardrails.Threshold;
import org.apache.cassandra.db.lifecycle.LifecycleNewTracker;
import org.apache.cassandra.db.rows.ComplexColumnData;
import org.apache.cassandra.db.rows.PartitionSerializationException;
@@ -213,7 +214,8 @@ public abstract class SortedTableWriter<P extends
SortedTablePartitionWriter> ex
long endPosition = dataWriter.position();
long rowSize = endPosition - partitionWriter.getInitialPosition();
- guardPartitionSize(key, rowSize);
+ guardPartitionThreshold(Guardrails.partitionSize, key, rowSize);
+ guardPartitionThreshold(Guardrails.partitionTombstones, key,
metadataCollector.totalTombstones);
maybeLogLargePartitionWarning(key, rowSize);
maybeLogManyTombstonesWarning(key, metadataCollector.totalTombstones);
metadataCollector.addPartitionSizeInBytes(rowSize);
@@ -325,19 +327,20 @@ public abstract class SortedTableWriter<P extends
SortedTablePartitionWriter> ex
return dataFile;
}
- private void guardPartitionSize(DecoratedKey key, long rowSize)
+ private void guardPartitionThreshold(Threshold guardrail, DecoratedKey
key, long size)
{
- if (Guardrails.partitionSize.triggersOn(rowSize, null))
+ if (guardrail.triggersOn(size, null))
{
- String what = String.format("%s.%s:%s on sstable %s",
- metadata.keyspace,
- metadata.name,
-
metadata().partitionKeyType.getString(key.getKey()),
- getFilename());
- Guardrails.partitionSize.guard(rowSize, what, true, null);
+ String message = String.format("%s.%s:%s on sstable %s",
+ metadata.keyspace,
+ metadata.name,
+
metadata().partitionKeyType.getString(key.getKey()),
+ getFilename());
+ guardrail.guard(size, message, true, null);
}
}
+ @Deprecated
private void maybeLogLargePartitionWarning(DecoratedKey key, long rowSize)
{
if (rowSize >
DatabaseDescriptor.getCompactionLargePartitionWarningThreshold())
@@ -347,6 +350,7 @@ public abstract class SortedTableWriter<P extends
SortedTablePartitionWriter> ex
}
}
+ @Deprecated
private void maybeLogManyTombstonesWarning(DecoratedKey key, int
tombstoneCount)
{
if (tombstoneCount >
DatabaseDescriptor.getCompactionTombstoneWarningThreshold())
diff --git a/src/java/org/apache/cassandra/service/StorageService.java
b/src/java/org/apache/cassandra/service/StorageService.java
index 5e66718577..5f99fef4bc 100644
--- a/src/java/org/apache/cassandra/service/StorageService.java
+++ b/src/java/org/apache/cassandra/service/StorageService.java
@@ -6949,6 +6949,7 @@ public class StorageService extends
NotificationBroadcasterSupport implements IE
DatabaseDescriptor.setKeyspaceCountWarnThreshold(value);
}
+ @Override
public void setCompactionTombstoneWarningThreshold(int count)
{
if (count < 0)
@@ -6957,6 +6958,7 @@ public class StorageService extends
NotificationBroadcasterSupport implements IE
DatabaseDescriptor.setCompactionTombstoneWarningThreshold(count);
}
+ @Override
public int getCompactionTombstoneWarningThreshold()
{
return DatabaseDescriptor.getCompactionTombstoneWarningThreshold();
diff --git a/src/java/org/apache/cassandra/service/StorageServiceMBean.java
b/src/java/org/apache/cassandra/service/StorageServiceMBean.java
index 5897afcce3..154b0d86d3 100644
--- a/src/java/org/apache/cassandra/service/StorageServiceMBean.java
+++ b/src/java/org/apache/cassandra/service/StorageServiceMBean.java
@@ -1115,8 +1115,10 @@ public interface StorageServiceMBean extends
NotificationEmitter
@Deprecated
void setKeyspaceCountWarnThreshold(int value);
- public void setCompactionTombstoneWarningThreshold(int count);
- public int getCompactionTombstoneWarningThreshold();
+ @Deprecated
+ void setCompactionTombstoneWarningThreshold(int count);
+ @Deprecated
+ int getCompactionTombstoneWarningThreshold();
public boolean getReadThresholdsEnabled();
public void setReadThresholdsEnabled(boolean value);
diff --git
a/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionSizeTest.java
b/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionSizeTest.java
index b2ee8d7d94..8dc49e14c9 100644
---
a/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionSizeTest.java
+++
b/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionSizeTest.java
@@ -75,12 +75,11 @@ public class GuardrailPartitionSizeTest extends
GuardrailTester
@Test
public void testPartitionSize()
{
+ // test yaml-loaded config
testPartitionSize(WARN_THRESHOLD, FAIL_THRESHOLD);
- }
+ schemaChange("DROP TABLE %s");
- @Test
- public void testPartitionSizeWithDynamicUpdate()
- {
+ // test dynamic config
int warn = WARN_THRESHOLD * 2;
int fail = FAIL_THRESHOLD * 2;
cluster.get(1).runOnInstance(() ->
Guardrails.instance.setPartitionSizeThreshold(warn + "B", fail + "B"));
diff --git
a/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionSizeTest.java
b/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionTombstonesTest.java
similarity index 54%
copy from
test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionSizeTest.java
copy to
test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionTombstonesTest.java
index b2ee8d7d94..24045cad33 100644
---
a/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionSizeTest.java
+++
b/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailPartitionTombstonesTest.java
@@ -28,32 +28,26 @@ import org.apache.cassandra.db.guardrails.Guardrails;
import org.apache.cassandra.distributed.Cluster;
import org.apache.cassandra.distributed.api.ConsistencyLevel;
-import static java.nio.ByteBuffer.allocate;
-
/**
- * Tests the guardrail for the size of partition size, {@link
Guardrails#partitionSize}.
+ * Tests the guardrail for the number of tombstones in a partition, {@link
Guardrails#partitionTombstones}.
* <p>
* This test only includes the activation of the guardrail during sstable
writes, focusing on the emmitted log messages.
* The tests for config, client warnings and diagnostic events are in
- * {@link org.apache.cassandra.db.guardrails.GuardrailPartitionSizeTest}.
+ * {@link org.apache.cassandra.db.guardrails.GuardrailPartitionTombstonesTest}.
*/
-public class GuardrailPartitionSizeTest extends GuardrailTester
+public class GuardrailPartitionTombstonesTest extends GuardrailTester
{
- private static final int WARN_THRESHOLD = 1024 * 1024; // bytes (1 MiB)
- private static final int FAIL_THRESHOLD = WARN_THRESHOLD * 2; // bytes (2
MiB)
-
- private static final int NUM_NODES = 1;
- private static final int NUM_CLUSTERINGS = 5;
+ private static final int WARN_THRESHOLD = 500; // high enough to exceed
system tables, which aren't excluded
+ private static final int FAIL_THRESHOLD = WARN_THRESHOLD * 2;
private static Cluster cluster;
@BeforeClass
public static void setupCluster() throws IOException
{
- cluster = init(Cluster.build(NUM_NODES)
- .withConfig(c ->
c.set("partition_size_warn_threshold", WARN_THRESHOLD + "B")
-
.set("partition_size_fail_threshold", FAIL_THRESHOLD + "B")
-
.set("compaction_large_partition_warning_threshold", "999GiB")
+ cluster = init(Cluster.build(1)
+ .withConfig(c ->
c.set("partition_tombstones_warn_threshold", WARN_THRESHOLD)
+
.set("partition_tombstones_fail_threshold", FAIL_THRESHOLD)
.set("memtable_heap_space",
"512MiB")) // avoids flushes
.start());
cluster.disableAutoCompaction(KEYSPACE);
@@ -73,70 +67,64 @@ public class GuardrailPartitionSizeTest extends
GuardrailTester
}
@Test
- public void testPartitionSize()
- {
- testPartitionSize(WARN_THRESHOLD, FAIL_THRESHOLD);
- }
-
- @Test
- public void testPartitionSizeWithDynamicUpdate()
+ public void testPartitionTombstones()
{
- int warn = WARN_THRESHOLD * 2;
- int fail = FAIL_THRESHOLD * 2;
- cluster.get(1).runOnInstance(() ->
Guardrails.instance.setPartitionSizeThreshold(warn + "B", fail + "B"));
- testPartitionSize(warn, fail);
+ // test yaml-loaded config
+ testPartitionTombstones(WARN_THRESHOLD, FAIL_THRESHOLD);
+ schemaChange("DROP TABLE %s");
+
+ // test dynamic config
+ int warn = WARN_THRESHOLD + 10;
+ int fail = FAIL_THRESHOLD + 10;
+ cluster.get(1).runOnInstance(() ->
Guardrails.instance.setPartitionTombstonesThreshold(warn, fail));
+ testPartitionTombstones(warn, fail);
}
- private void testPartitionSize(int warn, int fail)
+ private void testPartitionTombstones(int warn, int fail)
{
- schemaChange("CREATE TABLE %s (k int, c int, v blob, PRIMARY KEY (k,
c))");
+ schemaChange("CREATE TABLE %s (k int, c int, v int, PRIMARY KEY (k,
c))");
// empty table
assertNotWarnedOnFlush();
assertNotWarnedOnCompact();
- // keep partition size lower than thresholds
- execute("INSERT INTO %s (k, c, v) VALUES (1, 1, ?)", allocate(1));
+ // keep partition tombstones lower than thresholds
+ populateTable(warn);
assertNotWarnedOnFlush();
assertNotWarnedOnCompact();
// exceed warn threshold
- for (int c = 0; c < NUM_CLUSTERINGS; c++)
- {
- execute("INSERT INTO %s (k, c, v) VALUES (1, ?, ?)", c,
allocate(warn / NUM_CLUSTERINGS));
- }
+ populateTable(warn + 1);
assertWarnedOnFlush(expectedMessages(1));
assertWarnedOnCompact(expectedMessages(1));
// exceed fail threshold
- for (int c = 0; c < NUM_CLUSTERINGS * 10; c++)
- {
- execute("INSERT INTO %s (k, c, v) VALUES (1, ?, ?)", c,
allocate(fail / NUM_CLUSTERINGS));
- }
+ populateTable(fail + 1);
assertFailedOnFlush(expectedMessages(1));
assertFailedOnCompact(expectedMessages(1));
- // remove most of the data to be under the threshold again
+ // remove most of the tombstones to be under the threshold again
execute("DELETE FROM %s WHERE k = 1 AND c > 1");
assertNotWarnedOnFlush();
assertNotWarnedOnCompact();
- // exceed warn threshold in multiple partitions
- for (int c = 0; c < NUM_CLUSTERINGS; c++)
+ // exceed warn threshold in multiple partitions,
+ // this shouldn't trigger the fail threshold because the count is
per-partition.
+ for (int c = 0; c <= warn; c++)
{
- execute("INSERT INTO %s (k, c, v) VALUES (1, ?, ?)", c,
allocate(warn / NUM_CLUSTERINGS));
- execute("INSERT INTO %s (k, c, v) VALUES (2, ?, ?)", c,
allocate(warn / NUM_CLUSTERINGS));
+ execute("DELETE FROM %s WHERE k = 2 AND c = ?", c);
+ execute("DELETE FROM %s WHERE k = 3 AND c = ?", c);
}
- assertWarnedOnFlush(expectedMessages(1, 2));
- assertWarnedOnCompact(expectedMessages(1, 2));
+ assertWarnedOnFlush(expectedMessages(2, 3));
+ assertWarnedOnCompact(expectedMessages(2, 3));
+ }
- // exceed warn threshold in a new partition
- for (int c = 0; c < NUM_CLUSTERINGS; c++)
+ private void populateTable(int numTombstones)
+ {
+ for (int c = 0; c < numTombstones; c++)
{
- execute("INSERT INTO %s (k, c, v) VALUES (3, ?, ?)", c,
allocate(warn / NUM_CLUSTERINGS));
+ execute("DELETE FROM %s WHERE k = 1 AND c = ?", c);
}
- assertWarnedOnFlush(expectedMessages(3));
- assertWarnedOnCompact(expectedMessages(1, 2, 3));
}
private void execute(String query, Object... args)
@@ -148,7 +136,7 @@ public class GuardrailPartitionSizeTest extends
GuardrailTester
{
String[] messages = new String[keys.length];
for (int i = 0; i < keys.length; i++)
- messages[i] = String.format("Guardrail partition_size violated:
Partition %s:%d", qualifiedTableName, keys[i]);
+ messages[i] = String.format("Guardrail partition_tombstones
violated: Partition %s:%d", qualifiedTableName, keys[i]);
return messages;
}
}
diff --git
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailPartitionTombstonesTest.java
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailPartitionTombstonesTest.java
new file mode 100644
index 0000000000..bb5618bc5c
--- /dev/null
+++
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailPartitionTombstonesTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.db.guardrails;
+
+import org.junit.Test;
+
+/**
+ * Tests the guardrail for the number of tombstones in a partition, {@link
Guardrails#partitionTombstones}.
+ * <p>
+ * The emission on unredacted log messages is tested in {@link
org.apache.cassandra.distributed.test.guardrails.GuardrailPartitionTombstonesTest}.
+ */
+public class GuardrailPartitionTombstonesTest extends ThresholdTester
+{
+ private static final int WARN_THRESHOLD = 500; // high enough to exceed
system tables, which aren't excluded
+ private static final int FAIL_THRESHOLD = WARN_THRESHOLD + 1;
+ private static final String REDACTED_MESSAGE = "Guardrail
partition_tombstones violated: Partition <redacted>";
+
+ public GuardrailPartitionTombstonesTest()
+ {
+ super(WARN_THRESHOLD,
+ FAIL_THRESHOLD,
+ Guardrails.partitionTombstones,
+ Guardrails::setPartitionTombstonesThreshold,
+ Guardrails::getPartitionTombstonesWarnThreshold,
+ Guardrails::getPartitionTombstonesFailThreshold);
+ }
+
+ @Test
+ public void testPartitionTombstonesEnabled() throws Throwable
+ {
+ // Insert tombstones under both thresholds.
+ populateTable(WARN_THRESHOLD);
+
+ flush();
+ listener.assertNotWarned();
+ listener.assertNotFailed();
+ listener.clear();
+
+ compact();
+ listener.assertNotWarned();
+ listener.assertNotFailed();
+ listener.clear();
+
+ // Insert enough tombstones to trigger the warning guardrail, but not
the failure one.
+ populateTable(WARN_THRESHOLD + 1);
+
+ flush();
+ listener.assertWarned(REDACTED_MESSAGE);
+ listener.clear();
+
+ compact();
+ listener.assertWarned(REDACTED_MESSAGE);
+ listener.clear();
+
+ // Insert enough tombstones to trigger the failure guardrail.
+ populateTable(FAIL_THRESHOLD + 1);
+
+ flush();
+ listener.assertFailed(REDACTED_MESSAGE);
+ listener.clear();
+
+ compact();
+ listener.assertFailed(REDACTED_MESSAGE);
+ listener.clear();
+
+ // remove most of the data to be under the threshold again
+ assertValid("DELETE FROM %s WHERE k = 1 AND c > 0");
+
+ flush();
+ listener.assertNotWarned();
+ listener.assertNotFailed();
+
+ compact();
+ listener.assertNotWarned();
+ listener.assertNotFailed();
+ listener.clear();
+ }
+
+ @Test
+ public void testPartitionTombstonesDisabled() throws Throwable
+ {
+ guardrails().setPartitionTombstonesThreshold(-1, -1);
+
+ populateTable(FAIL_THRESHOLD + 1);
+
+ flush();
+ listener.assertNotWarned();
+ listener.assertNotFailed();
+
+ compact();
+ listener.assertNotWarned();
+ listener.assertNotFailed();
+ }
+
+ private void populateTable(int numTombstones) throws Throwable
+ {
+ createTable("CREATE TABLE IF NOT EXISTS %s (k int, c int, v int,
PRIMARY KEY(k, c))");
+ disableCompaction();
+
+ for (int c = 0; c < numTombstones; c++)
+ {
+ assertValid("DELETE FROM %s WHERE k = 1 AND c = " + c);
+ }
+
+ // insert some additional partitions to make sure the guardrail is
per-partition
+ for (int k = 0; k <= FAIL_THRESHOLD; k++)
+ {
+ assertValid("DELETE FROM %s WHERE k = " + k + " AND c = 0");
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]