gms: feature_service: re-enable features on node startup

Re-enable previously persisted enabled features on node
startup. The features list to be enabled is read from
`system.local#enabled_features`.

In case an unknown feature is encountered, the node
fails to boot with an exception, because that means
the node is doing a prohibited downgrade procedure.

Features should be enabled before commitlog starts replaying
since some features affect storage (for example, when
determining used sstable format).

This patch implements a part of solution proposed by Tomek
in https://github.com/scylladb/scylla/issues/4458.

Tests: unit(dev)

Signed-off-by: Pavel Solodovnikov <pa.solodovnikov@scylladb.com>
This commit is contained in:
Pavel Solodovnikov
2021-11-01 10:16:57 +03:00
committed by Avi Kivity
parent 777985b64d
commit 1365e2f13e
3 changed files with 42 additions and 0 deletions

View File

@@ -2932,6 +2932,41 @@ future<> system_keyspace::delete_paxos_decision(const schema& s, const partition
).discard_result();
}
future<> system_keyspace::enable_features_on_startup(sharded<gms::feature_service>& feat) {
auto pre_enabled_features = co_await get_scylla_local_param(gms::feature_service::ENABLED_FEATURES_KEY);
if (!pre_enabled_features) {
co_return;
}
gms::feature_service& local_feat_srv = feat.local();
const auto known_features = local_feat_srv.known_feature_set();
const auto& registered_features = local_feat_srv.registered_features();
const auto persisted_features = gms::feature_service::to_feature_set(*pre_enabled_features);
for (auto&& f : persisted_features) {
slogger.debug("Enabling persisted feature '{}'", f);
const bool is_registered_feat = registered_features.contains(sstring(f));
if (!is_registered_feat || !known_features.contains(f)) {
if (is_registered_feat) {
throw std::runtime_error(format(
"Feature '{}' was previously enabled in the cluster but its support is disabled by this node. "
"Set the corresponding configuration option to enable the support for the feature.", f));
} else {
throw std::runtime_error(format("Unknown feature '{}' was previously enabled in the cluster. "
" That means this node is performing a prohibited downgrade procedure"
" and should not be allowed to boot.", f));
}
}
if (is_registered_feat) {
// `gms::feature::enable` should be run within a seastar thread context
co_await seastar::async([&local_feat_srv, f] {
local_feat_srv.enable(sstring(f));
});
}
// If a feature is not in `registered_features` but still in `known_features` list
// that means the feature name is used for backward compatibility and should be implicitly
// enabled in the code by default, so just skip it.
}
}
sstring system_keyspace_name() {
return system_keyspace::NAME;
}

View File

@@ -415,6 +415,7 @@ public:
static future<bool> cdc_is_rewritten();
static future<> cdc_set_rewritten(std::optional<cdc::generation_id_v1>);
static future<> enable_features_on_startup(sharded<gms::feature_service>& feat);
}; // class system_keyspace
future<> system_keyspace_make(distributed<database>& db, distributed<service::storage_service>& ss);

View File

@@ -967,6 +967,12 @@ int main(int ac, char** av) {
supervisor::notify("setting up system keyspace");
db::system_keyspace::setup(db, qp, feature_service, messaging).get();
// Re-enable previously enabled features on node startup.
// This should be done before commitlog starts replaying
// since some features affect storage.
db::system_keyspace::enable_features_on_startup(feature_service).get();
supervisor::notify("starting commit log");
auto cl = db.local().commitlog();
if (cl != nullptr) {