// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
-// This source code is licensed under the BSD-style license found in the
-// LICENSE file in the root directory of this source tree. An additional grant
-// of patent rights can be found in the PATENTS file in the same directory.
+// 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).
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
#include <thread>
#include <utility>
#include "db/db_impl.h"
-#include "port/stack_trace.h"
#include "port/port.h"
+#include "port/stack_trace.h"
#include "rocksdb/db.h"
#include "rocksdb/env.h"
#include "rocksdb/utilities/checkpoint.h"
#include "rocksdb/utilities/transaction_db.h"
+#include "util/fault_injection_test_env.h"
#include "util/sync_point.h"
#include "util/testharness.h"
DB* db_;
Options last_options_;
std::vector<ColumnFamilyHandle*> handles_;
+ std::string snapshot_name_;
CheckpointTest() : env_(Env::Default()) {
env_->SetBackgroundThreads(1, Env::LOW);
env_->SetBackgroundThreads(1, Env::HIGH);
- dbname_ = test::TmpDir(env_) + "/db_test";
+ dbname_ = test::PerThreadDBPath(env_, "checkpoint_test");
alternative_wal_dir_ = dbname_ + "/wal";
auto options = CurrentOptions();
auto delete_options = options;
// Destroy it for not alternative WAL dir is used.
EXPECT_OK(DestroyDB(dbname_, options));
db_ = nullptr;
+ snapshot_name_ = test::PerThreadDBPath(env_, "snapshot");
+ std::string snapshot_tmp_name = snapshot_name_ + ".tmp";
+ EXPECT_OK(DestroyDB(snapshot_name_, options));
+ env_->DeleteDir(snapshot_name_);
+ EXPECT_OK(DestroyDB(snapshot_tmp_name, options));
+ env_->DeleteDir(snapshot_tmp_name);
Reopen(options);
}
options.db_paths.emplace_back(dbname_ + "_3", 0);
options.db_paths.emplace_back(dbname_ + "_4", 0);
EXPECT_OK(DestroyDB(dbname_, options));
+ EXPECT_OK(DestroyDB(snapshot_name_, options));
}
// Return the current option configuration.
};
TEST_F(CheckpointTest, GetSnapshotLink) {
- for (uint64_t log_size_for_fush : {0, 1000000}) {
+ for (uint64_t log_size_for_flush : {0, 1000000}) {
Options options;
- const std::string snapshot_name = test::TmpDir(env_) + "/snapshot";
DB* snapshotDB;
ReadOptions roptions;
std::string result;
delete db_;
db_ = nullptr;
ASSERT_OK(DestroyDB(dbname_, options));
- ASSERT_OK(DestroyDB(snapshot_name, options));
- env_->DeleteDir(snapshot_name);
// Create a database
Status s;
ASSERT_OK(Put(key, "v1"));
// Take a snapshot
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
- ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name, log_size_for_fush));
+ ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_, log_size_for_flush));
ASSERT_OK(Put(key, "v2"));
ASSERT_EQ("v2", Get(key));
ASSERT_OK(Flush());
ASSERT_EQ("v2", Get(key));
// Open snapshot and verify contents while DB is running
options.create_if_missing = false;
- ASSERT_OK(DB::Open(options, snapshot_name, &snapshotDB));
+ ASSERT_OK(DB::Open(options, snapshot_name_, &snapshotDB));
ASSERT_OK(snapshotDB->Get(roptions, key, &result));
ASSERT_EQ("v1", result);
delete snapshotDB;
// Open snapshot and verify contents
options.create_if_missing = false;
- dbname_ = snapshot_name;
+ dbname_ = snapshot_name_;
ASSERT_OK(DB::Open(options, dbname_, &db_));
ASSERT_EQ("v1", Get(key));
delete db_;
delete checkpoint;
// Restore DB name
- dbname_ = test::TmpDir(env_) + "/db_test";
+ dbname_ = test::PerThreadDBPath(env_, "db_test");
}
}
ASSERT_OK(Put(4, "four", "four"));
ASSERT_OK(Put(5, "five", "five"));
- const std::string snapshot_name = test::TmpDir(env_) + "/snapshot";
DB* snapshotDB;
ReadOptions roptions;
std::string result;
std::vector<ColumnFamilyHandle*> cphandles;
- ASSERT_OK(DestroyDB(snapshot_name, options));
- env_->DeleteDir(snapshot_name);
-
Status s;
// Take a snapshot
rocksdb::port::Thread t([&]() {
Checkpoint* checkpoint;
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
- ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name));
+ ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_));
delete checkpoint;
});
TEST_SYNC_POINT("CheckpointTest::CheckpointCF:1");
for (size_t i = 0; i < cfs.size(); ++i) {
column_families.push_back(ColumnFamilyDescriptor(cfs[i], options));
}
- ASSERT_OK(DB::Open(options, snapshot_name,
+ ASSERT_OK(DB::Open(options, snapshot_name_,
column_families, &cphandles, &snapshotDB));
ASSERT_OK(snapshotDB->Get(roptions, cphandles[0], "Default", &result));
ASSERT_EQ("Default1", result);
cphandles.clear();
delete snapshotDB;
snapshotDB = nullptr;
- ASSERT_OK(DestroyDB(snapshot_name, options));
}
TEST_F(CheckpointTest, CheckpointCFNoFlush) {
Flush();
ASSERT_OK(Put(2, "two", "two"));
- const std::string snapshot_name = test::TmpDir(env_) + "/snapshot";
DB* snapshotDB;
ReadOptions roptions;
std::string result;
std::vector<ColumnFamilyHandle*> cphandles;
- ASSERT_OK(DestroyDB(snapshot_name, options));
- env_->DeleteDir(snapshot_name);
-
Status s;
// Take a snapshot
rocksdb::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::BackgroundCallFlush:start", [&](void* arg) {
+ "DBImpl::BackgroundCallFlush:start", [&](void* /*arg*/) {
// Flush should never trigger.
- ASSERT_TRUE(false);
+ FAIL();
});
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
Checkpoint* checkpoint;
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
- ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name, 1000000));
+ ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_, 1000000));
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
delete checkpoint;
for (size_t i = 0; i < cfs.size(); ++i) {
column_families.push_back(ColumnFamilyDescriptor(cfs[i], options));
}
- ASSERT_OK(DB::Open(options, snapshot_name, column_families, &cphandles,
+ ASSERT_OK(DB::Open(options, snapshot_name_, column_families, &cphandles,
&snapshotDB));
ASSERT_OK(snapshotDB->Get(roptions, cphandles[0], "Default", &result));
ASSERT_EQ("Default", result);
cphandles.clear();
delete snapshotDB;
snapshotDB = nullptr;
- ASSERT_OK(DestroyDB(snapshot_name, options));
}
TEST_F(CheckpointTest, CurrentFileModifiedWhileCheckpointing) {
- const std::string kSnapshotName = test::TmpDir(env_) + "/snapshot";
- ASSERT_OK(DestroyDB(kSnapshotName, CurrentOptions()));
- env_->DeleteDir(kSnapshotName);
-
Options options = CurrentOptions();
options.max_manifest_file_size = 0; // always rollover manifest for file add
Reopen(options);
rocksdb::port::Thread t([&]() {
Checkpoint* checkpoint;
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
- ASSERT_OK(checkpoint->CreateCheckpoint(kSnapshotName));
+ ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_));
delete checkpoint;
});
TEST_SYNC_POINT(
DB* snapshotDB;
// Successful Open() implies that CURRENT pointed to the manifest in the
// checkpoint.
- ASSERT_OK(DB::Open(options, kSnapshotName, &snapshotDB));
+ ASSERT_OK(DB::Open(options, snapshot_name_, &snapshotDB));
delete snapshotDB;
snapshotDB = nullptr;
}
TEST_F(CheckpointTest, CurrentFileModifiedWhileCheckpointing2PC) {
Close();
- const std::string kSnapshotName = test::TmpDir(env_) + "/snapshot";
- const std::string dbname = test::TmpDir() + "/transaction_testdb";
- ASSERT_OK(DestroyDB(kSnapshotName, CurrentOptions()));
+ const std::string dbname = test::PerThreadDBPath("transaction_testdb");
ASSERT_OK(DestroyDB(dbname, CurrentOptions()));
- env_->DeleteDir(kSnapshotName);
env_->DeleteDir(dbname);
Options options = CurrentOptions();
rocksdb::port::Thread t([&]() {
Checkpoint* checkpoint;
ASSERT_OK(Checkpoint::Create(txdb, &checkpoint));
- ASSERT_OK(checkpoint->CreateCheckpoint(kSnapshotName));
+ ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_));
delete checkpoint;
});
TEST_SYNC_POINT(
// No more than two logs files should exist.
std::vector<std::string> files;
- env_->GetChildren(kSnapshotName, &files);
+ env_->GetChildren(snapshot_name_, &files);
int num_log_files = 0;
for (auto& file : files) {
uint64_t num;
column_families.push_back(
ColumnFamilyDescriptor("CFB", ColumnFamilyOptions()));
std::vector<rocksdb::ColumnFamilyHandle*> cf_handles;
- ASSERT_OK(TransactionDB::Open(options, txn_db_options, kSnapshotName,
+ ASSERT_OK(TransactionDB::Open(options, txn_db_options, snapshot_name_,
column_families, &cf_handles, &snapshotDB));
ASSERT_OK(snapshotDB->Get(read_options, "foo", &value));
ASSERT_EQ(value, "bar");
delete txdb;
}
+TEST_F(CheckpointTest, CheckpointInvalidDirectoryName) {
+ for (std::string checkpoint_dir : {"", "/", "////"}) {
+ Checkpoint* checkpoint;
+ ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
+ ASSERT_TRUE(checkpoint->CreateCheckpoint("").IsInvalidArgument());
+ delete checkpoint;
+ }
+}
+
+TEST_F(CheckpointTest, CheckpointWithParallelWrites) {
+ // When run with TSAN, this exposes the data race fixed in
+ // https://github.com/facebook/rocksdb/pull/3603
+ ASSERT_OK(Put("key1", "val1"));
+ port::Thread thread([this]() { ASSERT_OK(Put("key2", "val2")); });
+ Checkpoint* checkpoint;
+ ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
+ ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_));
+ delete checkpoint;
+ thread.join();
+}
+
+TEST_F(CheckpointTest, CheckpointWithUnsyncedDataDropped) {
+ Options options = CurrentOptions();
+ std::unique_ptr<FaultInjectionTestEnv> env(new FaultInjectionTestEnv(env_));
+ options.env = env.get();
+ Reopen(options);
+ ASSERT_OK(Put("key1", "val1"));
+ Checkpoint* checkpoint;
+ ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
+ ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_));
+ delete checkpoint;
+ env->DropUnsyncedFileData();
+
+ // make sure it's openable even though whatever data that wasn't synced got
+ // dropped.
+ options.env = env_;
+ DB* snapshot_db;
+ ASSERT_OK(DB::Open(options, snapshot_name_, &snapshot_db));
+ ReadOptions read_opts;
+ std::string get_result;
+ ASSERT_OK(snapshot_db->Get(read_opts, "key1", &get_result));
+ ASSERT_EQ("val1", get_result);
+ delete snapshot_db;
+ delete db_;
+ db_ = nullptr;
+}
+
} // namespace rocksdb
int main(int argc, char** argv) {
#else
#include <stdio.h>
-int main(int argc, char** argv) {
+int main(int /*argc*/, char** /*argv*/) {
fprintf(stderr, "SKIPPED as Checkpoint is not supported in ROCKSDB_LITE\n");
return 0;
}