| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- //
- #ifndef ROCKSDB_LITE
- #include "rocksdb/utilities/ldb_cmd.h"
- #include "db/version_edit.h"
- #include "db/version_set.h"
- #include "env/composite_env_wrapper.h"
- #include "file/filename.h"
- #include "port/stack_trace.h"
- #include "rocksdb/file_checksum.h"
- #include "test_util/sync_point.h"
- #include "test_util/testharness.h"
- #include "test_util/testutil.h"
- #include "util/file_checksum_helper.h"
- using std::string;
- using std::vector;
- using std::map;
- namespace ROCKSDB_NAMESPACE {
- class LdbCmdTest : public testing::Test {
- public:
- LdbCmdTest() : testing::Test() {}
- Env* TryLoadCustomOrDefaultEnv() {
- const char* test_env_uri = getenv("TEST_ENV_URI");
- if (!test_env_uri) {
- return Env::Default();
- }
- Env* env = Env::Default();
- Env::LoadEnv(test_env_uri, &env, &env_guard_);
- return env;
- }
- private:
- std::shared_ptr<Env> env_guard_;
- };
- TEST_F(LdbCmdTest, HexToString) {
- // map input to expected outputs.
- // odd number of "hex" half bytes doesn't make sense
- map<string, vector<int>> inputMap = {
- {"0x07", {7}}, {"0x5050", {80, 80}}, {"0xFF", {-1}},
- {"0x1234", {18, 52}}, {"0xaaAbAC", {-86, -85, -84}}, {"0x1203", {18, 3}},
- };
- for (const auto& inPair : inputMap) {
- auto actual = ROCKSDB_NAMESPACE::LDBCommand::HexToString(inPair.first);
- auto expected = inPair.second;
- for (unsigned int i = 0; i < actual.length(); i++) {
- EXPECT_EQ(expected[i], static_cast<int>((signed char) actual[i]));
- }
- auto reverse = ROCKSDB_NAMESPACE::LDBCommand::StringToHex(actual);
- EXPECT_STRCASEEQ(inPair.first.c_str(), reverse.c_str());
- }
- }
- TEST_F(LdbCmdTest, HexToStringBadInputs) {
- const vector<string> badInputs = {
- "0xZZ", "123", "0xx5", "0x111G", "0x123", "Ox12", "0xT", "0x1Q1",
- };
- for (const auto badInput : badInputs) {
- try {
- ROCKSDB_NAMESPACE::LDBCommand::HexToString(badInput);
- std::cerr << "Should fail on bad hex value: " << badInput << "\n";
- FAIL();
- } catch (...) {
- }
- }
- }
- TEST_F(LdbCmdTest, MemEnv) {
- Env* base_env = TryLoadCustomOrDefaultEnv();
- std::unique_ptr<Env> env(NewMemEnv(base_env));
- Options opts;
- opts.env = env.get();
- opts.create_if_missing = true;
- opts.file_system.reset(new LegacyFileSystemWrapper(opts.env));
- DB* db = nullptr;
- std::string dbname = test::TmpDir();
- ASSERT_OK(DB::Open(opts, dbname, &db));
- WriteOptions wopts;
- for (int i = 0; i < 100; i++) {
- char buf[16];
- snprintf(buf, sizeof(buf), "%08d", i);
- ASSERT_OK(db->Put(wopts, buf, buf));
- }
- FlushOptions fopts;
- fopts.wait = true;
- ASSERT_OK(db->Flush(fopts));
- delete db;
- char arg1[] = "./ldb";
- char arg2[1024];
- snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str());
- char arg3[] = "dump_live_files";
- char* argv[] = {arg1, arg2, arg3};
- ASSERT_EQ(0,
- LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
- }
- class FileChecksumTestHelper {
- private:
- Options options_;
- DB* db_;
- std::string dbname_;
- Status VerifyChecksum(LiveFileMetaData& file_meta) {
- std::string cur_checksum;
- std::string checksum_func_name;
- Status s;
- EnvOptions soptions;
- std::unique_ptr<SequentialFile> file_reader;
- std::string file_path = dbname_ + "/" + file_meta.name;
- s = options_.env->NewSequentialFile(file_path, &file_reader, soptions);
- if (!s.ok()) {
- return s;
- }
- std::unique_ptr<char[]> scratch(new char[2048]);
- bool first_read = true;
- Slice result;
- FileChecksumFunc* file_checksum_func =
- options_.sst_file_checksum_func.get();
- if (file_checksum_func == nullptr) {
- cur_checksum = kUnknownFileChecksum;
- checksum_func_name = kUnknownFileChecksumFuncName;
- } else {
- checksum_func_name = file_checksum_func->Name();
- s = file_reader->Read(2048, &result, scratch.get());
- if (!s.ok()) {
- return s;
- }
- while (result.size() != 0) {
- if (first_read) {
- first_read = false;
- cur_checksum =
- file_checksum_func->Value(scratch.get(), result.size());
- } else {
- cur_checksum = file_checksum_func->Extend(cur_checksum, scratch.get(),
- result.size());
- }
- s = file_reader->Read(2048, &result, scratch.get());
- if (!s.ok()) {
- return s;
- }
- }
- }
- std::string stored_checksum = file_meta.file_checksum;
- std::string stored_checksum_func_name = file_meta.file_checksum_func_name;
- if ((cur_checksum != stored_checksum) ||
- (checksum_func_name != stored_checksum_func_name)) {
- return Status::Corruption(
- "Checksum does not match! The file: " + file_meta.name +
- ", checksum name: " + stored_checksum_func_name + " and checksum " +
- stored_checksum + ". However, expected checksum name: " +
- checksum_func_name + " and checksum " + cur_checksum);
- }
- return Status::OK();
- }
- public:
- FileChecksumTestHelper(Options& options, DB* db, std::string db_name)
- : options_(options), db_(db), dbname_(db_name) {}
- ~FileChecksumTestHelper() {}
- // Verify the checksum information in Manifest.
- Status VerifyChecksumInManifest(
- const std::vector<LiveFileMetaData>& live_files) {
- // Step 1: verify if the dbname_ is correct
- if (dbname_[dbname_.length() - 1] != '/') {
- dbname_.append("/");
- }
- // Step 2, get the the checksum information by recovering the VersionSet
- // from Manifest.
- std::unique_ptr<FileChecksumList> checksum_list(NewFileChecksumList());
- EnvOptions sopt;
- std::shared_ptr<Cache> tc(NewLRUCache(options_.max_open_files - 10,
- options_.table_cache_numshardbits));
- options_.db_paths.emplace_back(dbname_, 0);
- options_.num_levels = 64;
- WriteController wc(options_.delayed_write_rate);
- WriteBufferManager wb(options_.db_write_buffer_size);
- ImmutableDBOptions immutable_db_options(options_);
- VersionSet versions(dbname_, &immutable_db_options, sopt, tc.get(), &wb,
- &wc, nullptr);
- std::vector<std::string> cf_name_list;
- Status s;
- s = versions.ListColumnFamilies(&cf_name_list, dbname_,
- options_.file_system.get());
- if (s.ok()) {
- std::vector<ColumnFamilyDescriptor> cf_list;
- for (const auto& name : cf_name_list) {
- fprintf(stdout, "cf_name: %s", name.c_str());
- cf_list.emplace_back(name, ColumnFamilyOptions(options_));
- }
- s = versions.Recover(cf_list, true);
- }
- if (s.ok()) {
- s = versions.GetLiveFilesChecksumInfo(checksum_list.get());
- }
- if (!s.ok()) {
- return s;
- }
- // Step 3 verify the checksum
- if (live_files.size() != checksum_list->size()) {
- return Status::Corruption("The number of files does not match!");
- }
- for (size_t i = 0; i < live_files.size(); i++) {
- std::string stored_checksum = "";
- std::string stored_func_name = "";
- s = checksum_list->SearchOneFileChecksum(
- live_files[i].file_number, &stored_checksum, &stored_func_name);
- if (s.IsNotFound()) {
- return s;
- }
- if (live_files[i].file_checksum != stored_checksum ||
- live_files[i].file_checksum_func_name != stored_func_name) {
- return Status::Corruption(
- "Checksum does not match! The file: " +
- ToString(live_files[i].file_number) +
- ". In Manifest, checksum name: " + stored_func_name +
- " and checksum " + stored_checksum +
- ". However, expected checksum name: " +
- live_files[i].file_checksum_func_name + " and checksum " +
- live_files[i].file_checksum);
- }
- }
- return Status::OK();
- }
- // Verify the checksum of each file by recalculting the checksum and
- // comparing it with the one being generated when a SST file is created.
- Status VerifyEachFileChecksum() {
- assert(db_ != nullptr);
- std::vector<LiveFileMetaData> live_files;
- db_->GetLiveFilesMetaData(&live_files);
- for (auto a_file : live_files) {
- Status cs = VerifyChecksum(a_file);
- if (!cs.ok()) {
- return cs;
- }
- }
- return Status::OK();
- }
- };
- TEST_F(LdbCmdTest, DumpFileChecksumNoChecksum) {
- Env* base_env = TryLoadCustomOrDefaultEnv();
- std::unique_ptr<Env> env(NewMemEnv(base_env));
- Options opts;
- opts.env = env.get();
- opts.create_if_missing = true;
- opts.file_system.reset(new LegacyFileSystemWrapper(opts.env));
- DB* db = nullptr;
- std::string dbname = test::TmpDir();
- ASSERT_OK(DB::Open(opts, dbname, &db));
- WriteOptions wopts;
- FlushOptions fopts;
- fopts.wait = true;
- Random rnd(test::RandomSeed());
- for (int i = 0; i < 200; i++) {
- char buf[16];
- snprintf(buf, sizeof(buf), "%08d", i);
- std::string v;
- test::RandomString(&rnd, 100, &v);
- ASSERT_OK(db->Put(wopts, buf, v));
- }
- ASSERT_OK(db->Flush(fopts));
- for (int i = 100; i < 300; i++) {
- char buf[16];
- snprintf(buf, sizeof(buf), "%08d", i);
- std::string v;
- test::RandomString(&rnd, 100, &v);
- ASSERT_OK(db->Put(wopts, buf, v));
- }
- ASSERT_OK(db->Flush(fopts));
- for (int i = 200; i < 400; i++) {
- char buf[16];
- snprintf(buf, sizeof(buf), "%08d", i);
- std::string v;
- test::RandomString(&rnd, 100, &v);
- ASSERT_OK(db->Put(wopts, buf, v));
- }
- ASSERT_OK(db->Flush(fopts));
- for (int i = 300; i < 400; i++) {
- char buf[16];
- snprintf(buf, sizeof(buf), "%08d", i);
- std::string v;
- test::RandomString(&rnd, 100, &v);
- ASSERT_OK(db->Put(wopts, buf, v));
- }
- ASSERT_OK(db->Flush(fopts));
- char arg1[] = "./ldb";
- char arg2[1024];
- snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str());
- char arg3[] = "file_checksum_dump";
- char* argv[] = {arg1, arg2, arg3};
- ASSERT_EQ(0,
- LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
- // Verify each sst file checksum value and checksum name
- FileChecksumTestHelper fct_helper(opts, db, dbname);
- ASSERT_OK(fct_helper.VerifyEachFileChecksum());
- // Manually trigger compaction
- char b_buf[16];
- snprintf(b_buf, sizeof(b_buf), "%08d", 0);
- char e_buf[16];
- snprintf(e_buf, sizeof(e_buf), "%08d", 399);
- Slice begin(b_buf);
- Slice end(e_buf);
- CompactRangeOptions options;
- ASSERT_OK(db->CompactRange(options, &begin, &end));
- // Verify each sst file checksum after compaction
- FileChecksumTestHelper fct_helper_ac(opts, db, dbname);
- ASSERT_OK(fct_helper_ac.VerifyEachFileChecksum());
- ASSERT_EQ(0,
- LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
- // Verify the checksum information in memory is the same as that in Manifest;
- std::vector<LiveFileMetaData> live_files;
- db->GetLiveFilesMetaData(&live_files);
- delete db;
- ASSERT_OK(fct_helper_ac.VerifyChecksumInManifest(live_files));
- }
- TEST_F(LdbCmdTest, DumpFileChecksumCRC32) {
- Env* base_env = TryLoadCustomOrDefaultEnv();
- std::unique_ptr<Env> env(NewMemEnv(base_env));
- Options opts;
- opts.env = env.get();
- opts.create_if_missing = true;
- opts.sst_file_checksum_func =
- std::shared_ptr<FileChecksumFunc>(CreateFileChecksumFuncCrc32c());
- opts.file_system.reset(new LegacyFileSystemWrapper(opts.env));
- DB* db = nullptr;
- std::string dbname = test::TmpDir();
- ASSERT_OK(DB::Open(opts, dbname, &db));
- WriteOptions wopts;
- FlushOptions fopts;
- fopts.wait = true;
- Random rnd(test::RandomSeed());
- for (int i = 0; i < 100; i++) {
- char buf[16];
- snprintf(buf, sizeof(buf), "%08d", i);
- std::string v;
- test::RandomString(&rnd, 100, &v);
- ASSERT_OK(db->Put(wopts, buf, v));
- }
- ASSERT_OK(db->Flush(fopts));
- for (int i = 50; i < 150; i++) {
- char buf[16];
- snprintf(buf, sizeof(buf), "%08d", i);
- std::string v;
- test::RandomString(&rnd, 100, &v);
- ASSERT_OK(db->Put(wopts, buf, v));
- }
- ASSERT_OK(db->Flush(fopts));
- for (int i = 100; i < 200; i++) {
- char buf[16];
- snprintf(buf, sizeof(buf), "%08d", i);
- std::string v;
- test::RandomString(&rnd, 100, &v);
- ASSERT_OK(db->Put(wopts, buf, v));
- }
- ASSERT_OK(db->Flush(fopts));
- for (int i = 150; i < 250; i++) {
- char buf[16];
- snprintf(buf, sizeof(buf), "%08d", i);
- std::string v;
- test::RandomString(&rnd, 100, &v);
- ASSERT_OK(db->Put(wopts, buf, v));
- }
- ASSERT_OK(db->Flush(fopts));
- char arg1[] = "./ldb";
- char arg2[1024];
- snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str());
- char arg3[] = "file_checksum_dump";
- char* argv[] = {arg1, arg2, arg3};
- ASSERT_EQ(0,
- LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
- // Verify each sst file checksum value and checksum name
- FileChecksumTestHelper fct_helper(opts, db, dbname);
- ASSERT_OK(fct_helper.VerifyEachFileChecksum());
- // Manually trigger compaction
- char b_buf[16];
- snprintf(b_buf, sizeof(b_buf), "%08d", 0);
- char e_buf[16];
- snprintf(e_buf, sizeof(e_buf), "%08d", 249);
- Slice begin(b_buf);
- Slice end(e_buf);
- CompactRangeOptions options;
- ASSERT_OK(db->CompactRange(options, &begin, &end));
- // Verify each sst file checksum after compaction
- FileChecksumTestHelper fct_helper_ac(opts, db, dbname);
- ASSERT_OK(fct_helper_ac.VerifyEachFileChecksum());
- ASSERT_EQ(0,
- LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
- // Verify the checksum information in memory is the same as that in Manifest;
- std::vector<LiveFileMetaData> live_files;
- db->GetLiveFilesMetaData(&live_files);
- delete db;
- ASSERT_OK(fct_helper_ac.VerifyChecksumInManifest(live_files));
- }
- TEST_F(LdbCmdTest, OptionParsing) {
- // test parsing flags
- Options opts;
- opts.env = TryLoadCustomOrDefaultEnv();
- {
- std::vector<std::string> args;
- args.push_back("scan");
- args.push_back("--ttl");
- args.push_back("--timestamp");
- LDBCommand* command = ROCKSDB_NAMESPACE::LDBCommand::InitFromCmdLineArgs(
- args, opts, LDBOptions(), nullptr);
- const std::vector<std::string> flags = command->TEST_GetFlags();
- EXPECT_EQ(flags.size(), 2);
- EXPECT_EQ(flags[0], "ttl");
- EXPECT_EQ(flags[1], "timestamp");
- delete command;
- }
- // test parsing options which contains equal sign in the option value
- {
- std::vector<std::string> args;
- args.push_back("scan");
- args.push_back("--db=/dev/shm/ldbtest/");
- args.push_back(
- "--from='abcd/efg/hijk/lmn/"
- "opq:__rst.uvw.xyz?a=3+4+bcd+efghi&jk=lm_no&pq=rst-0&uv=wx-8&yz=a&bcd_"
- "ef=gh.ijk'");
- LDBCommand* command = ROCKSDB_NAMESPACE::LDBCommand::InitFromCmdLineArgs(
- args, opts, LDBOptions(), nullptr);
- const std::map<std::string, std::string> option_map =
- command->TEST_GetOptionMap();
- EXPECT_EQ(option_map.at("db"), "/dev/shm/ldbtest/");
- EXPECT_EQ(option_map.at("from"),
- "'abcd/efg/hijk/lmn/"
- "opq:__rst.uvw.xyz?a=3+4+bcd+efghi&jk=lm_no&pq=rst-0&uv=wx-8&yz="
- "a&bcd_ef=gh.ijk'");
- delete command;
- }
- }
- TEST_F(LdbCmdTest, ListFileTombstone) {
- Env* base_env = TryLoadCustomOrDefaultEnv();
- std::unique_ptr<Env> env(NewMemEnv(base_env));
- Options opts;
- opts.env = env.get();
- opts.create_if_missing = true;
- DB* db = nullptr;
- std::string dbname = test::TmpDir();
- ASSERT_OK(DB::Open(opts, dbname, &db));
- WriteOptions wopts;
- ASSERT_OK(db->Put(wopts, "foo", "1"));
- ASSERT_OK(db->Put(wopts, "bar", "2"));
- FlushOptions fopts;
- fopts.wait = true;
- ASSERT_OK(db->Flush(fopts));
- ASSERT_OK(db->DeleteRange(wopts, db->DefaultColumnFamily(), "foo", "foo2"));
- ASSERT_OK(db->DeleteRange(wopts, db->DefaultColumnFamily(), "bar", "foo2"));
- ASSERT_OK(db->Flush(fopts));
- delete db;
- {
- char arg1[] = "./ldb";
- char arg2[1024];
- snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str());
- char arg3[] = "list_file_range_deletes";
- char* argv[] = {arg1, arg2, arg3};
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "ListFileRangeDeletesCommand::DoCommand:BeforePrint", [&](void* arg) {
- std::string* out_str = reinterpret_cast<std::string*>(arg);
- // Count number of tombstones printed
- int num_tb = 0;
- const std::string kFingerprintStr = "start: ";
- auto offset = out_str->find(kFingerprintStr);
- while (offset != std::string::npos) {
- num_tb++;
- offset =
- out_str->find(kFingerprintStr, offset + kFingerprintStr.size());
- }
- EXPECT_EQ(2, num_tb);
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- ASSERT_EQ(
- 0, LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- // Test the case of limiting tombstones
- {
- char arg1[] = "./ldb";
- char arg2[1024];
- snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str());
- char arg3[] = "list_file_range_deletes";
- char arg4[] = "--max_keys=1";
- char* argv[] = {arg1, arg2, arg3, arg4};
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "ListFileRangeDeletesCommand::DoCommand:BeforePrint", [&](void* arg) {
- std::string* out_str = reinterpret_cast<std::string*>(arg);
- // Count number of tombstones printed
- int num_tb = 0;
- const std::string kFingerprintStr = "start: ";
- auto offset = out_str->find(kFingerprintStr);
- while (offset != std::string::npos) {
- num_tb++;
- offset =
- out_str->find(kFingerprintStr, offset + kFingerprintStr.size());
- }
- EXPECT_EQ(1, num_tb);
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- ASSERT_EQ(
- 0, LDBCommandRunner::RunCommand(4, argv, opts, LDBOptions(), nullptr));
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- }
- } // namespace ROCKSDB_NAMESPACE
- #ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
- extern "C" {
- void RegisterCustomObjects(int argc, char** argv);
- }
- #else
- void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {}
- #endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
- int main(int argc, char** argv) {
- ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
- ::testing::InitGoogleTest(&argc, argv);
- RegisterCustomObjects(argc, argv);
- return RUN_ALL_TESTS();
- }
- #else
- #include <stdio.h>
- int main(int /*argc*/, char** /*argv*/) {
- fprintf(stderr, "SKIPPED as LDBCommand is not supported in ROCKSDB_LITE\n");
- return 0;
- }
- #endif // ROCKSDB_LITE
|