Simplified CMS guardrail check and add test to catch corner cases

This commit is contained in:
nvharikrishna 2026-07-25 18:41:42 +05:30
parent 239160cda0
commit b507bf9b52
3 changed files with 22 additions and 11 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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
{