Files
scylladb/test/boost/sstable_move_test.cc
Kefu Chai 3e84d43f93 treewide: use seastar::format() or fmt::format() explicitly
before this change, we rely on `using namespace seastar` to use
`seastar::format()` without qualifying the `format()` with its
namespace. this works fine until we changed the parameter type
of format string `seastar::format()` from `const char*` to
`fmt::format_string<...>`. this change practically invited
`seastar::format()` to the club of `std::format()` and `fmt::format()`,
where all members accept a templated parameter as its `fmt`
parameter. and `seastar::format()` is not the best candidate anymore.
despite that argument-dependent lookup (ADT for short) favors the
function which is in the same namespace as its parameter, but
`using namespace` makes `seastar::format()` more competitive,
so both `std::format()` and `seastar::format()` are considered
as the condidates.

that is what is happening scylladb in quite a few caller sites of
`format()`, hence ADT is not able to tell which function the winner
in the name lookup:

```
/__w/scylladb/scylladb/mutation/mutation_fragment_stream_validator.cc:265:12: error: call to 'format' is ambiguous
  265 |     return format("{} ({}.{} {})", _name_view, s.ks_name(), s.cf_name(), s.id());
      |            ^~~~~~
/usr/bin/../lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/format:4290:5: note: candidate function [with _Args = <const std::basic_string_view<char> &, const seastar::basic_sstring<char, unsigned int, 15> &, const seastar::basic_sstring<char, unsigned int, 15> &, const utils::tagged_uuid<table_id_tag> &>]
 4290 |     format(format_string<_Args...> __fmt, _Args&&... __args)
      |     ^
/__w/scylladb/scylladb/seastar/include/seastar/core/print.hh:143:1: note: candidate function [with A = <const std::basic_string_view<char> &, const seastar::basic_sstring<char, unsigned int, 15> &, const seastar::basic_sstring<char, unsigned int, 15> &, const utils::tagged_uuid<table_id_tag> &>]
  143 | format(fmt::format_string<A...> fmt, A&&... a) {
      | ^
```

in this change, we

change all `format()` to either `fmt::format()` or `seastar::format()`
with following rules:
- if the caller expects an `sstring` or `std::string_view`, change to
  `seastar::format()`
- if the caller expects an `std::string`, change to `fmt::format()`.
  because, `sstring::operator std::basic_string` would incur a deep
  copy.

we will need another change to enable scylladb to compile with the
latest seastar. namely, to pass the format string as a templated
parameter down to helper functions which format their parameters.
to miminize the scope of this change, let's include that change when
bumping up the seastar submodule. as that change will depend on
the seastar change.

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>
2024-09-11 23:21:40 +03:00

137 lines
5.6 KiB
C++

/*
* Copyright (C) 2020-present ScyllaDB
*/
/*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <filesystem>
#include "test/lib/scylla_test_case.hh"
#include <seastar/testing/thread_test_case.hh>
#include "utils/lister.hh"
#include "test/lib/tmpdir.hh"
#include "test/lib/sstable_test_env.hh"
#include "sstable_test.hh"
using namespace sstables;
namespace fs = std::filesystem;
// Must be called from a seastar thread
static auto copy_sst_to_tmpdir(fs::path tmp_path, test_env& env, sstables::schema_ptr schema_ptr, fs::path src_path, sstables::generation_type gen) {
auto sst = env.reusable_sst(schema_ptr, src_path.native(), gen).get();
auto dst_path = tmp_path / src_path.filename() / format("gen-{}", gen);
recursive_touch_directory(dst_path.native()).get();
for (auto p : sst->all_components()) {
auto src_path = test(sst).filename(p.first);
copy_file(src_path, dst_path / src_path.filename());
}
return std::make_pair(env.reusable_sst(schema_ptr, dst_path.native(), gen).get(), dst_path.native());
}
SEASTAR_THREAD_TEST_CASE(test_sstable_move) {
tmpdir tmp;
auto env = test_env();
auto stop_env = defer([&env] { env.stop().get(); });
sstables::sstable_generation_generator gen_generator{0};
auto [ sst, cur_dir ] = copy_sst_to_tmpdir(tmp.path(), env, uncompressed_schema(), fs::path(uncompressed_dir()), gen_generator());
generation_type gen{0};
for (auto i = 0; i < 2; i++) {
gen = gen_generator();
auto new_dir = seastar::format("{}/gen-{}", fs::path(cur_dir).parent_path().native(), gen);
touch_directory(new_dir).get();
test(sst).move_to_new_dir(new_dir, gen).get();
// the source directory must be empty now
remove_file(cur_dir).get();
cur_dir = new_dir;
}
// close the sst and make we can load it from the new directory.
sst->close_files().get();
sst = env.reusable_sst(uncompressed_schema(), cur_dir, gen).get();
}
SEASTAR_THREAD_TEST_CASE(test_sstable_move_idempotent) {
tmpdir tmp;
auto env = test_env();
auto stop_env = defer([&env] { env.stop().get(); });
sstables::sstable_generation_generator gen_generator{0};
auto [ sst, cur_dir ] = copy_sst_to_tmpdir(tmp.path(), env, uncompressed_schema(), fs::path(uncompressed_dir()), gen_generator());
sstring old_path = sst->get_storage().prefix();
touch_directory(format("{}/{}", old_path, sstables::staging_dir)).get();
sst->change_state(sstables::sstable_state::staging).get();
sst->change_state(sstables::sstable_state::normal).get();
BOOST_REQUIRE(sstable_directory::compare_sstable_storage_prefix(old_path, sst->get_storage().prefix()));
sst->close_files().get();
}
// Simulate a crashed create_links.
// This implementation simulates create_links;
// ideally, we should have error injection points in create_links
// to cause it to return mid-way.
//
// Returns true when done
//
// Must be called from a seastar thread
static bool partial_create_links(sstable_ptr sst, fs::path dst_path, sstables::generation_type gen, int count) {
auto schema = sst->get_schema();
auto tmp_toc = sstable::filename(dst_path.native(), schema->ks_name(), schema->cf_name(), sst->get_version(), gen, sstable_format_types::big, component_type::TemporaryTOC);
link_file(test(sst).filename(component_type::TOC).native(), tmp_toc).get();
for (auto& [c, s] : sst->all_components()) {
if (count-- <= 0) {
return false;
}
auto src = test(sst).filename(c);
auto dst = sstable::filename(dst_path.native(), schema->ks_name(), schema->cf_name(), sst->get_version(), gen, sstable_format_types::big, c);
link_file(src.native(), dst).get();
}
if (count-- <= 0) {
return false;
}
auto dst = sstable::filename(dst_path.native(), schema->ks_name(), schema->cf_name(), sst->get_version(), gen, sstable_format_types::big, component_type::TOC);
remove_file(tmp_toc).get();
return true;
}
SEASTAR_THREAD_TEST_CASE(test_sstable_move_replay) {
tmpdir tmp;
auto env = test_env();
auto stop_env = defer([&env] { env.stop().get(); });
sstables::sstable_generation_generator gen_generator{0};
auto [ sst, cur_dir ] = copy_sst_to_tmpdir(tmp.path(), env, uncompressed_schema(), fs::path(uncompressed_dir()), gen_generator());
bool done;
int count = 0;
do {
auto gen = gen_generator();
auto new_dir = seastar::format("{}/gen-{}", fs::path(cur_dir).parent_path().native(), gen);
touch_directory(new_dir).get();
done = partial_create_links(sst, fs::path(new_dir), gen, count++);
test(sst).move_to_new_dir(new_dir, gen).get();
remove_file(cur_dir).get();
cur_dir = new_dir;
} while (!done);
}
SEASTAR_THREAD_TEST_CASE(test_sstable_move_exists_failure) {
tmpdir tmp;
auto env = test_env();
auto stop_env = defer([&env] { env.stop().get(); });
// please note, the SSTables used by this test are stored under
// get_test_dir("uncompressed", "ks", fs::path(uncompressed_dir())), so the
// gen_1 and gen_2 have to match with the these SSTables under that directory.
sstables::generation_type gen_1{1};
auto [ src_sst, cur_dir ] = copy_sst_to_tmpdir(tmp.path(), env, uncompressed_schema(), fs::path(uncompressed_dir()), gen_1);
sstables::generation_type gen_2{2};
auto [ dst_sst, new_dir ] = copy_sst_to_tmpdir(tmp.path(), env, uncompressed_schema(), fs::path(uncompressed_dir()), gen_2);
dst_sst->close_files().get();
BOOST_REQUIRE_THROW(test(src_sst).move_to_new_dir(new_dir, gen_2).get(), malformed_sstable_exception);
}