mirror of
https://github.com/scylladb/scylladb.git
synced 2026-06-04 14:03:06 +00:00
database: apply auto_snapshot_ttl
When automatically taking a snapshot before a table is truncated or dropped, set snapshot_options::expires_at if auto_snapshot_ttl is set. This is then stored in the snapshot manifest.json file. Add a unit test to verify that the auto_snapshot_ttl is applied in the snapshot manifest.json. Signed-off-by: Benny Halevy <bhalevy@scylladb.com>
This commit is contained in:
@@ -3152,7 +3152,15 @@ future<> database::truncate_table_on_all_shards(sharded<database>& sharded_db, s
|
||||
auto truncated_at = truncated_at_opt.value_or(db_clock::now());
|
||||
auto name = snapshot_name_opt.value_or(
|
||||
format("{:d}-{}", truncated_at.time_since_epoch().count(), cf.schema()->cf_name()));
|
||||
co_await snapshot_table_on_all_shards(sharded_db, table_shards, name, db::snapshot_options{});
|
||||
auto ttl = sharded_db.local().get_config().auto_snapshot_ttl();
|
||||
db::snapshot_options opts;
|
||||
if (ttl) {
|
||||
// Add one second to compensate for created_at being truncated
|
||||
// to second resolution, ensuring the snapshot lives for at least
|
||||
// auto_snapshot_ttl seconds.
|
||||
opts.expires_at = opts.created_at + (ttl + 1) * 1s;
|
||||
}
|
||||
co_await snapshot_table_on_all_shards(sharded_db, table_shards, name, opts);
|
||||
}
|
||||
|
||||
co_await sharded_db.invoke_on_all([&] (database& db) {
|
||||
|
||||
@@ -549,7 +549,7 @@ static std::set<sstring> collect_sstables(const std::set<sstring>& all_files, co
|
||||
}
|
||||
|
||||
// Validate that the manifest.json lists exactly the SSTables present in the snapshot directory
|
||||
static future<> validate_manifest(const locator::topology& topology, const fs::path& snapshot_dir, const std::set<sstring>& in_snapshot_dir, gc_clock::time_point min_time, bool tablets_enabled) {
|
||||
static future<> validate_manifest(const locator::topology& topology, const fs::path& snapshot_dir, const std::set<sstring>& in_snapshot_dir, gc_clock::time_point min_time, bool tablets_enabled, std::optional<int> ttl = std::nullopt) {
|
||||
sstring suffix = "-TOC.txt";
|
||||
auto sstables_in_snapshot = collect_sstables(in_snapshot_dir, suffix);
|
||||
std::set<sstring> sstables_in_manifest;
|
||||
@@ -606,7 +606,15 @@ static future<> validate_manifest(const locator::topology& topology, const fs::p
|
||||
BOOST_REQUIRE(created_at_seconds > 0);
|
||||
auto& expires_at = manifest_snapshot["expires_at"];
|
||||
BOOST_REQUIRE(expires_at.IsNumber());
|
||||
BOOST_REQUIRE_GE(expires_at.GetInt64(), created_at_seconds);
|
||||
auto expires_at_seconds = expires_at.GetInt64();
|
||||
if (ttl) {
|
||||
BOOST_REQUIRE_GE(expires_at_seconds, created_at_seconds + *ttl);
|
||||
BOOST_REQUIRE_LE(expires_at_seconds, created_at_seconds + *ttl + 1);
|
||||
} else {
|
||||
BOOST_REQUIRE_GE(expires_at.GetInt64(), created_at_seconds);
|
||||
}
|
||||
} else if (ttl) {
|
||||
BOOST_FAIL("manifest should have expires_at when ttl is set");
|
||||
}
|
||||
|
||||
BOOST_REQUIRE(manifest_json.HasMember("table"));
|
||||
@@ -757,6 +765,58 @@ SEASTAR_TEST_CASE(snapshot_skip_flush_works) {
|
||||
});
|
||||
}
|
||||
|
||||
SEASTAR_THREAD_TEST_CASE(test_auto_snapshot_ttl) {
|
||||
bool tablets_enabled = true;
|
||||
bool create_mvs = false;
|
||||
int ttl = 1;
|
||||
#ifdef SCYLLA_BUILD_MODE_DEBUG
|
||||
ttl = 3;
|
||||
#endif
|
||||
|
||||
auto db_cfg_ptr = make_shared<db::config>();
|
||||
db_cfg_ptr->tablets_mode_for_new_keyspaces(tablets_enabled ? db::tablets_mode_t::mode::enabled : db::tablets_mode_t::mode::disabled);
|
||||
db_cfg_ptr->auto_snapshot(true);
|
||||
db_cfg_ptr->auto_snapshot_ttl(ttl);
|
||||
std::string ks_name = "ks";
|
||||
std::string table_name = "test";
|
||||
size_t num_keys = 100;
|
||||
do_with_some_data_in_thread({table_name}, [&] (cql_test_env& e) {
|
||||
auto min_time = gc_clock::now();
|
||||
take_snapshot(e, ks_name, table_name).get();
|
||||
|
||||
auto& cf = e.local_db().find_column_family(ks_name, table_name);
|
||||
auto table_directory = table_dir(cf);
|
||||
|
||||
auto in_table_dir = collect_files(table_directory).get();
|
||||
// snapshot triggered a flush and wrote the data down.
|
||||
BOOST_REQUIRE_GE(in_table_dir.size(), 9);
|
||||
|
||||
testlog.debug("Dropping table {}.{}", ks_name, table_name);
|
||||
replica::database::legacy_drop_table_on_all_shards(e.db(), e.get_system_keyspace(), ks_name, table_name, true).get();
|
||||
|
||||
fs::path snapshot_dir;
|
||||
auto snapshot_base_dir = table_directory / sstables::snapshots_dir;
|
||||
directory_lister lister(snapshot_base_dir, lister::dir_entry_types::of<directory_entry_type::directory>());
|
||||
while (auto de = lister.get().get()) {
|
||||
if (de->name.starts_with("pre-drop")) {
|
||||
BOOST_REQUIRE(snapshot_dir.empty()); // only one pre-drop snapshot should be present
|
||||
testlog.debug("Found auto-snapshot directory: {}", snapshot_base_dir / de->name);
|
||||
snapshot_dir = snapshot_base_dir / de->name;
|
||||
}
|
||||
}
|
||||
|
||||
auto in_snapshot_dir = collect_files(snapshot_dir).get();
|
||||
|
||||
in_table_dir.insert("manifest.json");
|
||||
in_table_dir.insert("schema.cql");
|
||||
// all files were copied and manifest was generated
|
||||
BOOST_REQUIRE_EQUAL(in_table_dir, in_snapshot_dir);
|
||||
|
||||
const auto& topology = e.local_db().get_token_metadata().get_topology();
|
||||
validate_manifest(topology, snapshot_dir, in_snapshot_dir, min_time, tablets_enabled, ttl).get();
|
||||
}, create_mvs, db_cfg_ptr, num_keys).get();
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(snapshot_list_okay) {
|
||||
return do_with_some_data_in_thread({"cf"}, [] (cql_test_env& e) {
|
||||
auto& cf = e.local_db().find_column_family("ks", "cf");
|
||||
|
||||
Reference in New Issue
Block a user