The STORAGE option is designed to hold a map of options
used for customizing storage for given keyspace.
The option is kept in a system_schema.scylla_keyspaces table.
The option is only available if the whole cluster is aware
of it - guarded by a cluster feature.
Example of the table contents:
```
cassandra@cqlsh> select * from system_schema.scylla_keyspaces;
keyspace_name | storage_options | storage_type
---------------+------------------------------------------------+--------------
ksx | {'bucket': '/tmp/xx', 'endpoint': 'localhost'} | S3
```
378 lines
11 KiB
C++
378 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2021-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
#include "data_dictionary.hh"
|
|
#include "impl.hh"
|
|
#include "user_types_metadata.hh"
|
|
#include "keyspace_metadata.hh"
|
|
#include "schema.hh"
|
|
#include "utils/overloaded_functor.hh"
|
|
#include <fmt/core.h>
|
|
#include <ostream>
|
|
#include <boost/range/adaptor/map.hpp>
|
|
#include <boost/range/adaptor/filtered.hpp>
|
|
#include <boost/algorithm/string/join.hpp>
|
|
#include <array>
|
|
|
|
namespace data_dictionary {
|
|
|
|
schema_ptr
|
|
table::schema() const {
|
|
return _ops->get_table_schema(*this);
|
|
}
|
|
|
|
const std::vector<view_ptr>&
|
|
table::views() const {
|
|
return _ops->get_table_views(*this);
|
|
}
|
|
|
|
const secondary_index::secondary_index_manager&
|
|
table::get_index_manager() const {
|
|
return _ops->get_index_manager(*this);
|
|
}
|
|
|
|
lw_shared_ptr<keyspace_metadata>
|
|
keyspace::metadata() const {
|
|
return _ops->get_keyspace_metadata(*this);
|
|
}
|
|
|
|
const user_types_metadata&
|
|
keyspace::user_types() const {
|
|
return metadata()->user_types();
|
|
}
|
|
|
|
bool
|
|
keyspace::is_internal() const {
|
|
return _ops->is_internal(*this);
|
|
}
|
|
|
|
const locator::abstract_replication_strategy&
|
|
keyspace::get_replication_strategy() const {
|
|
return _ops->get_replication_strategy(*this);
|
|
}
|
|
|
|
std::optional<keyspace>
|
|
database::try_find_keyspace(std::string_view name) const {
|
|
return _ops->try_find_keyspace(*this, name);
|
|
}
|
|
|
|
bool
|
|
database::has_keyspace(std::string_view name) const {
|
|
return bool(try_find_keyspace(name));
|
|
}
|
|
|
|
keyspace
|
|
database::find_keyspace(std::string_view name) const {
|
|
auto ks = try_find_keyspace(name);
|
|
if (!ks) {
|
|
throw no_such_keyspace(name);
|
|
}
|
|
return *ks;
|
|
}
|
|
|
|
std::vector<keyspace>
|
|
database::get_keyspaces() const {
|
|
return _ops->get_keyspaces(*this);
|
|
}
|
|
|
|
std::vector<table>
|
|
database::get_tables() const {
|
|
return _ops->get_tables(*this);
|
|
}
|
|
|
|
std::optional<table>
|
|
database::try_find_table(std::string_view ks, std::string_view table) const {
|
|
return _ops->try_find_table(*this, ks, table);
|
|
}
|
|
|
|
table
|
|
database::find_table(std::string_view ks, std::string_view table) const {
|
|
auto t = try_find_table(ks, table);
|
|
if (!t) {
|
|
throw no_such_column_family(ks, table);
|
|
}
|
|
return *t;
|
|
}
|
|
|
|
std::optional<table>
|
|
database::try_find_table(utils::UUID id) const {
|
|
return _ops->try_find_table(*this, id);
|
|
}
|
|
|
|
table
|
|
database::find_column_family(utils::UUID uuid) const {
|
|
auto t = try_find_table(uuid);
|
|
if (!t) {
|
|
throw no_such_column_family(uuid);
|
|
}
|
|
return *t;
|
|
}
|
|
|
|
schema_ptr
|
|
database::find_schema(std::string_view ks, std::string_view table) const {
|
|
return find_table(ks, table).schema();
|
|
}
|
|
|
|
schema_ptr
|
|
database::find_schema(utils::UUID uuid) const {
|
|
return find_column_family(uuid).schema();
|
|
}
|
|
|
|
bool
|
|
database::has_schema(std::string_view ks_name, std::string_view cf_name) const {
|
|
return bool(try_find_table(ks_name, cf_name));
|
|
}
|
|
|
|
table
|
|
database::find_column_family(schema_ptr s) const {
|
|
return find_column_family(s->id());
|
|
}
|
|
|
|
schema_ptr
|
|
database::find_indexed_table(std::string_view ks_name, std::string_view index_name) const {
|
|
return _ops->find_indexed_table(*this, ks_name, index_name);
|
|
}
|
|
|
|
sstring
|
|
database::get_available_index_name(std::string_view ks_name, std::string_view table_name,
|
|
std::optional<sstring> index_name_root) const {
|
|
return _ops->get_available_index_name(*this, ks_name, table_name, index_name_root);
|
|
}
|
|
|
|
std::set<sstring>
|
|
database::existing_index_names(std::string_view ks_name, std::string_view cf_to_exclude) const {
|
|
return _ops->existing_index_names(*this, ks_name, cf_to_exclude);
|
|
}
|
|
|
|
schema_ptr
|
|
database::get_cdc_base_table(sstring_view ks_name, std::string_view table_name) const {
|
|
return get_cdc_base_table(*find_table(ks_name, table_name).schema());
|
|
}
|
|
|
|
schema_ptr
|
|
database::get_cdc_base_table(const schema& s) const {
|
|
return _ops->get_cdc_base_table(*this, s);
|
|
}
|
|
|
|
const db::extensions&
|
|
database::extensions() const {
|
|
return _ops->get_extensions(*this);
|
|
}
|
|
|
|
const gms::feature_service&
|
|
database::features() const {
|
|
return _ops->get_features(*this);
|
|
}
|
|
|
|
const db::config&
|
|
database::get_config() const {
|
|
return _ops->get_config(*this);
|
|
}
|
|
|
|
replica::database&
|
|
database::real_database() const {
|
|
return _ops->real_database(*this);
|
|
}
|
|
|
|
impl::~impl() = default;
|
|
|
|
keyspace_metadata::keyspace_metadata(std::string_view name,
|
|
std::string_view strategy_name,
|
|
locator::replication_strategy_config_options strategy_options,
|
|
bool durable_writes,
|
|
std::vector<schema_ptr> cf_defs)
|
|
: keyspace_metadata(name,
|
|
strategy_name,
|
|
std::move(strategy_options),
|
|
durable_writes,
|
|
std::move(cf_defs),
|
|
user_types_metadata{}) { }
|
|
|
|
keyspace_metadata::keyspace_metadata(std::string_view name,
|
|
std::string_view strategy_name,
|
|
locator::replication_strategy_config_options strategy_options,
|
|
bool durable_writes,
|
|
std::vector<schema_ptr> cf_defs,
|
|
user_types_metadata user_types)
|
|
: keyspace_metadata(name,
|
|
strategy_name,
|
|
std::move(strategy_options),
|
|
durable_writes,
|
|
std::move(cf_defs),
|
|
user_types_metadata{},
|
|
storage_options{}) { }
|
|
|
|
keyspace_metadata::keyspace_metadata(std::string_view name,
|
|
std::string_view strategy_name,
|
|
locator::replication_strategy_config_options strategy_options,
|
|
bool durable_writes,
|
|
std::vector<schema_ptr> cf_defs,
|
|
user_types_metadata user_types,
|
|
storage_options storage_opts)
|
|
: _name{name}
|
|
, _strategy_name{locator::abstract_replication_strategy::to_qualified_class_name(strategy_name.empty() ? "NetworkTopologyStrategy" : strategy_name)}
|
|
, _strategy_options{std::move(strategy_options)}
|
|
, _durable_writes{durable_writes}
|
|
, _user_types{std::move(user_types)}
|
|
, _storage_options{std::move(storage_opts)}
|
|
{
|
|
for (auto&& s : cf_defs) {
|
|
_cf_meta_data.emplace(s->cf_name(), s);
|
|
}
|
|
}
|
|
|
|
void keyspace_metadata::validate(const locator::topology& topology) const {
|
|
using namespace locator;
|
|
abstract_replication_strategy::validate_replication_strategy(name(), strategy_name(), strategy_options(), topology);
|
|
}
|
|
|
|
lw_shared_ptr<keyspace_metadata>
|
|
keyspace_metadata::new_keyspace(std::string_view name,
|
|
std::string_view strategy_name,
|
|
locator::replication_strategy_config_options options,
|
|
bool durables_writes,
|
|
std::vector<schema_ptr> cf_defs,
|
|
storage_options storage_opts)
|
|
{
|
|
return ::make_lw_shared<keyspace_metadata>(name, strategy_name, options, durables_writes, cf_defs, user_types_metadata{}, storage_opts);
|
|
}
|
|
|
|
void keyspace_metadata::add_user_type(const user_type ut) {
|
|
_user_types.add_type(ut);
|
|
}
|
|
|
|
void keyspace_metadata::remove_user_type(const user_type ut) {
|
|
_user_types.remove_type(ut);
|
|
}
|
|
|
|
std::vector<schema_ptr> keyspace_metadata::tables() const {
|
|
return boost::copy_range<std::vector<schema_ptr>>(_cf_meta_data
|
|
| boost::adaptors::map_values
|
|
| boost::adaptors::filtered([] (auto&& s) { return !s->is_view(); }));
|
|
}
|
|
|
|
std::vector<view_ptr> keyspace_metadata::views() const {
|
|
return boost::copy_range<std::vector<view_ptr>>(_cf_meta_data
|
|
| boost::adaptors::map_values
|
|
| boost::adaptors::filtered(std::mem_fn(&schema::is_view))
|
|
| boost::adaptors::transformed([] (auto&& s) { return view_ptr(s); }));
|
|
}
|
|
|
|
storage_options::value_type storage_options::from_map(std::string_view type, std::map<sstring, sstring> values) {
|
|
if (type == "LOCAL") {
|
|
if (!values.empty()) {
|
|
throw std::runtime_error("Local storage does not accept any custom options");
|
|
}
|
|
return local{};
|
|
}
|
|
if (type == "S3") {
|
|
s3 options;
|
|
const std::array<std::pair<sstring, sstring*>, 2> allowed_options {
|
|
std::make_pair("bucket", &options.bucket),
|
|
std::make_pair("endpoint", &options.endpoint),
|
|
};
|
|
for (auto& option : allowed_options) {
|
|
if (auto it = values.find(option.first); it != values.end()) {
|
|
*option.second = it->second;
|
|
} else {
|
|
throw std::runtime_error(format("Missing S3 option: {}", option.first));
|
|
}
|
|
}
|
|
if (values.size() > allowed_options.size()) {
|
|
throw std::runtime_error(format("Extraneous options for S3: {}; allowed: {}",
|
|
boost::algorithm::join(values | boost::adaptors::map_keys, ","),
|
|
boost::algorithm::join(allowed_options | boost::adaptors::map_keys, ",")));
|
|
}
|
|
return options;
|
|
}
|
|
throw std::runtime_error(format("Unknown storage type: {}", type));
|
|
}
|
|
|
|
std::string_view storage_options::type_string() const {
|
|
return std::visit(overloaded_functor {
|
|
[] (const storage_options::local&) {
|
|
return "LOCAL";
|
|
},
|
|
[] (const storage_options::s3&) {
|
|
return "S3";
|
|
}
|
|
}, value);
|
|
}
|
|
|
|
std::map<sstring, sstring> storage_options::to_map() const {
|
|
std::map<sstring, sstring> ret;
|
|
std::visit(overloaded_functor {
|
|
[] (const storage_options::local&) {
|
|
},
|
|
[&ret] (const storage_options::s3& v) {
|
|
ret.emplace("bucket", v.bucket);
|
|
ret.emplace("endpoint", v.endpoint);
|
|
}
|
|
}, value);
|
|
return ret;
|
|
}
|
|
|
|
bool storage_options::can_update_to(const storage_options& new_options) {
|
|
return value == new_options.value;
|
|
}
|
|
|
|
no_such_keyspace::no_such_keyspace(std::string_view ks_name)
|
|
: runtime_error{format("Can't find a keyspace {}", ks_name)}
|
|
{
|
|
}
|
|
|
|
no_such_column_family::no_such_column_family(const utils::UUID& uuid)
|
|
: runtime_error{format("Can't find a column family with UUID {}", uuid)}
|
|
{
|
|
}
|
|
|
|
no_such_column_family::no_such_column_family(std::string_view ks_name, std::string_view cf_name)
|
|
: runtime_error{format("Can't find a column family {} in keyspace {}", cf_name, ks_name)}
|
|
{
|
|
}
|
|
|
|
no_such_column_family::no_such_column_family(std::string_view ks_name, const utils::UUID& uuid)
|
|
: runtime_error{format("Can't find a column family with UUID {} in keyspace {}", uuid, ks_name)}
|
|
{
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const keyspace_metadata& m) {
|
|
os << "KSMetaData{";
|
|
os << "name=" << m._name;
|
|
os << ", strategyClass=" << m._strategy_name;
|
|
os << ", strategyOptions={";
|
|
int n = 0;
|
|
for (auto& p : m._strategy_options) {
|
|
if (n++ != 0) {
|
|
os << ", ";
|
|
}
|
|
os << p.first << "=" << p.second;
|
|
}
|
|
os << "}";
|
|
os << ", cfMetaData={";
|
|
n = 0;
|
|
for (auto& p : m._cf_meta_data) {
|
|
if (n++ != 0) {
|
|
os << ", ";
|
|
}
|
|
os << p.first << "=" << p.second;
|
|
}
|
|
os << "}";
|
|
os << ", durable_writes=" << m._durable_writes;
|
|
os << ", userTypes=" << m._user_types;
|
|
os << "}";
|
|
return os;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const user_types_metadata& m) {
|
|
os << "org.apache.cassandra.config.UTMetaData@" << &m;
|
|
return os;
|
|
}
|
|
|
|
}
|