From 68f0cf6fac1be25def05209ee00e84d9744933e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Jadwiszczak?= Date: Tue, 12 May 2026 23:11:14 +0200 Subject: [PATCH 1/2] strong_consistency: allow only QUORUM/LOCAL_QUORUM CL for writes To successfully write data to strong consistent table, a quorum of replicas need to be used to save the data to Raft log. So the only reasonable consistency level is QUORUM/LOCAL_QUORUM (currently SC doesn't support multi DC). --- .../strong_consistency/modification_statement.cc | 9 +++++++++ test/cluster/test_strong_consistency.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cql3/statements/strong_consistency/modification_statement.cc b/cql3/statements/strong_consistency/modification_statement.cc index 51990448f1..e24b674ee7 100644 --- a/cql3/statements/strong_consistency/modification_statement.cc +++ b/cql3/statements/strong_consistency/modification_statement.cc @@ -8,6 +8,7 @@ #include "modification_statement.hh" +#include "db/consistency_level_type.hh" #include "db/timeout_clock.hh" #include "transport/messages/result_message.hh" #include "cql3/query_processor.hh" @@ -34,10 +35,18 @@ future> modification_statement::execute(query_process .then(cql_transport::messages::propagate_exception_as_future>); } +static void validate_consistency_level(const db::consistency_level& cl) { + if (cl != db::consistency_level::QUORUM && cl != db::consistency_level::LOCAL_QUORUM) { + throw exceptions::invalid_request_exception("Strongly consistent writes must use QUORUM/LOCAL_QUORUM consistency level"); + } +} + future> modification_statement::execute_without_checking_exception_message( query_processor& qp, service::query_state& qs, const query_options& options, std::optional guard) const { + validate_consistency_level(options.get_consistency()); + auto timeout = db::timeout_clock::now() + _statement->get_timeout(qs.get_client_state(), options); auto json_cache = base_statement::json_cache_opt{}; const auto keys = _statement->build_partition_keys(options, json_cache); diff --git a/test/cluster/test_strong_consistency.py b/test/cluster/test_strong_consistency.py index 1dbd5566c7..e3eb76830d 100644 --- a/test/cluster/test_strong_consistency.py +++ b/test/cluster/test_strong_consistency.py @@ -191,7 +191,7 @@ async def test_basic_write_read(manager: ManagerClient): # Test with prepared statements as well insert_stmt = cql.prepare(f"INSERT INTO {ks}.test (pk, c) VALUES (?, ?)") - bound_insert_stmt = BoundStatement(insert_stmt, consistency_level=ConsistencyLevel.ONE) + bound_insert_stmt = BoundStatement(insert_stmt) select_stmt = cql.prepare(f"SELECT * FROM {ks}.test WHERE pk = ?") bound_select_stmt = BoundStatement(select_stmt, consistency_level=ConsistencyLevel.ONE) bound_select_stmt.bind([10]) From d073097ebfa5fa04dddad9898a5fc6d4c8bac684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Jadwiszczak?= Date: Tue, 12 May 2026 23:13:29 +0200 Subject: [PATCH 2/2] strong_consistency: allow QUORUM/LOCAL_QUORUM and ONE/LOCAL_ONE for reads We can execute strong consistent read queries in 2 ways: - with QUORUM/LOCAL_QUORUM CL - this path executes `read_barrier()` before reading the data, which synchronizes Raft log with the leader. But to execute it, we need quorum of replicas - with ONE/LOCAL_ONE CL - this path just reads data from one replica without any synchronization (not implemented yet) --- cql3/statements/strong_consistency/select_statement.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cql3/statements/strong_consistency/select_statement.cc b/cql3/statements/strong_consistency/select_statement.cc index 98bfb22c5c..213f89948a 100644 --- a/cql3/statements/strong_consistency/select_statement.cc +++ b/cql3/statements/strong_consistency/select_statement.cc @@ -8,6 +8,7 @@ #include "select_statement.hh" +#include "db/consistency_level_type.hh" #include "query/query-request.hh" #include "cql3/query_processor.hh" #include "service/strong_consistency/coordinator.hh" @@ -17,10 +18,19 @@ namespace cql3::statements::strong_consistency { using result_message = cql_transport::messages::result_message; +static void validate_consistency_level(const db::consistency_level& cl) { + if (cl != db::consistency_level::QUORUM && cl != db::consistency_level::LOCAL_QUORUM && + cl != db::consistency_level::ONE && cl != db::consistency_level::LOCAL_ONE) { + throw exceptions::invalid_request_exception("Strongly consistent reads must use QUORUM/LOCAL_QUORUM or ONE/LOCAL_ONE consistency level"); + } +} + future<::shared_ptr> select_statement::do_execute(query_processor& qp, service::query_state& state, const query_options& options) const { + validate_consistency_level(options.get_consistency()); + const auto key_ranges = _restrictions->get_partition_key_ranges(options); if (key_ranges.size() != 1 || !query::is_single_partition(key_ranges[0])) { throw exceptions::invalid_request_exception("Strongly consistent queries can only target a single partition");