nodetool: cluster repair: do not fail if a table was dropped

nodetool cluster repair without additional params repairs all tablet
keyspaces in a cluster. Currently, if a table is dropped while
the command is running, all tables are repaired but the command finishes
with a failure.

Modify nodetool cluster repair. If a table wasn't specified
(i.e. all tables are repaired), the command finishes successfully
even if a table was dropped.

If a table was specified and it does not exist (e.g. because it was
dropped before the repair was requested), then the behavior remains
unchanged.

Fixes: SCYLLADB-568.

Closes scylladb/scylladb#28739

(cherry picked from commit 2e68f48068)

Closes scylladb/scylladb#29280
This commit is contained in:
Aleksandra Martyniuk
2026-02-20 11:14:13 +01:00
committed by Avi Kivity
parent 70b4d640d3
commit 88e00db91c
2 changed files with 68 additions and 0 deletions

View File

@@ -368,3 +368,68 @@ def test_repair_keyspace(nodetool):
expected_request("GET", "/storage_service/keyspaces", params={"replication": "tablets"}, response=[]),
]},
["error processing arguments: nodetool cluster repair repairs only tablet keyspaces. To repair vnode keyspaces use nodetool repair."])
def test_cluster_repair_table_dropped(nodetool):
id1 = "ef1b7a61-66c8-494c-bb03-6f65724e6eee"
res = nodetool("cluster", "repair", "ks", expected_requests=[
expected_request("GET", "/storage_service/keyspaces", response=["ks"]),
expected_request("GET", "/storage_service/keyspaces", params={"replication": "tablets"}, response=["ks"]),
expected_request("GET", "/column_family", response=[{"ks": "ks", "cf": "table1"}, {"ks": "ks", "cf": "table2"}]),
expected_request(
"POST",
"/storage_service/tablets/repair",
params={
"ks": "ks",
"table": "table1",
"tokens": "all"},
response={"message": "Can't find a column family table1 in keyspace ks", "code": 400}, response_status=400),
expected_request(
"POST",
"/storage_service/tablets/repair",
params={
"ks": "ks",
"table": "table2",
"tokens": "all"},
response={"tablet_task_id": id1}),
expected_request(
"GET",
f"/task_manager/wait_task/{id1}",
response={"state": "done"}),
])
assert _remove_log_timestamp(res.stdout) == f"""\
Starting repair with task_id={id1} keyspace=ks table=table2
Repair with task_id={id1} finished
"""
def test_cluster_repair_specified_table_dropped(nodetool):
id1 = "ef1b7a61-66c8-494c-bb03-6f65724e6eee"
check_nodetool_fails_with_error_contains(
nodetool,
("cluster", "repair", "ks", "table1", "table2"),
{"expected_requests": [
expected_request("GET", "/storage_service/keyspaces", response=["ks"]),
expected_request("GET", "/storage_service/keyspaces", params={"replication": "tablets"}, response=["ks"]),
expected_request(
"POST",
"/storage_service/tablets/repair",
params={
"ks": "ks",
"table": "table1",
"tokens": "all"},
response={"message": "Can't find a column family table1 in keyspace ks", "code": 400}, response_status=400),
expected_request(
"POST",
"/storage_service/tablets/repair",
params={
"ks": "ks",
"table": "table2",
"tokens": "all"},
response={"tablet_task_id": id1}),
expected_request(
"GET",
f"/task_manager/wait_task/{id1}",
response={"state": "done"}),
]
},
[f"Can't find a column family table1 in keyspace ks"])

View File

@@ -583,6 +583,9 @@ void cluster_repair_operation(scylla_rest_client& client, const bpo::variables_m
log("Repair with task_id={} finished", task_id);
}
} catch (const api_request_failed& ex) {
if (tables.empty() && std::string(ex.what()).contains("Can't find a column family")) {
continue;
}
log("ERROR: Repair request for keyspace={} table={} failed with {}", keyspace, table, ex);
exit_code = EXIT_FAILURE;
}