Also provide test.pu util function to trigger it. Can be useful for
testing.
---
CI:
https://jenkins.scylladb.com/job/scylla-master/job/scylla-ci/11707/
diff --git a/test/topology/util.py b/test/topology/util.py
index afa3c7acd2..65ca70bc22 100644
--- a/test/topology/util.py
+++ b/test/topology/util.py
@@ -398,6 +398,14 @@ async def trigger_snapshot(manager, server: ServerInfo) -> None:
host = cql.cluster.metadata.get_host(server.ip_addr)
await
manager.api.client.post(f"/raft/trigger_snapshot/{group0_id}", host=server.ip_addr)
+async def trigger_stepdown(manager, server: ServerInfo) -> None:
+ cql = manager.get_cql()
+ group0_id = (await cql.run_async(
+ "select value from system.scylla_local where key = 'raft_group0_id'"))[0].value
+
+ host = cql.cluster.metadata.get_host(server.ip_addr)
+ await
manager.api.client.post(f"/raft/trigger_stepdown/{group0_id}", host=server.ip_addr)
+
async def get_coordinator_host_ids(manager: ManagerClient) -> list[str]:
diff --git a/api/raft.cc b/api/raft.cc
index bdf83b2358..115ffcc6c7 100644
--- a/api/raft.cc
+++ b/api/raft.cc
@@ -123,6 +123,37 @@ void set_raft(http_context&, httpd::routes& r, sharded<service::raft_group_regis
throw bad_param_exception{fmt::format("Server for group ID {} not found", gid)};
}
+ co_return json_void{};
+ });
+ r::trigger_stepdown.set(r, [&raft_gr] (std::unique_ptr<http::request> req) -> future<json_return_type> {
+ raft::group_id gid{utils::UUID{req->get_path_param("group_id")}};
+ auto timeout_dur = std::invoke([timeout_str = req->get_query_param("timeout")] {
+ if (timeout_str.empty()) {
+ return std::chrono::seconds{60};
+ }
+ auto dur = std::stoll(timeout_str);
+ if (dur <= 0) {
+ throw std::runtime_error{"Timeout must be a positive number."};
+ }
+ return std::chrono::seconds{dur};
+ });
+
+ std::atomic<bool> found_srv{false};
+ co_await raft_gr.invoke_on_all([gid, timeout_dur, &found_srv] (service::raft_group_registry& raft_gr) -> future<> {
+ auto* srv = raft_gr.find_server(gid);
+ if (!srv) {
+ co_return;
+ }
+
+ found_srv = true;
+
apilog.info("Triggering stepdown for group {}", gid);
+ co_await srv->stepdown(timeout_dur);
+ });
+
+ if (!found_srv) {
+ throw std::runtime_error{fmt::format("Server for group ID {} not found", gid)};
+ }
+
co_return json_void{};
});
}
@@ -131,6 +162,7 @@ void unset_raft(http_context&, httpd::routes& r) {
r::trigger_snapshot.unset(r);
r::get_leader_host.unset(r);
r::read_barrier.unset(r);
+ r::trigger_stepdown.unset(r);
}
}
diff --git a/api/api-doc/raft.json b/api/api-doc/raft.json
index 971475eb9a..8a8d90b981 100644
--- a/api/api-doc/raft.json
+++ b/api/api-doc/raft.json
@@ -94,6 +94,38 @@
]
}
]
+ },
+ {
+ "path":"/raft/trigger_stepdown/{group_id}",
+ "operations":[
+ {
+ "method":"POST",
+ "summary":"Triggers stepdown of a leader for given Raft group (does nothing if the node is not a leader)",
+ "type":"string",
+ "nickname":"trigger_stepdown",
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "name":"group_id",
+ "description":"The ID of the group which leader should stepdown",
+ "required":true,
+ "allowMultiple":false,
+ "type":"string",
+ "paramType":"path"
+ },
+ {
+ "name":"timeout",
+ "description":"Timeout in seconds after which the endpoint returns a failure. If not provided, 60s is used.",
+ "required":false,
+ "allowMultiple":false,
+ "type":"long",
+ "paramType":"query"
+ }
+ ]
+ }
+ ]
}
]
}
--
Gleb.