test: add new guardrail tests matching documentation scenarios
Add tests for RF guardrails (min/max warn/fail, RF=0 bypass, threshold=-1 disable, ALTER KEYSPACE) and write consistency level guardrails to cover all scenarios described in guardrails.rst. Test runtime (dev): test_guardrail_replication_strategy - 6s test_guardrail_write_consistency_level - 5s Refs: SCYLLADB-257
This commit is contained in:
@@ -191,3 +191,86 @@ def test_config_replication_strategy_warn_list_roundtrips_quotes(cql):
|
||||
cql.execute("UPDATE system.config SET value = '[SimpleStrategy]' WHERE name = 'replication_strategy_warn_list'")
|
||||
# reproduces #
|
||||
cql.execute("UPDATE system.config SET value = '[\"SimpleStrategy\"]' WHERE name = 'replication_strategy_warn_list'")
|
||||
|
||||
|
||||
def test_rf_zero_always_allowed(cql, this_dc):
|
||||
"""Maximum RF guardrails fire correctly with high RF, but RF=0
|
||||
(meaning 'do not replicate to this data center') must never trigger
|
||||
any guardrail — even when both minimum and maximum thresholds are
|
||||
active. Also verifies metric increments and message formats for
|
||||
maximum RF guardrails (docs/cql/guardrails.rst)."""
|
||||
with ExitStack() as config_modifications:
|
||||
config_modifications.enter_context(config_value_context(cql, 'minimum_replication_factor_warn_threshold', '3'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'minimum_replication_factor_fail_threshold', '2'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'maximum_replication_factor_warn_threshold', '5'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'maximum_replication_factor_fail_threshold', '7'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'replication_strategy_warn_list', ''))
|
||||
dc = re.escape(this_dc)
|
||||
|
||||
# max RF warn: RF=6 > warn=5 but < fail=7
|
||||
create_ks_and_assert_warnings_and_errors(cql, ks_opts('NetworkTopologyStrategy', 6, dc=this_dc, tablets=False),
|
||||
metric_name='scylla_cql_maximum_replication_factor_warn_violations',
|
||||
warnings=[MAXIMUM_RF_WARN_RE.format(dc=dc, rf=6, threshold=5)])
|
||||
|
||||
# max RF fail: RF=8 > fail=7
|
||||
create_ks_and_assert_warnings_and_errors(cql, ks_opts('NetworkTopologyStrategy', 8, dc=this_dc, tablets=False),
|
||||
metric_name='scylla_cql_maximum_replication_factor_fail_violations',
|
||||
failures=[MAXIMUM_RF_FAIL_RE.format(dc=dc, rf=8, threshold=7)])
|
||||
|
||||
# RF=0 bypasses all guardrails.
|
||||
create_ks_and_assert_warnings_and_errors(cql, ks_opts('NetworkTopologyStrategy', 0, dc=this_dc, tablets=True))
|
||||
|
||||
|
||||
def test_rf_threshold_minus_one_disables_check(cql, this_dc):
|
||||
"""Setting an RF threshold to -1 disables that guardrail entirely.
|
||||
Verify that with all four thresholds set to -1, any RF value (low or
|
||||
high) is accepted without warnings or errors."""
|
||||
with ExitStack() as config_modifications:
|
||||
config_modifications.enter_context(config_value_context(cql, 'minimum_replication_factor_warn_threshold', '-1'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'minimum_replication_factor_fail_threshold', '-1'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'maximum_replication_factor_warn_threshold', '-1'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'maximum_replication_factor_fail_threshold', '-1'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'replication_strategy_warn_list', ''))
|
||||
# RF=1 — would normally trigger the default minimum_replication_factor_warn_threshold=3
|
||||
create_ks_and_assert_warnings_and_errors(cql, ks_opts('NetworkTopologyStrategy', 1, dc=this_dc, tablets=True))
|
||||
# RF=100 — would normally trigger maximum thresholds; disable tablets
|
||||
# to avoid the rack count check.
|
||||
create_ks_and_assert_warnings_and_errors(cql, ks_opts('NetworkTopologyStrategy', 100, dc=this_dc, tablets=False))
|
||||
|
||||
|
||||
def test_alter_keyspace_minimum_rf_warn(cql, this_dc):
|
||||
with ExitStack() as config_modifications:
|
||||
config_modifications.enter_context(config_value_context(cql, 'minimum_replication_factor_warn_threshold', '3'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'replication_strategy_warn_list', ''))
|
||||
with new_test_keyspace(cql, ks_opts('NetworkTopologyStrategy', 3, dc=this_dc, tablets=False)) as ks:
|
||||
response_future = cql.execute_async(f"ALTER KEYSPACE {ks}" + ks_opts('NetworkTopologyStrategy', 1, dc=this_dc))
|
||||
response_future.result()
|
||||
assert response_future.warnings is not None
|
||||
|
||||
|
||||
def test_alter_keyspace_minimum_rf_fail(cql, this_dc):
|
||||
with ExitStack() as config_modifications:
|
||||
config_modifications.enter_context(config_value_context(cql, 'minimum_replication_factor_fail_threshold', '3'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'replication_strategy_warn_list', ''))
|
||||
with new_test_keyspace(cql, ks_opts('NetworkTopologyStrategy', 3, dc=this_dc, tablets=False)) as ks:
|
||||
with pytest.raises(ConfigurationException):
|
||||
cql.execute(f"ALTER KEYSPACE {ks}" + ks_opts('NetworkTopologyStrategy', 1, dc=this_dc))
|
||||
|
||||
|
||||
def test_alter_keyspace_maximum_rf_warn(cql, this_dc):
|
||||
with ExitStack() as config_modifications:
|
||||
config_modifications.enter_context(config_value_context(cql, 'maximum_replication_factor_warn_threshold', '2'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'replication_strategy_warn_list', ''))
|
||||
with new_test_keyspace(cql, ks_opts('NetworkTopologyStrategy', 1, dc=this_dc, tablets=False)) as ks:
|
||||
response_future = cql.execute_async(f"ALTER KEYSPACE {ks}" + ks_opts('NetworkTopologyStrategy', 3, dc=this_dc))
|
||||
response_future.result()
|
||||
assert response_future.warnings is not None
|
||||
|
||||
|
||||
def test_alter_keyspace_maximum_rf_fail(cql, this_dc):
|
||||
with ExitStack() as config_modifications:
|
||||
config_modifications.enter_context(config_value_context(cql, 'maximum_replication_factor_fail_threshold', '2'))
|
||||
config_modifications.enter_context(config_value_context(cql, 'replication_strategy_warn_list', ''))
|
||||
with new_test_keyspace(cql, ks_opts('NetworkTopologyStrategy', 1, dc=this_dc, tablets=False)) as ks:
|
||||
with pytest.raises(ConfigurationException):
|
||||
cql.execute(f"ALTER KEYSPACE {ks}" + ks_opts('NetworkTopologyStrategy', 3, dc=this_dc))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
# SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
||||
|
||||
import pytest
|
||||
import re
|
||||
from contextlib import ExitStack
|
||||
from cassandra import ConsistencyLevel, WriteFailure
|
||||
from cassandra.protocol import InvalidRequest
|
||||
@@ -49,13 +50,18 @@ def check_warned(cql, query, cl=ConsistencyLevel.ONE, config_value=None):
|
||||
assert after_writes > before_writes
|
||||
assert after_warned > before_warned
|
||||
assert len(ret.response_future.warnings) > 0
|
||||
cl_name = ConsistencyLevel.value_to_name[cl]
|
||||
warning = "\n".join(ret.response_future.warnings)
|
||||
assert re.search(f"{cl_name}.*write_consistency_levels_warned.*not recommended", warning)
|
||||
|
||||
def check_disallowed(cql, query, cl=ConsistencyLevel.ONE, config_value=None):
|
||||
with config_value_context(cql, "write_consistency_levels_disallowed", config_value or ConsistencyLevel.value_to_name[cl]):
|
||||
cl_name = ConsistencyLevel.value_to_name[cl]
|
||||
with config_value_context(cql, "write_consistency_levels_disallowed", config_value or cl_name):
|
||||
before_writes = get_metric(cql, WRITES_METRIC, cl)
|
||||
before_disallowed = get_metric(cql, DISALLOWED_METRIC)
|
||||
|
||||
with pytest.raises(InvalidRequest, match="(?i)not allowed"):
|
||||
# Verify the error mentions the guardrail name and that the CL is forbidden.
|
||||
with pytest.raises(InvalidRequest, match=f"{cl_name}.*forbidden.*write_consistency_levels_disallowed"):
|
||||
cql.execute(SimpleStatement(query, consistency_level=cl))
|
||||
|
||||
after_writes = get_metric(cql, WRITES_METRIC, cl)
|
||||
@@ -158,3 +164,4 @@ def test_write_cl_multiple_disallowed_levels(cql, test_table):
|
||||
check_disallowed(cql, query, cl=ConsistencyLevel.ALL, config_value=config)
|
||||
check_disallowed(cql, query, cl=ConsistencyLevel.ANY, config_value=config)
|
||||
check_no_warning(cql, query, cl=ConsistencyLevel.QUORUM, disallowed=config)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user