mirror of
https://github.com/scylladb/scylladb.git
synced 2026-05-02 06:05:53 +00:00
before this change, we create a new UUID for a new sstable managed by the s3_storage, and we use the string representation of UUID defined by RFC4122 like "0aa490de-7a85-46e2-8f90-38b8f496d53b" for naming the objects stored on s3_storage. but this representation is not what we are using for storing sstables on local filesystem when the option of "uuid_sstable_identifiers_enabled" is enabled. instead, we are using a base36-based representation which is shorter. to be consistent with the naming of the sstables created for local filesystem, and more importantly, to simplify the interaction between the local copy of sstables and those stored on object storage, we should use the same string representation of the sstable identifier. so, in this change: 1. instead of creating a new UUID, just reuse the generation of the sstable for the object's key. 2. do not store the uuid in the sstable_registry system table. As we already have the generation of the sstable for the same purpose. 3. switch the sstable identifier representation from the one defined by the RFC4122 (implemented by fmt::formatter<utils::UUID>) to the base36-based one (implemented by fmt::formatter<sstables::generation_type>) Fixes #14175 Signed-off-by: Kefu Chai <kefu.chai@scylladb.com> Closes scylladb/scylladb#14406 * github.com:scylladb/scylladb: sstable: remove _remote_prefix from s3_storage sstable: switch to uuid identifier for naming S3 sstable objects
210 lines
8.7 KiB
C++
210 lines
8.7 KiB
C++
#include <boost/test/unit_test.hpp>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include <seastar/core/sstring.hh>
|
|
#include <seastar/core/future-util.hh>
|
|
#include <seastar/core/do_with.hh>
|
|
#include <seastar/core/distributed.hh>
|
|
#include "sstables/sstables.hh"
|
|
#include "test/lib/scylla_test_case.hh"
|
|
#include "schema/schema.hh"
|
|
#include "replica/database.hh"
|
|
#include "compaction/compaction_manager.hh"
|
|
#include "test/boost/sstable_test.hh"
|
|
#include "test/lib/flat_mutation_reader_assertions.hh"
|
|
#include "test/lib/key_utils.hh"
|
|
#include "test/lib/sstable_utils.hh"
|
|
#include "test/lib/test_services.hh"
|
|
#include "test/lib/test_utils.hh"
|
|
#include "db/config.hh"
|
|
|
|
using namespace sstables;
|
|
|
|
static schema_builder get_schema_builder() {
|
|
return schema_builder("tests", "sstable_resharding_test")
|
|
.with_column("id", utf8_type, column_kind::partition_key)
|
|
.with_column("value", int32_type);
|
|
}
|
|
|
|
static schema_ptr get_schema() {
|
|
return get_schema_builder().build();
|
|
}
|
|
|
|
static schema_ptr get_schema(unsigned shard_count, unsigned sharding_ignore_msb_bits) {
|
|
return get_schema_builder().with_sharder(shard_count, sharding_ignore_msb_bits).build();
|
|
}
|
|
|
|
// Asserts that sstable::compute_owner_shards(...) produces correct results.
|
|
static future<> assert_sstable_computes_correct_owners(test_env& env, const sstables::shared_sstable& base_sst) {
|
|
auto sst = co_await env.reusable_sst(base_sst);
|
|
co_await sst->load_owner_shards(sst->get_schema()->get_sharder());
|
|
BOOST_REQUIRE_EQUAL(sst->get_shards_for_this_sstable(), base_sst->get_shards_for_this_sstable());
|
|
}
|
|
|
|
// Must be called in a seastar thread.
|
|
void run_sstable_resharding_test(sstables::test_env& env) {
|
|
for (const auto version : writable_sstable_versions) {
|
|
auto s = get_schema();
|
|
auto cf = env.make_table_for_tests(s);
|
|
auto close_cf = deferred_stop(cf);
|
|
auto sst_gen = cf.make_sst_factory(version);
|
|
std::unordered_map<shard_id, std::vector<mutation>> muts;
|
|
static constexpr auto keys_per_shard = 1000u;
|
|
|
|
// create sst shared by all shards
|
|
auto sst = std::invoke([&] {
|
|
auto mt = make_lw_shared<replica::memtable>(s);
|
|
auto get_mutation = [mt, s] (const dht::decorated_key& key, auto value) {
|
|
mutation m(s, key);
|
|
m.set_clustered_cell(clustering_key::make_empty(), bytes("value"), data_value(int32_t(value)), api::timestamp_type(0));
|
|
return m;
|
|
};
|
|
auto cfg = std::make_unique<db::config>();
|
|
for (auto i : boost::irange(0u, smp::count)) {
|
|
const auto keys = tests::generate_partition_keys(keys_per_shard, s, i);
|
|
BOOST_REQUIRE(keys.size() == keys_per_shard);
|
|
muts[i].reserve(keys_per_shard);
|
|
for (auto k : boost::irange(0u, keys_per_shard)) {
|
|
auto m = get_mutation(keys[k], i);
|
|
muts[i].push_back(m);
|
|
mt->apply(std::move(m));
|
|
}
|
|
}
|
|
return make_sstable_containing(cf.make_sstable(version), mt);
|
|
});
|
|
|
|
// FIXME: sstable write has a limitation in which it will generate sharding metadata only
|
|
// for a single shard. workaround that by setting shards manually. from this test perspective,
|
|
// it doesn't matter because we check each partition individually of each sstable created
|
|
// for a shard that owns the shared input sstable.
|
|
sstables::test(sst).set_shards(boost::copy_range<std::vector<unsigned>>(boost::irange(0u, smp::count)));
|
|
|
|
auto filter_size = [&env] (shared_sstable sst) -> uint64_t {
|
|
if (!env.get_storage_options().is_local_type()) {
|
|
// FIXME -- use s3::client::get_object_stats() when it appears
|
|
return 0;
|
|
}
|
|
|
|
auto filter_fname = sstables::test(sst).filename(component_type::Filter);
|
|
return file_size(filter_fname.native()).get0();
|
|
};
|
|
|
|
uint64_t bloom_filter_size_before = filter_size(sst);
|
|
|
|
auto erm = cf->get_effective_replication_map();
|
|
|
|
auto descriptor = sstables::compaction_descriptor({sst}, 0, std::numeric_limits<uint64_t>::max());
|
|
descriptor.options = sstables::compaction_type_options::make_reshard();
|
|
descriptor.sharder = &cf->schema()->get_sharder();
|
|
descriptor.creator = [&env, &cf, version] (shard_id shard) mutable {
|
|
// we need generation calculated by instance of cf at requested shard,
|
|
// or resource usage wouldn't be fairly distributed among shards.
|
|
auto gen = smp::submit_to(shard, [&cf] () {
|
|
return column_family_test::calculate_generation_for_new_table(*cf);
|
|
}).get0();
|
|
|
|
return env.make_sstable(cf->schema(), gen, version);
|
|
};
|
|
auto cdata = compaction_manager::create_compaction_data();
|
|
compaction_progress_monitor progress_monitor;
|
|
auto res = sstables::compact_sstables(std::move(descriptor), cdata, cf.as_table_state(), progress_monitor).get0();
|
|
sst->destroy().get();
|
|
|
|
auto new_sstables = std::move(res.new_sstables);
|
|
BOOST_REQUIRE(new_sstables.size() == smp::count);
|
|
|
|
uint64_t bloom_filter_size_after = 0;
|
|
std::unordered_set<shard_id> processed_shards;
|
|
|
|
for (auto& sstable : new_sstables) {
|
|
auto new_sst = env.reusable_sst(s, sstable->generation(), version).get0();
|
|
bloom_filter_size_after += filter_size(new_sst);
|
|
auto shards = new_sst->get_shards_for_this_sstable();
|
|
BOOST_REQUIRE(shards.size() == 1); // check sstable is unshared.
|
|
auto shard = shards.front();
|
|
BOOST_REQUIRE(processed_shards.insert(shard).second == true); // check resharding created one sstable per shard.
|
|
assert_sstable_computes_correct_owners(env, new_sst).get();
|
|
|
|
auto rd = assert_that(new_sst->as_mutation_source().make_reader_v2(s, env.make_reader_permit()));
|
|
BOOST_REQUIRE(muts[shard].size() == keys_per_shard);
|
|
for (auto k : boost::irange(0u, keys_per_shard)) {
|
|
rd.produces(muts[shard][k]);
|
|
}
|
|
rd.produces_end_of_stream();
|
|
new_sst->destroy().get();
|
|
}
|
|
BOOST_REQUIRE_CLOSE_FRACTION(float(bloom_filter_size_before), float(bloom_filter_size_after), 0.1);
|
|
}
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(sstable_resharding_test) {
|
|
return sstables::test_env::do_with_async([] (auto& env) {
|
|
run_sstable_resharding_test(env);
|
|
});
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(sstable_resharding_over_s3_test, *boost::unit_test::precondition(tests::has_scylla_test_env)) {
|
|
return sstables::test_env::do_with_async([] (auto& env) {
|
|
run_sstable_resharding_test(env);
|
|
}, test_env_config{
|
|
.storage = make_test_object_storage_options(),
|
|
.use_uuid = true,
|
|
});
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(sstable_is_shared_correctness) {
|
|
return test_env::do_with_async([] (test_env& env) {
|
|
for (const auto version : writable_sstable_versions) {
|
|
auto cfg = std::make_unique<db::config>();
|
|
|
|
auto get_mutation = [] (const schema_ptr& s, const dht::decorated_key& key, auto value) {
|
|
mutation m(s, key);
|
|
m.set_clustered_cell(clustering_key::make_empty(), bytes("value"), data_value(int32_t(value)), api::timestamp_type(0));
|
|
return m;
|
|
};
|
|
|
|
// created sstable owned only by this shard
|
|
{
|
|
auto s = get_schema();
|
|
auto sst_gen = env.make_sst_factory(s, version);
|
|
|
|
const auto keys = tests::generate_partition_keys(smp::count * 10, s);
|
|
std::vector<mutation> muts;
|
|
for (auto& k : keys) {
|
|
muts.push_back(get_mutation(s, k, 0));
|
|
}
|
|
|
|
auto sst = make_sstable_containing(sst_gen, muts);
|
|
BOOST_REQUIRE(!sst->is_shared());
|
|
assert_sstable_computes_correct_owners(env, sst).get();
|
|
}
|
|
|
|
// create sstable owned by all shards
|
|
// created unshared sstable
|
|
{
|
|
auto key_s = get_schema();
|
|
auto single_sharded_s = get_schema(1, cfg->murmur3_partitioner_ignore_msb_bits());
|
|
auto sst_gen = env.make_sst_factory(single_sharded_s, version);
|
|
|
|
std::vector<mutation> muts;
|
|
for (shard_id shard : boost::irange(0u, smp::count)) {
|
|
const auto keys = tests::generate_partition_keys(10, key_s, shard);
|
|
for (auto& k : keys) {
|
|
muts.push_back(get_mutation(single_sharded_s, k, shard));
|
|
}
|
|
}
|
|
|
|
auto sst = make_sstable_containing(sst_gen, muts);
|
|
BOOST_REQUIRE(!sst->is_shared());
|
|
|
|
auto all_shards_s = get_schema(smp::count, cfg->murmur3_partitioner_ignore_msb_bits());
|
|
sst = env.reusable_sst(all_shards_s, sst->generation(), version).get0();
|
|
BOOST_REQUIRE(smp::count == 1 || sst->is_shared());
|
|
BOOST_REQUIRE(sst->get_shards_for_this_sstable().size() == smp::count);
|
|
assert_sstable_computes_correct_owners(env, sst).get();
|
|
}
|
|
}
|
|
});
|
|
}
|