diff --git a/src/java/org/apache/cassandra/db/guardrails/CMSSizeGuardrail.java b/src/java/org/apache/cassandra/db/guardrails/CMSSizeGuardrail.java index e1fb4c0b9a..b404d45fcc 100644 --- a/src/java/org/apache/cassandra/db/guardrails/CMSSizeGuardrail.java +++ b/src/java/org/apache/cassandra/db/guardrails/CMSSizeGuardrail.java @@ -46,11 +46,8 @@ public class CMSSizeGuardrail extends MinThreshold public void guard(int totalNodes, int cmsSize) { - // A cluster can't host more CMS replicas than it has nodes. If the request already uses every node there's - // nothing more to enforce (e.g. during bootstrap), and requiring more would brick reconfiguration. Otherwise - // the request must meet the configured minimum, even when the threshold exceeds the current cluster size, so - // an operator can't sidestep the floor by setting a high threshold. An operator who genuinely wants a smaller - // CMS should disable the guardrail. When it is disabled, guard() below is a no-op. + // If the request already uses every node, the cluster can't do better, so skip the check. + // Otherwise the CMS size must meet the configured minimum. if (cmsSize >= totalNodes) return; diff --git a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java index cc44b5add8..7275524a51 100644 --- a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java +++ b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java @@ -618,7 +618,8 @@ public final class Guardrails implements GuardrailsMBean /** * Guardrail on the minimum CMS size (aggregate replication factor across DCs). */ - public static final CMSSizeGuardrail minimumCmsSize = new CMSSizeGuardrail(state -> CONFIG_PROVIDER.getOrCreate(state).getMinimumCmsSizeFailThreshold()); + public static final CMSSizeGuardrail minimumCmsSize = + new CMSSizeGuardrail(state -> CONFIG_PROVIDER.getOrCreate(state).getMinimumCmsSizeFailThreshold()); /** * Guardrail on the maximum replication factor. diff --git a/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailCmsSizeReconfigurationTest.java b/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailCmsSizeReconfigurationTest.java index e99f15f0ec..ecdf76370d 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailCmsSizeReconfigurationTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/guardrails/GuardrailCmsSizeReconfigurationTest.java @@ -90,9 +90,8 @@ public class GuardrailCmsSizeReconfigurationTest extends TestBaseImpl @Test public void reconfigureBelowClusterCapacityIsRejectedWhenThresholdAboveClusterSize() throws IOException { - // Threshold (4) is above the cluster size (3), but the cluster can still host a CMS of 3. Requesting a smaller - // size than the cluster can support must be rejected rather than silently skipped, so a high threshold can't - // be used to sidestep the floor. + // Threshold (4) is above the cluster size (3), but the cluster can still host a CMS of 3, so a request for 2 + // is rejected. try (Cluster cluster = build(3).withConfig(config(4)).start()) { cluster.get(1).nodetoolResult("cms", "reconfigure", "2") @@ -105,8 +104,8 @@ public class GuardrailCmsSizeReconfigurationTest extends TestBaseImpl @Test public void reconfigureAtClusterCapacityIsAllowedWhenThresholdAboveClusterSize() throws IOException { - // Threshold (4) is above the cluster size (3). A CMS of 3 already uses every node, so it is allowed even though - // it is below the threshold: the cluster simply cannot do better, and blocking it would brick reconfiguration. + // Threshold (4) is above the cluster size (3). A CMS of 3 already uses every node, so it is allowed even + // though it is below the threshold. try (Cluster cluster = build(3).withConfig(config(4)).start()) { cluster.get(1).nodetoolResult("cms", "reconfigure", "3") @@ -115,6 +114,20 @@ public class GuardrailCmsSizeReconfigurationTest extends TestBaseImpl } } + @Test + public void reconfigureAboveClusterSizeIsNotBlockedByGuardrail() throws IOException + { + // Requested size (5) exceeds the cluster size (3), so the guardrail does not apply. The reconfiguration still + // fails, but because CMS placement can't find enough nodes, not with the guardrail's "failure threshold" error. + try (Cluster cluster = build(3).withConfig(config(10)).start()) + { + cluster.get(1).nodetoolResult("cms", "reconfigure", "5") + .asserts() + .failure() + .stderrContains("not enough nodes"); + } + } + @Test public void reconfigureMultiDcBelowThresholdIsRejected() throws IOException {