service: Don't use new tablet_resize_finalization state until supported

In a rolling upgrade, nodes that weren't upgraded yet will not recognize
the new tablet_resize_finalization state, that serves both split and
merges, leading to a crash. To fix that, coordinator will pick the
old tablet_split_finalization state for serving split finalization,
until the cluster agrees on merge, so it can start using the new
generic state for resize finalization introduced in merge series.
Regression was introduced in e00798f.

Fixes #22840.

Reported-by: Tomasz Grabiec <tgrabiec@scylladb.com>
Signed-off-by: Raphael S. Carvalho <raphaelsc@scylladb.com>

Closes scylladb/scylladb#22845
This commit is contained in:
Raphael S. Carvalho
2025-02-13 10:42:38 -03:00
committed by Avi Kivity
parent de8de50fb9
commit d78f57e94a
5 changed files with 12 additions and 11 deletions

View File

@@ -744,6 +744,8 @@ future<> storage_service::topology_state_load(state_change_hint hint) {
[[fallthrough]];
case topology::transition_state::tablet_migration:
[[fallthrough]];
case topology::transition_state::tablet_split_finalization:
[[fallthrough]];
case topology::transition_state::tablet_resize_finalization:
[[fallthrough]];
case topology::transition_state::commit_cdc_generation:

View File

@@ -2338,6 +2338,8 @@ class topology_coordinator : public endpoint_lifecycle_subscriber {
case topology::transition_state::tablet_migration:
co_await handle_tablet_migration(std::move(guard), false);
break;
case topology::transition_state::tablet_split_finalization:
[[fallthrough]];
case topology::transition_state::tablet_resize_finalization:
co_await handle_tablet_resize_finalization(std::move(guard));
break;
@@ -2918,11 +2920,15 @@ future<bool> topology_coordinator::maybe_start_tablet_resize_finalization(group0
co_return false;
}
auto resize_finalization_transition_state = [this] {
return _db.features().tablet_merge ? topology::transition_state::tablet_resize_finalization : topology::transition_state::tablet_split_finalization;
};
std::vector<canonical_mutation> updates;
updates.emplace_back(
topology_mutation_builder(guard.write_timestamp())
.set_transition_state(topology::transition_state::tablet_resize_finalization)
.set_transition_state(resize_finalization_transition_state())
.set_version(_topo_sm._topology.version + 1)
.build());

View File

@@ -148,6 +148,7 @@ static std::unordered_map<topology::transition_state, sstring> transition_state_
{topology::transition_state::write_both_read_old, "write both read old"},
{topology::transition_state::write_both_read_new, "write both read new"},
{topology::transition_state::tablet_migration, "tablet migration"},
{topology::transition_state::tablet_split_finalization, "tablet split finalization"},
{topology::transition_state::tablet_resize_finalization, "tablet resize finalization"},
{topology::transition_state::tablet_draining, "tablet draining"},
{topology::transition_state::left_token_ring, "left token ring"},
@@ -156,21 +157,12 @@ static std::unordered_map<topology::transition_state, sstring> transition_state_
{topology::transition_state::lock, "lock"},
};
// Allows old deprecated names to be recognized and point to the correct transition.
static std::unordered_map<sstring, topology::transition_state> deprecated_name_to_transition_state = {
{"tablet split finalization", topology::transition_state::tablet_resize_finalization},
};
topology::transition_state transition_state_from_string(const sstring& s) {
for (auto&& e : transition_state_to_name_map) {
if (e.second == s) {
return e.first;
}
}
auto it = deprecated_name_to_transition_state.find(s);
if (it != deprecated_name_to_transition_state.end()) {
return it->second;
}
on_internal_error(tsmlogger, format("cannot map name {} to transition_state", s));
}

View File

@@ -117,6 +117,7 @@ struct topology {
write_both_read_new,
tablet_migration,
tablet_resize_finalization,
tablet_split_finalization, // deprecated in favor of tablet_resize_finalization, kept for backward compatibility.
left_token_ring,
rollback_to_normal,
truncate_table,

View File

@@ -3490,7 +3490,7 @@ SEASTAR_TEST_CASE(test_explicit_tablets_disable) {
SEASTAR_TEST_CASE(test_recognition_of_deprecated_name_for_resize_transition) {
using transition_state = service::topology::transition_state;
BOOST_REQUIRE_EQUAL(service::transition_state_from_string("tablet split finalization"), transition_state::tablet_resize_finalization);
BOOST_REQUIRE_EQUAL(service::transition_state_from_string("tablet split finalization"), transition_state::tablet_split_finalization);
BOOST_REQUIRE_EQUAL(service::transition_state_from_string("tablet resize finalization"), transition_state::tablet_resize_finalization);
return make_ready_future<>();
}