Files
scylladb/gms/feature_service.cc
Piotr Sarna 3272b4826f db: add keyspace-storage-options experimental feature
Specifying non-standard keyspace options is experimental, so it's
going to be protected by a configuration flag.
2022-04-08 09:17:01 +02:00

366 lines
16 KiB
C++

/*
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* Copyright (C) 2020-present ScyllaDB
*/
#include <any>
#include <seastar/core/sstring.hh>
#include <seastar/core/seastar.hh>
#include <seastar/core/smp.hh>
#include "log.hh"
#include "db/config.hh"
#include "gms/feature.hh"
#include "gms/feature_service.hh"
#include "db/system_keyspace.hh"
#include "db/query_context.hh"
#include "to_string.hh"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
namespace gms {
// Deprecated features - sent to other nodes via gossip, but assumed true in the code
constexpr std::string_view features::RANGE_TOMBSTONES = "RANGE_TOMBSTONES";
constexpr std::string_view features::LARGE_PARTITIONS = "LARGE_PARTITIONS";
constexpr std::string_view features::MATERIALIZED_VIEWS = "MATERIALIZED_VIEWS";
constexpr std::string_view features::COUNTERS = "COUNTERS";
constexpr std::string_view features::INDEXES = "INDEXES";
constexpr std::string_view features::DIGEST_MULTIPARTITION_READ = "DIGEST_MULTIPARTITION_READ";
constexpr std::string_view features::CORRECT_COUNTER_ORDER = "CORRECT_COUNTER_ORDER";
constexpr std::string_view features::SCHEMA_TABLES_V3 = "SCHEMA_TABLES_V3";
constexpr std::string_view features::CORRECT_NON_COMPOUND_RANGE_TOMBSTONES = "CORRECT_NON_COMPOUND_RANGE_TOMBSTONES";
constexpr std::string_view features::WRITE_FAILURE_REPLY = "WRITE_FAILURE_REPLY";
constexpr std::string_view features::XXHASH = "XXHASH";
constexpr std::string_view features::ROLES = "ROLES";
constexpr std::string_view features::LA_SSTABLE = "LA_SSTABLE_FORMAT";
constexpr std::string_view features::STREAM_WITH_RPC_STREAM = "STREAM_WITH_RPC_STREAM";
constexpr std::string_view features::ROW_LEVEL_REPAIR = "ROW_LEVEL_REPAIR";
constexpr std::string_view features::TRUNCATION_TABLE = "TRUNCATION_TABLE";
constexpr std::string_view features::CORRECT_STATIC_COMPACT_IN_MC = "CORRECT_STATIC_COMPACT_IN_MC";
constexpr std::string_view features::UNBOUNDED_RANGE_TOMBSTONES = "UNBOUNDED_RANGE_TOMBSTONES";
constexpr std::string_view features::MC_SSTABLE = "MC_SSTABLE_FORMAT";
// Up-to-date features
constexpr std::string_view features::UDF = "UDF";
constexpr std::string_view features::MD_SSTABLE = "MD_SSTABLE_FORMAT";
constexpr std::string_view features::ME_SSTABLE = "ME_SSTABLE_FORMAT";
constexpr std::string_view features::VIEW_VIRTUAL_COLUMNS = "VIEW_VIRTUAL_COLUMNS";
constexpr std::string_view features::DIGEST_INSENSITIVE_TO_EXPIRY = "DIGEST_INSENSITIVE_TO_EXPIRY";
constexpr std::string_view features::COMPUTED_COLUMNS = "COMPUTED_COLUMNS";
constexpr std::string_view features::CDC = "CDC";
constexpr std::string_view features::NONFROZEN_UDTS = "NONFROZEN_UDTS";
constexpr std::string_view features::HINTED_HANDOFF_SEPARATE_CONNECTION = "HINTED_HANDOFF_SEPARATE_CONNECTION";
constexpr std::string_view features::LWT = "LWT";
constexpr std::string_view features::PER_TABLE_PARTITIONERS = "PER_TABLE_PARTITIONERS";
constexpr std::string_view features::PER_TABLE_CACHING = "PER_TABLE_CACHING";
constexpr std::string_view features::DIGEST_FOR_NULL_VALUES = "DIGEST_FOR_NULL_VALUES";
constexpr std::string_view features::CORRECT_IDX_TOKEN_IN_SECONDARY_INDEX = "CORRECT_IDX_TOKEN_IN_SECONDARY_INDEX";
constexpr std::string_view features::ALTERNATOR_STREAMS = "ALTERNATOR_STREAMS";
constexpr std::string_view features::ALTERNATOR_TTL = "ALTERNATOR_TTL";
constexpr std::string_view features::RANGE_SCAN_DATA_VARIANT = "RANGE_SCAN_DATA_VARIANT";
constexpr std::string_view features::CDC_GENERATIONS_V2 = "CDC_GENERATIONS_V2";
constexpr std::string_view features::UDA = "UDA";
constexpr std::string_view features::SEPARATE_PAGE_SIZE_AND_SAFETY_LIMIT = "SEPARATE_PAGE_SIZE_AND_SAFETY_LIMIT";
constexpr std::string_view features::SUPPORTS_RAFT_CLUSTER_MANAGEMENT = "SUPPORTS_RAFT_CLUSTER_MANAGEMENT";
constexpr std::string_view features::USES_RAFT_CLUSTER_MANAGEMENT = "USES_RAFT_CLUSTER_MANAGEMENT";
constexpr std::string_view features::TOMBSTONE_GC_OPTIONS = "TOMBSTONE_GC_OPTIONS";
constexpr std::string_view features::PARALLELIZED_AGGREGATION = "PARALLELIZED_AGGREGATION";
constexpr std::string_view features::KEYSPACE_STORAGE_OPTIONS = "KEYSPACE_STORAGE_OPTIONS";
static logging::logger logger("features");
feature_config::feature_config() {
}
feature_service::feature_service(feature_config cfg) : _config(cfg)
, _udf_feature(*this, features::UDF)
, _md_sstable_feature(*this, features::MD_SSTABLE)
, _me_sstable_feature(*this, features::ME_SSTABLE)
, _view_virtual_columns(*this, features::VIEW_VIRTUAL_COLUMNS)
, _digest_insensitive_to_expiry(*this, features::DIGEST_INSENSITIVE_TO_EXPIRY)
, _computed_columns(*this, features::COMPUTED_COLUMNS)
, _cdc_feature(*this, features::CDC)
, _nonfrozen_udts(*this, features::NONFROZEN_UDTS)
, _hinted_handoff_separate_connection(*this, features::HINTED_HANDOFF_SEPARATE_CONNECTION)
, _lwt_feature(*this, features::LWT)
, _per_table_partitioners_feature(*this, features::PER_TABLE_PARTITIONERS)
, _per_table_caching_feature(*this, features::PER_TABLE_CACHING)
, _digest_for_null_values_feature(*this, features::DIGEST_FOR_NULL_VALUES)
, _correct_idx_token_in_secondary_index_feature(*this, features::CORRECT_IDX_TOKEN_IN_SECONDARY_INDEX)
, _alternator_streams_feature(*this, features::ALTERNATOR_STREAMS)
, _alternator_ttl_feature(*this, features::ALTERNATOR_TTL)
, _range_scan_data_variant(*this, features::RANGE_SCAN_DATA_VARIANT)
, _cdc_generations_v2(*this, features::CDC_GENERATIONS_V2)
, _uda(*this, features::UDA)
, _separate_page_size_and_safety_limit(*this, features::SEPARATE_PAGE_SIZE_AND_SAFETY_LIMIT)
, _supports_raft_cluster_mgmt(*this, features::SUPPORTS_RAFT_CLUSTER_MANAGEMENT)
, _uses_raft_cluster_mgmt(*this, features::USES_RAFT_CLUSTER_MANAGEMENT)
, _tombstone_gc_options(*this, features::TOMBSTONE_GC_OPTIONS)
, _parallelized_aggregation(*this, features::PARALLELIZED_AGGREGATION)
, _keyspace_storage_options(*this, features::KEYSPACE_STORAGE_OPTIONS)
{}
feature_config feature_config_from_db_config(db::config& cfg, std::set<sstring> disabled) {
feature_config fcfg;
fcfg._disabled_features = std::move(disabled);
switch (sstables::from_string(cfg.sstable_format())) {
case sstables::sstable_version_types::ka:
case sstables::sstable_version_types::la:
case sstables::sstable_version_types::mc:
fcfg._disabled_features.insert(sstring(gms::features::MD_SSTABLE));
[[fallthrough]];
case sstables::sstable_version_types::md:
fcfg._disabled_features.insert(sstring(gms::features::ME_SSTABLE));
[[fallthrough]];
case sstables::sstable_version_types::me:
break;
}
if (!cfg.enable_user_defined_functions()) {
fcfg._disabled_features.insert(sstring(gms::features::UDF));
} else {
if (!cfg.check_experimental(db::experimental_features_t::UDF)) {
throw std::runtime_error(
"You must use both enable_user_defined_functions and experimental_features:udf "
"to enable user-defined functions");
}
}
if (!cfg.check_experimental(db::experimental_features_t::ALTERNATOR_STREAMS)) {
fcfg._disabled_features.insert(sstring(gms::features::ALTERNATOR_STREAMS));
}
if (!cfg.check_experimental(db::experimental_features_t::ALTERNATOR_TTL)) {
fcfg._disabled_features.insert(sstring(gms::features::ALTERNATOR_TTL));
}
if (!cfg.check_experimental(db::experimental_features_t::RAFT)) {
fcfg._disabled_features.insert(sstring(gms::features::SUPPORTS_RAFT_CLUSTER_MANAGEMENT));
fcfg._disabled_features.insert(sstring(gms::features::USES_RAFT_CLUSTER_MANAGEMENT));
} else {
// Disable support for using raft cluster management so that it cannot
// be enabled by accident.
// This prevents the `USES_RAFT_CLUSTER_MANAGEMENT` feature from being
// advertised via gossip ahead of time.
fcfg._masked_features.insert(sstring(gms::features::USES_RAFT_CLUSTER_MANAGEMENT));
}
if (!cfg.check_experimental(db::experimental_features_t::KEYSPACE_STORAGE_OPTIONS)) {
fcfg._disabled_features.insert(sstring(gms::features::KEYSPACE_STORAGE_OPTIONS));
}
return fcfg;
}
future<> feature_service::stop() {
return make_ready_future<>();
}
void feature_service::register_feature(feature& f) {
auto i = _registered_features.emplace(f.name(), f);
assert(i.second);
}
void feature_service::unregister_feature(feature& f) {
_registered_features.erase(f.name());
}
void feature_service::enable(const sstring& name) {
if (auto it = _registered_features.find(name); it != _registered_features.end()) {
auto&& f = it->second;
auto& f_ref = f.get();
if (db::qctx && !f_ref) {
persist_enabled_feature_info(f_ref);
}
f_ref.enable();
}
}
future<> feature_service::support(const std::string_view& name) {
_config._masked_features.erase(sstring(name));
if (db::qctx) {
// Update `system.local#supported_features` accordingly
co_await db::system_keyspace::save_local_supported_features(supported_feature_set());
}
}
std::set<std::string_view> feature_service::known_feature_set() {
// Add features known by this local node. When a new feature is
// introduced in scylla, update it here, e.g.,
// return sstring("FEATURE1,FEATURE2")
std::set<std::string_view> features = {
// Deprecated features - sent to other nodes via gossip, but assumed true in the code
gms::features::RANGE_TOMBSTONES,
gms::features::LARGE_PARTITIONS,
gms::features::COUNTERS,
gms::features::DIGEST_MULTIPARTITION_READ,
gms::features::CORRECT_COUNTER_ORDER,
gms::features::SCHEMA_TABLES_V3,
gms::features::CORRECT_NON_COMPOUND_RANGE_TOMBSTONES,
gms::features::WRITE_FAILURE_REPLY,
gms::features::XXHASH,
gms::features::ROLES,
gms::features::LA_SSTABLE,
gms::features::STREAM_WITH_RPC_STREAM,
gms::features::MATERIALIZED_VIEWS,
gms::features::INDEXES,
gms::features::ROW_LEVEL_REPAIR,
gms::features::TRUNCATION_TABLE,
gms::features::CORRECT_STATIC_COMPACT_IN_MC,
gms::features::UNBOUNDED_RANGE_TOMBSTONES,
gms::features::MC_SSTABLE,
// Up-to-date features
gms::features::VIEW_VIRTUAL_COLUMNS,
gms::features::DIGEST_INSENSITIVE_TO_EXPIRY,
gms::features::COMPUTED_COLUMNS,
gms::features::NONFROZEN_UDTS,
gms::features::HINTED_HANDOFF_SEPARATE_CONNECTION,
gms::features::PER_TABLE_PARTITIONERS,
gms::features::PER_TABLE_CACHING,
gms::features::LWT,
gms::features::MD_SSTABLE,
gms::features::ME_SSTABLE,
gms::features::UDF,
gms::features::CDC,
gms::features::DIGEST_FOR_NULL_VALUES,
gms::features::CORRECT_IDX_TOKEN_IN_SECONDARY_INDEX,
gms::features::ALTERNATOR_STREAMS,
gms::features::ALTERNATOR_TTL,
gms::features::RANGE_SCAN_DATA_VARIANT,
gms::features::CDC_GENERATIONS_V2,
gms::features::UDA,
gms::features::SEPARATE_PAGE_SIZE_AND_SAFETY_LIMIT,
gms::features::SUPPORTS_RAFT_CLUSTER_MANAGEMENT,
gms::features::USES_RAFT_CLUSTER_MANAGEMENT,
gms::features::TOMBSTONE_GC_OPTIONS,
gms::features::PARALLELIZED_AGGREGATION,
gms::features::KEYSPACE_STORAGE_OPTIONS,
};
for (const sstring& s : _config._disabled_features) {
features.erase(s);
}
return features;
}
const std::unordered_map<sstring, std::reference_wrapper<feature>>& feature_service::registered_features() const {
return _registered_features;
}
std::set<std::string_view> feature_service::supported_feature_set() {
auto features = known_feature_set();
for (const sstring& s : _config._masked_features) {
features.erase(s);
}
return features;
}
feature::feature(feature_service& service, std::string_view name, bool enabled)
: _service(&service)
, _name(name)
, _enabled(enabled) {
_service->register_feature(*this);
}
feature::~feature() {
if (_service) {
_service->unregister_feature(*this);
}
}
feature& feature::operator=(feature&& other) {
_service->unregister_feature(*this);
_service = std::exchange(other._service, nullptr);
_name = other._name;
_enabled = other._enabled;
_s = std::move(other._s);
_service->register_feature(*this);
return *this;
}
void feature::enable() {
if (!_enabled) {
if (this_shard_id() == 0) {
logger.info("Feature {} is enabled", name());
}
_enabled = true;
_s();
}
}
db::schema_features feature_service::cluster_schema_features() const {
db::schema_features f;
f.set_if<db::schema_feature::VIEW_VIRTUAL_COLUMNS>(bool(_view_virtual_columns));
f.set_if<db::schema_feature::DIGEST_INSENSITIVE_TO_EXPIRY>(bool(_digest_insensitive_to_expiry));
f.set_if<db::schema_feature::COMPUTED_COLUMNS>(bool(_computed_columns));
f.set_if<db::schema_feature::CDC_OPTIONS>(bool(_cdc_feature));
f.set_if<db::schema_feature::PER_TABLE_PARTITIONERS>(bool(_per_table_partitioners_feature));
f.set_if<db::schema_feature::SCYLLA_KEYSPACES>(bool(_keyspace_storage_options));
return f;
}
std::set<sstring> feature_service::to_feature_set(sstring features_string) {
std::set<sstring> features;
boost::split(features, features_string, boost::is_any_of(","));
features.erase("");
return features;
}
void feature_service::persist_enabled_feature_info(const gms::feature& f) const {
// Executed in seastar::async context, because `gms::feature::enable`
// is only allowed to run within a thread context
std::optional<sstring> raw_old_value = db::system_keyspace::get_scylla_local_param(ENABLED_FEATURES_KEY).get0();
if (!raw_old_value) {
db::system_keyspace::set_scylla_local_param(ENABLED_FEATURES_KEY, f.name()).get0();
return;
}
auto feats_set = to_feature_set(*raw_old_value);
feats_set.emplace(f.name());
db::system_keyspace::set_scylla_local_param(ENABLED_FEATURES_KEY, ::join(",", feats_set)).get0();
}
void feature_service::enable(const std::set<std::string_view>& list) {
for (gms::feature& f : {
std::ref(_udf_feature),
std::ref(_md_sstable_feature),
std::ref(_me_sstable_feature),
std::ref(_view_virtual_columns),
std::ref(_digest_insensitive_to_expiry),
std::ref(_computed_columns),
std::ref(_cdc_feature),
std::ref(_nonfrozen_udts),
std::ref(_hinted_handoff_separate_connection),
std::ref(_lwt_feature),
std::ref(_per_table_partitioners_feature),
std::ref(_per_table_caching_feature),
std::ref(_digest_for_null_values_feature),
std::ref(_correct_idx_token_in_secondary_index_feature),
std::ref(_alternator_streams_feature),
std::ref(_alternator_ttl_feature),
std::ref(_range_scan_data_variant),
std::ref(_cdc_generations_v2),
std::ref(_uda),
std::ref(_separate_page_size_and_safety_limit),
std::ref(_supports_raft_cluster_mgmt),
std::ref(_uses_raft_cluster_mgmt),
std::ref(_tombstone_gc_options),
std::ref(_parallelized_aggregation),
std::ref(_keyspace_storage_options),
})
{
if (list.contains(f.name())) {
if (db::qctx && !f) {
persist_enabled_feature_info(f);
}
f.enable();
}
}
}
} // namespace gms