diff --git a/sstables/storage.cc b/sstables/storage.cc index c12908dcce..2c5646cad3 100644 --- a/sstables/storage.cc +++ b/sstables/storage.cc @@ -449,6 +449,7 @@ future<> filesystem_storage::snapshot(const sstable& sst, sstring name) const { co_await create_links_common(sst, snapshot_dir.native(), sst._generation, link_mode::default_mode); } future<> filesystem_storage::clone(const sstable& sst, generation_type gen, bool leave_unsealed) const { + sstlog.debug("Cloning {} dir={} generation={} leave_unsealed={}", sst.get_filename(), _dir.path().native(), gen, leave_unsealed); co_await create_links_common(sst, _dir.path().native(), std::move(gen), leave_unsealed ? link_mode::leave_unsealed : link_mode::default_mode); } diff --git a/test/boost/sstable_move_test.cc b/test/boost/sstable_move_test.cc index 48597383ab..2a67c42ef1 100644 --- a/test/boost/sstable_move_test.cc +++ b/test/boost/sstable_move_test.cc @@ -13,6 +13,8 @@ #include "utils/lister.hh" #include "test/lib/tmpdir.hh" #include "test/lib/sstable_test_env.hh" +#include "test/lib/sstable_utils.hh" +#include "test/lib/simple_schema.hh" #include "sstable_test.hh" using namespace sstables; @@ -140,3 +142,37 @@ SEASTAR_THREAD_TEST_CASE(test_sstable_move_exists_failure) { dst_sst->close_files().get(); BOOST_REQUIRE_THROW(test(src_sst).move_to_new_dir(new_dir, gen_2).get(), malformed_sstable_exception); } + +// Test that filesystem_storage::clone preserves the input sstable state. +// An sstable in staging is cloned and re-loaded; the re-loaded clone should still +// be in staging (i.e. state() == staging and requires_view_building() == true). +SEASTAR_THREAD_TEST_CASE(test_sstable_clone_preserves_staging_state) { + auto scf = make_sstable_compressor_factory_for_tests_in_thread(); + test_env env({}, *scf); + auto stop_env = defer([&env] { env.stop().get(); }); + + simple_schema ss; + auto schema = ss.schema(); + + // Create an sstable in normal state. + auto sst = make_sstable_containing(env.make_sst_factory(schema), {ss.new_mutation("key1")}); + + // Move it to staging state. + sst->change_state(sstable_state::staging).get(); + BOOST_REQUIRE(sst->state() == sstable_state::staging); + + // Clone the staging sstable to a new generation. The clone should land in the + // same (staging) directory as the source. + auto clone_gen = env.new_generation(); + sst->clone(clone_gen).get(); + + // Load the cloned sstable from the staging directory. We use the storage + // prefix of the source (which is the staging sub-directory) so that the + // loader can find the files, mirroring what distributed_loader does. + auto staging_dir = sst->get_storage().prefix(); + auto cloned_sst = env.reusable_sst(schema, staging_dir, clone_gen).get(); + + // Assert that the cloned sstable preserves the staging state. + BOOST_REQUIRE(cloned_sst->state() == sstable_state::staging); + BOOST_REQUIRE(cloned_sst->requires_view_building()); +} diff --git a/test/lib/test_services.cc b/test/lib/test_services.cc index f8bdafd15b..d154676a76 100644 --- a/test/lib/test_services.cc +++ b/test/lib/test_services.cc @@ -23,6 +23,7 @@ #include "utils/assert.hh" #include "utils/overloaded_functor.hh" #include +#include #include #include #include @@ -390,12 +391,24 @@ test_env::make_sstable(schema_ptr schema, sstring dir, sstables::generation_type size_t buffer_size, db_clock::time_point now) { // FIXME -- most of the callers work with _impl->dir's path, so // test_env can initialize the .dir/.prefix only once, when constructed + sstables::sstable_state state = sstables::sstable_state::normal; + auto filename = std::filesystem::path(dir).filename().native(); + if (filename == sstables::staging_dir) { + state = sstables::sstable_state::staging; + } else if (filename == sstables::upload_dir) { + state = sstables::sstable_state::upload; + } else if (filename == sstables::quarantine_dir) { + state = sstables::sstable_state::quarantine; + } + if (state != sstables::sstable_state::normal) { + dir = std::filesystem::path(dir).parent_path().native(); + } auto storage = _impl->storage; std::visit(overloaded_functor { [&dir] (data_dictionary::storage_options::local& o) { o.dir = dir; }, [&schema] (data_dictionary::storage_options::object_storage& o) { o.location = schema->id(); }, }, storage.value); - return _impl->mgr.make_sstable(std::move(schema), storage, generation, sstables::sstable_state::normal, v, f, now, default_io_error_handler_gen(), buffer_size); + return _impl->mgr.make_sstable(std::move(schema), storage, generation, state, v, f, now, default_io_error_handler_gen(), buffer_size); } shared_sstable