| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146 |
- // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
- // Copyright (c) 2016, Red Hat, 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/env_librados.h"
- #include <rados/librados.hpp>
- #include "env/mock_env.h"
- #include "test_util/testharness.h"
- #include "rocksdb/db.h"
- #include "rocksdb/slice.h"
- #include "rocksdb/options.h"
- #include "util/random.h"
- #include <chrono>
- #include <ostream>
- #include "rocksdb/utilities/transaction_db.h"
- class Timer {
- typedef std::chrono::high_resolution_clock high_resolution_clock;
- typedef std::chrono::milliseconds milliseconds;
- public:
- explicit Timer(bool run = false)
- {
- if (run)
- Reset();
- }
- void Reset()
- {
- _start = high_resolution_clock::now();
- }
- milliseconds Elapsed() const
- {
- return std::chrono::duration_cast<milliseconds>(high_resolution_clock::now() - _start);
- }
- template <typename T, typename Traits>
- friend std::basic_ostream<T, Traits>& operator<<(std::basic_ostream<T, Traits>& out, const Timer& timer)
- {
- return out << timer.Elapsed().count();
- }
- private:
- high_resolution_clock::time_point _start;
- };
- namespace ROCKSDB_NAMESPACE {
- class EnvLibradosTest : public testing::Test {
- public:
- // we will use all of these below
- const std::string db_name = "env_librados_test_db";
- const std::string db_pool = db_name + "_pool";
- const char *keyring = "admin";
- const char *config = "../ceph/src/ceph.conf";
- EnvLibrados* env_;
- const EnvOptions soptions_;
- EnvLibradosTest()
- : env_(new EnvLibrados(db_name, config, db_pool)) {
- }
- ~EnvLibradosTest() {
- delete env_;
- librados::Rados rados;
- int ret = 0;
- do {
- ret = rados.init("admin"); // just use the client.admin keyring
- if (ret < 0) { // let's handle any error that might have come back
- std::cerr << "couldn't initialize rados! error " << ret << std::endl;
- ret = EXIT_FAILURE;
- break;
- }
- ret = rados.conf_read_file(config);
- if (ret < 0) {
- // This could fail if the config file is malformed, but it'd be hard.
- std::cerr << "failed to parse config file " << config
- << "! error" << ret << std::endl;
- ret = EXIT_FAILURE;
- break;
- }
- /*
- * next, we actually connect to the cluster
- */
- ret = rados.connect();
- if (ret < 0) {
- std::cerr << "couldn't connect to cluster! error " << ret << std::endl;
- ret = EXIT_FAILURE;
- break;
- }
- /*
- * And now we're done, so let's remove our pool and then
- * shut down the connection gracefully.
- */
- int delete_ret = rados.pool_delete(db_pool.c_str());
- if (delete_ret < 0) {
- // be careful not to
- std::cerr << "We failed to delete our test pool!" << db_pool << delete_ret << std::endl;
- ret = EXIT_FAILURE;
- }
- } while (0);
- }
- };
- TEST_F(EnvLibradosTest, Basics) {
- uint64_t file_size;
- std::unique_ptr<WritableFile> writable_file;
- std::vector<std::string> children;
- ASSERT_OK(env_->CreateDir("/dir"));
- // Check that the directory is empty.
- ASSERT_EQ(Status::NotFound(), env_->FileExists("/dir/non_existent"));
- ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok());
- ASSERT_OK(env_->GetChildren("/dir", &children));
- ASSERT_EQ(0U, children.size());
- // Create a file.
- ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file, soptions_));
- writable_file.reset();
- // Check that the file exists.
- ASSERT_OK(env_->FileExists("/dir/f"));
- ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
- ASSERT_EQ(0U, file_size);
- ASSERT_OK(env_->GetChildren("/dir", &children));
- ASSERT_EQ(1U, children.size());
- ASSERT_EQ("f", children[0]);
- // Write to the file.
- ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file, soptions_));
- ASSERT_OK(writable_file->Append("abc"));
- writable_file.reset();
- // Check for expected size.
- ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
- ASSERT_EQ(3U, file_size);
- // Check that renaming works.
- ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok());
- ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g"));
- ASSERT_EQ(Status::NotFound(), env_->FileExists("/dir/f"));
- ASSERT_OK(env_->FileExists("/dir/g"));
- ASSERT_OK(env_->GetFileSize("/dir/g", &file_size));
- ASSERT_EQ(3U, file_size);
- // Check that opening non-existent file fails.
- std::unique_ptr<SequentialFile> seq_file;
- std::unique_ptr<RandomAccessFile> rand_file;
- ASSERT_TRUE(
- !env_->NewSequentialFile("/dir/non_existent", &seq_file, soptions_).ok());
- ASSERT_TRUE(!seq_file);
- ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file,
- soptions_).ok());
- ASSERT_TRUE(!rand_file);
- // Check that deleting works.
- ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok());
- ASSERT_OK(env_->DeleteFile("/dir/g"));
- ASSERT_EQ(Status::NotFound(), env_->FileExists("/dir/g"));
- ASSERT_OK(env_->GetChildren("/dir", &children));
- ASSERT_EQ(0U, children.size());
- ASSERT_OK(env_->DeleteDir("/dir"));
- }
- TEST_F(EnvLibradosTest, ReadWrite) {
- std::unique_ptr<WritableFile> writable_file;
- std::unique_ptr<SequentialFile> seq_file;
- std::unique_ptr<RandomAccessFile> rand_file;
- Slice result;
- char scratch[100];
- ASSERT_OK(env_->CreateDir("/dir"));
- ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file, soptions_));
- ASSERT_OK(writable_file->Append("hello "));
- ASSERT_OK(writable_file->Append("world"));
- writable_file.reset();
- // Read sequentially.
- ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file, soptions_));
- ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello".
- ASSERT_EQ(0, result.compare("hello"));
- ASSERT_OK(seq_file->Skip(1));
- ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world".
- ASSERT_EQ(0, result.compare("world"));
- ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF.
- ASSERT_EQ(0U, result.size());
- ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file.
- ASSERT_OK(seq_file->Read(1000, &result, scratch));
- ASSERT_EQ(0U, result.size());
- // Random reads.
- ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file, soptions_));
- ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world".
- ASSERT_EQ(0, result.compare("world"));
- ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello".
- ASSERT_EQ(0, result.compare("hello"));
- ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d".
- ASSERT_EQ(0, result.compare("d"));
- // Too high offset.
- ASSERT_OK(rand_file->Read(1000, 5, &result, scratch));
- }
- TEST_F(EnvLibradosTest, Locks) {
- FileLock* lock = nullptr;
- std::unique_ptr<WritableFile> writable_file;
- ASSERT_OK(env_->CreateDir("/dir"));
- ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file, soptions_));
- // These are no-ops, but we test they return success.
- ASSERT_OK(env_->LockFile("some file", &lock));
- ASSERT_OK(env_->UnlockFile(lock));
- ASSERT_OK(env_->LockFile("/dir/f", &lock));
- ASSERT_OK(env_->UnlockFile(lock));
- }
- TEST_F(EnvLibradosTest, Misc) {
- std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
- ASSERT_TRUE(!test_dir.empty());
- std::unique_ptr<WritableFile> writable_file;
- ASSERT_TRUE(!env_->NewWritableFile("/a/b", &writable_file, soptions_).ok());
- ASSERT_OK(env_->NewWritableFile("/a", &writable_file, soptions_));
- // These are no-ops, but we test they return success.
- ASSERT_OK(writable_file->Sync());
- ASSERT_OK(writable_file->Flush());
- ASSERT_OK(writable_file->Close());
- writable_file.reset();
- }
- TEST_F(EnvLibradosTest, LargeWrite) {
- const size_t kWriteSize = 300 * 1024;
- char* scratch = new char[kWriteSize * 2];
- std::string write_data;
- for (size_t i = 0; i < kWriteSize; ++i) {
- write_data.append(1, 'h');
- }
- std::unique_ptr<WritableFile> writable_file;
- ASSERT_OK(env_->CreateDir("/dir"));
- ASSERT_OK(env_->NewWritableFile("/dir/g", &writable_file, soptions_));
- ASSERT_OK(writable_file->Append("foo"));
- ASSERT_OK(writable_file->Append(write_data));
- writable_file.reset();
- std::unique_ptr<SequentialFile> seq_file;
- Slice result;
- ASSERT_OK(env_->NewSequentialFile("/dir/g", &seq_file, soptions_));
- ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo".
- ASSERT_EQ(0, result.compare("foo"));
- size_t read = 0;
- std::string read_data;
- while (read < kWriteSize) {
- ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch));
- read_data.append(result.data(), result.size());
- read += result.size();
- }
- ASSERT_TRUE(write_data == read_data);
- delete[] scratch;
- }
- TEST_F(EnvLibradosTest, FrequentlySmallWrite) {
- const size_t kWriteSize = 1 << 10;
- char* scratch = new char[kWriteSize * 2];
- std::string write_data;
- for (size_t i = 0; i < kWriteSize; ++i) {
- write_data.append(1, 'h');
- }
- std::unique_ptr<WritableFile> writable_file;
- ASSERT_OK(env_->CreateDir("/dir"));
- ASSERT_OK(env_->NewWritableFile("/dir/g", &writable_file, soptions_));
- ASSERT_OK(writable_file->Append("foo"));
- for (size_t i = 0; i < kWriteSize; ++i) {
- ASSERT_OK(writable_file->Append("h"));
- }
- writable_file.reset();
- std::unique_ptr<SequentialFile> seq_file;
- Slice result;
- ASSERT_OK(env_->NewSequentialFile("/dir/g", &seq_file, soptions_));
- ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo".
- ASSERT_EQ(0, result.compare("foo"));
- size_t read = 0;
- std::string read_data;
- while (read < kWriteSize) {
- ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch));
- read_data.append(result.data(), result.size());
- read += result.size();
- }
- ASSERT_TRUE(write_data == read_data);
- delete[] scratch;
- }
- TEST_F(EnvLibradosTest, Truncate) {
- const size_t kWriteSize = 300 * 1024;
- const size_t truncSize = 1024;
- std::string write_data;
- for (size_t i = 0; i < kWriteSize; ++i) {
- write_data.append(1, 'h');
- }
- std::unique_ptr<WritableFile> writable_file;
- ASSERT_OK(env_->CreateDir("/dir"));
- ASSERT_OK(env_->NewWritableFile("/dir/g", &writable_file, soptions_));
- ASSERT_OK(writable_file->Append(write_data));
- ASSERT_EQ(writable_file->GetFileSize(), kWriteSize);
- ASSERT_OK(writable_file->Truncate(truncSize));
- ASSERT_EQ(writable_file->GetFileSize(), truncSize);
- writable_file.reset();
- }
- TEST_F(EnvLibradosTest, DBBasics) {
- std::string kDBPath = "/tmp/DBBasics";
- DB* db;
- Options options;
- // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
- options.IncreaseParallelism();
- options.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options.create_if_missing = true;
- options.env = env_;
- // open DB
- Status s = DB::Open(options, kDBPath, &db);
- assert(s.ok());
- // Put key-value
- s = db->Put(WriteOptions(), "key1", "value");
- assert(s.ok());
- std::string value;
- // get value
- s = db->Get(ReadOptions(), "key1", &value);
- assert(s.ok());
- assert(value == "value");
- // atomically apply a set of updates
- {
- WriteBatch batch;
- batch.Delete("key1");
- batch.Put("key2", value);
- s = db->Write(WriteOptions(), &batch);
- }
- s = db->Get(ReadOptions(), "key1", &value);
- assert(s.IsNotFound());
- db->Get(ReadOptions(), "key2", &value);
- assert(value == "value");
- delete db;
- }
- TEST_F(EnvLibradosTest, DBLoadKeysInRandomOrder) {
- char key[20] = {0}, value[20] = {0};
- int max_loop = 1 << 10;
- Timer timer(false);
- std::cout << "Test size : loop(" << max_loop << ")" << std::endl;
- /**********************************
- use default env
- ***********************************/
- std::string kDBPath1 = "/tmp/DBLoadKeysInRandomOrder1";
- DB* db1;
- Options options1;
- // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
- options1.IncreaseParallelism();
- options1.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options1.create_if_missing = true;
- // open DB
- Status s1 = DB::Open(options1, kDBPath1, &db1);
- assert(s1.ok());
- ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
- timer.Reset();
- for (int i = 0; i < max_loop; ++i) {
- snprintf(key,
- 20,
- "%16lx",
- (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
- // Put key-value
- s1 = db1->Put(WriteOptions(), key, value);
- assert(s1.ok());
- }
- std::cout << "Time by default : " << timer << "ms" << std::endl;
- delete db1;
- /**********************************
- use librados env
- ***********************************/
- std::string kDBPath2 = "/tmp/DBLoadKeysInRandomOrder2";
- DB* db2;
- Options options2;
- // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
- options2.IncreaseParallelism();
- options2.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options2.create_if_missing = true;
- options2.env = env_;
- // open DB
- Status s2 = DB::Open(options2, kDBPath2, &db2);
- assert(s2.ok());
- ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
- timer.Reset();
- for (int i = 0; i < max_loop; ++i) {
- snprintf(key,
- 20,
- "%16lx",
- (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
- // Put key-value
- s2 = db2->Put(WriteOptions(), key, value);
- assert(s2.ok());
- }
- std::cout << "Time by librados : " << timer << "ms" << std::endl;
- delete db2;
- }
- TEST_F(EnvLibradosTest, DBBulkLoadKeysInRandomOrder) {
- char key[20] = {0}, value[20] = {0};
- int max_loop = 1 << 6;
- int bulk_size = 1 << 15;
- Timer timer(false);
- std::cout << "Test size : loop(" << max_loop << "); bulk_size(" << bulk_size << ")" << std::endl;
- /**********************************
- use default env
- ***********************************/
- std::string kDBPath1 = "/tmp/DBBulkLoadKeysInRandomOrder1";
- DB* db1;
- Options options1;
- // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
- options1.IncreaseParallelism();
- options1.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options1.create_if_missing = true;
- // open DB
- Status s1 = DB::Open(options1, kDBPath1, &db1);
- assert(s1.ok());
- ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
- timer.Reset();
- for (int i = 0; i < max_loop; ++i) {
- WriteBatch batch;
- for (int j = 0; j < bulk_size; ++j) {
- snprintf(key,
- 20,
- "%16lx",
- (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
- batch.Put(key, value);
- }
- s1 = db1->Write(WriteOptions(), &batch);
- assert(s1.ok());
- }
- std::cout << "Time by default : " << timer << "ms" << std::endl;
- delete db1;
- /**********************************
- use librados env
- ***********************************/
- std::string kDBPath2 = "/tmp/DBBulkLoadKeysInRandomOrder2";
- DB* db2;
- Options options2;
- // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
- options2.IncreaseParallelism();
- options2.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options2.create_if_missing = true;
- options2.env = env_;
- // open DB
- Status s2 = DB::Open(options2, kDBPath2, &db2);
- assert(s2.ok());
- ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
- timer.Reset();
- for (int i = 0; i < max_loop; ++i) {
- WriteBatch batch;
- for (int j = 0; j < bulk_size; ++j) {
- snprintf(key,
- 20,
- "%16lx",
- (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
- batch.Put(key, value);
- }
- s2 = db2->Write(WriteOptions(), &batch);
- assert(s2.ok());
- }
- std::cout << "Time by librados : " << timer << "ms" << std::endl;
- delete db2;
- }
- TEST_F(EnvLibradosTest, DBBulkLoadKeysInSequentialOrder) {
- char key[20] = {0}, value[20] = {0};
- int max_loop = 1 << 6;
- int bulk_size = 1 << 15;
- Timer timer(false);
- std::cout << "Test size : loop(" << max_loop << "); bulk_size(" << bulk_size << ")" << std::endl;
- /**********************************
- use default env
- ***********************************/
- std::string kDBPath1 = "/tmp/DBBulkLoadKeysInSequentialOrder1";
- DB* db1;
- Options options1;
- // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
- options1.IncreaseParallelism();
- options1.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options1.create_if_missing = true;
- // open DB
- Status s1 = DB::Open(options1, kDBPath1, &db1);
- assert(s1.ok());
- ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
- timer.Reset();
- for (int i = 0; i < max_loop; ++i) {
- WriteBatch batch;
- for (int j = 0; j < bulk_size; ++j) {
- snprintf(key,
- 20,
- "%019lld",
- (long long)(i * bulk_size + j));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
- batch.Put(key, value);
- }
- s1 = db1->Write(WriteOptions(), &batch);
- assert(s1.ok());
- }
- std::cout << "Time by default : " << timer << "ms" << std::endl;
- delete db1;
- /**********************************
- use librados env
- ***********************************/
- std::string kDBPath2 = "/tmp/DBBulkLoadKeysInSequentialOrder2";
- DB* db2;
- Options options2;
- // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
- options2.IncreaseParallelism();
- options2.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options2.create_if_missing = true;
- options2.env = env_;
- // open DB
- Status s2 = DB::Open(options2, kDBPath2, &db2);
- assert(s2.ok());
- ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
- timer.Reset();
- for (int i = 0; i < max_loop; ++i) {
- WriteBatch batch;
- for (int j = 0; j < bulk_size; ++j) {
- snprintf(key,
- 20,
- "%16lx",
- (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
- batch.Put(key, value);
- }
- s2 = db2->Write(WriteOptions(), &batch);
- assert(s2.ok());
- }
- std::cout << "Time by librados : " << timer << "ms" << std::endl;
- delete db2;
- }
- TEST_F(EnvLibradosTest, DBRandomRead) {
- char key[20] = {0}, value[20] = {0};
- int max_loop = 1 << 6;
- int bulk_size = 1 << 10;
- int read_loop = 1 << 20;
- Timer timer(false);
- std::cout << "Test size : keys_num(" << max_loop << ", " << bulk_size << "); read_loop(" << read_loop << ")" << std::endl;
- /**********************************
- use default env
- ***********************************/
- std::string kDBPath1 = "/tmp/DBRandomRead1";
- DB* db1;
- Options options1;
- // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
- options1.IncreaseParallelism();
- options1.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options1.create_if_missing = true;
- // open DB
- Status s1 = DB::Open(options1, kDBPath1, &db1);
- assert(s1.ok());
- ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
- for (int i = 0; i < max_loop; ++i) {
- WriteBatch batch;
- for (int j = 0; j < bulk_size; ++j) {
- snprintf(key,
- 20,
- "%019lld",
- (long long)(i * bulk_size + j));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
- batch.Put(key, value);
- }
- s1 = db1->Write(WriteOptions(), &batch);
- assert(s1.ok());
- }
- timer.Reset();
- int base1 = 0, offset1 = 0;
- for (int i = 0; i < read_loop; ++i) {
- base1 = r1.Uniform(max_loop);
- offset1 = r1.Uniform(bulk_size);
- std::string value1;
- snprintf(key,
- 20,
- "%019lld",
- (long long)(base1 * bulk_size + offset1));
- s1 = db1->Get(ReadOptions(), key, &value1);
- assert(s1.ok());
- }
- std::cout << "Time by default : " << timer << "ms" << std::endl;
- delete db1;
- /**********************************
- use librados env
- ***********************************/
- std::string kDBPath2 = "/tmp/DBRandomRead2";
- DB* db2;
- Options options2;
- // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
- options2.IncreaseParallelism();
- options2.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options2.create_if_missing = true;
- options2.env = env_;
- // open DB
- Status s2 = DB::Open(options2, kDBPath2, &db2);
- assert(s2.ok());
- ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
- for (int i = 0; i < max_loop; ++i) {
- WriteBatch batch;
- for (int j = 0; j < bulk_size; ++j) {
- snprintf(key,
- 20,
- "%019lld",
- (long long)(i * bulk_size + j));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
- batch.Put(key, value);
- }
- s2 = db2->Write(WriteOptions(), &batch);
- assert(s2.ok());
- }
- timer.Reset();
- int base2 = 0, offset2 = 0;
- for (int i = 0; i < read_loop; ++i) {
- base2 = r2.Uniform(max_loop);
- offset2 = r2.Uniform(bulk_size);
- std::string value2;
- snprintf(key,
- 20,
- "%019lld",
- (long long)(base2 * bulk_size + offset2));
- s2 = db2->Get(ReadOptions(), key, &value2);
- if (!s2.ok()) {
- std::cout << s2.ToString() << std::endl;
- }
- assert(s2.ok());
- }
- std::cout << "Time by librados : " << timer << "ms" << std::endl;
- delete db2;
- }
- class EnvLibradosMutipoolTest : public testing::Test {
- public:
- // we will use all of these below
- const std::string client_name = "client.admin";
- const std::string cluster_name = "ceph";
- const uint64_t flags = 0;
- const std::string db_name = "env_librados_test_db";
- const std::string db_pool = db_name + "_pool";
- const std::string wal_dir = "/wal";
- const std::string wal_pool = db_name + "_wal_pool";
- const size_t write_buffer_size = 1 << 20;
- const char *keyring = "admin";
- const char *config = "../ceph/src/ceph.conf";
- EnvLibrados* env_;
- const EnvOptions soptions_;
- EnvLibradosMutipoolTest() {
- env_ = new EnvLibrados(client_name, cluster_name, flags, db_name, config, db_pool, wal_dir, wal_pool, write_buffer_size);
- }
- ~EnvLibradosMutipoolTest() {
- delete env_;
- librados::Rados rados;
- int ret = 0;
- do {
- ret = rados.init("admin"); // just use the client.admin keyring
- if (ret < 0) { // let's handle any error that might have come back
- std::cerr << "couldn't initialize rados! error " << ret << std::endl;
- ret = EXIT_FAILURE;
- break;
- }
- ret = rados.conf_read_file(config);
- if (ret < 0) {
- // This could fail if the config file is malformed, but it'd be hard.
- std::cerr << "failed to parse config file " << config
- << "! error" << ret << std::endl;
- ret = EXIT_FAILURE;
- break;
- }
- /*
- * next, we actually connect to the cluster
- */
- ret = rados.connect();
- if (ret < 0) {
- std::cerr << "couldn't connect to cluster! error " << ret << std::endl;
- ret = EXIT_FAILURE;
- break;
- }
- /*
- * And now we're done, so let's remove our pool and then
- * shut down the connection gracefully.
- */
- int delete_ret = rados.pool_delete(db_pool.c_str());
- if (delete_ret < 0) {
- // be careful not to
- std::cerr << "We failed to delete our test pool!" << db_pool << delete_ret << std::endl;
- ret = EXIT_FAILURE;
- }
- delete_ret = rados.pool_delete(wal_pool.c_str());
- if (delete_ret < 0) {
- // be careful not to
- std::cerr << "We failed to delete our test pool!" << wal_pool << delete_ret << std::endl;
- ret = EXIT_FAILURE;
- }
- } while (0);
- }
- };
- TEST_F(EnvLibradosMutipoolTest, Basics) {
- uint64_t file_size;
- std::unique_ptr<WritableFile> writable_file;
- std::vector<std::string> children;
- std::vector<std::string> v = {"/tmp/dir1", "/tmp/dir2", "/tmp/dir3", "/tmp/dir4", "dir"};
- for (size_t i = 0; i < v.size(); ++i) {
- std::string dir = v[i];
- std::string dir_non_existent = dir + "/non_existent";
- std::string dir_f = dir + "/f";
- std::string dir_g = dir + "/g";
- ASSERT_OK(env_->CreateDir(dir.c_str()));
- // Check that the directory is empty.
- ASSERT_EQ(Status::NotFound(), env_->FileExists(dir_non_existent.c_str()));
- ASSERT_TRUE(!env_->GetFileSize(dir_non_existent.c_str(), &file_size).ok());
- ASSERT_OK(env_->GetChildren(dir.c_str(), &children));
- ASSERT_EQ(0U, children.size());
- // Create a file.
- ASSERT_OK(env_->NewWritableFile(dir_f.c_str(), &writable_file, soptions_));
- writable_file.reset();
- // Check that the file exists.
- ASSERT_OK(env_->FileExists(dir_f.c_str()));
- ASSERT_OK(env_->GetFileSize(dir_f.c_str(), &file_size));
- ASSERT_EQ(0U, file_size);
- ASSERT_OK(env_->GetChildren(dir.c_str(), &children));
- ASSERT_EQ(1U, children.size());
- ASSERT_EQ("f", children[0]);
- // Write to the file.
- ASSERT_OK(env_->NewWritableFile(dir_f.c_str(), &writable_file, soptions_));
- ASSERT_OK(writable_file->Append("abc"));
- writable_file.reset();
- // Check for expected size.
- ASSERT_OK(env_->GetFileSize(dir_f.c_str(), &file_size));
- ASSERT_EQ(3U, file_size);
- // Check that renaming works.
- ASSERT_TRUE(!env_->RenameFile(dir_non_existent.c_str(), dir_g.c_str()).ok());
- ASSERT_OK(env_->RenameFile(dir_f.c_str(), dir_g.c_str()));
- ASSERT_EQ(Status::NotFound(), env_->FileExists(dir_f.c_str()));
- ASSERT_OK(env_->FileExists(dir_g.c_str()));
- ASSERT_OK(env_->GetFileSize(dir_g.c_str(), &file_size));
- ASSERT_EQ(3U, file_size);
- // Check that opening non-existent file fails.
- std::unique_ptr<SequentialFile> seq_file;
- std::unique_ptr<RandomAccessFile> rand_file;
- ASSERT_TRUE(
- !env_->NewSequentialFile(dir_non_existent.c_str(), &seq_file, soptions_).ok());
- ASSERT_TRUE(!seq_file);
- ASSERT_TRUE(!env_->NewRandomAccessFile(dir_non_existent.c_str(), &rand_file,
- soptions_).ok());
- ASSERT_TRUE(!rand_file);
- // Check that deleting works.
- ASSERT_TRUE(!env_->DeleteFile(dir_non_existent.c_str()).ok());
- ASSERT_OK(env_->DeleteFile(dir_g.c_str()));
- ASSERT_EQ(Status::NotFound(), env_->FileExists(dir_g.c_str()));
- ASSERT_OK(env_->GetChildren(dir.c_str(), &children));
- ASSERT_EQ(0U, children.size());
- ASSERT_OK(env_->DeleteDir(dir.c_str()));
- }
- }
- TEST_F(EnvLibradosMutipoolTest, DBBasics) {
- std::string kDBPath = "/tmp/DBBasics";
- std::string walPath = "/tmp/wal";
- DB* db;
- Options options;
- // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
- options.IncreaseParallelism();
- options.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options.create_if_missing = true;
- options.env = env_;
- options.wal_dir = walPath;
- // open DB
- Status s = DB::Open(options, kDBPath, &db);
- assert(s.ok());
- // Put key-value
- s = db->Put(WriteOptions(), "key1", "value");
- assert(s.ok());
- std::string value;
- // get value
- s = db->Get(ReadOptions(), "key1", &value);
- assert(s.ok());
- assert(value == "value");
- // atomically apply a set of updates
- {
- WriteBatch batch;
- batch.Delete("key1");
- batch.Put("key2", value);
- s = db->Write(WriteOptions(), &batch);
- }
- s = db->Get(ReadOptions(), "key1", &value);
- assert(s.IsNotFound());
- db->Get(ReadOptions(), "key2", &value);
- assert(value == "value");
- delete db;
- }
- TEST_F(EnvLibradosMutipoolTest, DBBulkLoadKeysInRandomOrder) {
- char key[20] = {0}, value[20] = {0};
- int max_loop = 1 << 6;
- int bulk_size = 1 << 15;
- Timer timer(false);
- std::cout << "Test size : loop(" << max_loop << "); bulk_size(" << bulk_size << ")" << std::endl;
- /**********************************
- use default env
- ***********************************/
- std::string kDBPath1 = "/tmp/DBBulkLoadKeysInRandomOrder1";
- std::string walPath = "/tmp/wal";
- DB* db1;
- Options options1;
- // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
- options1.IncreaseParallelism();
- options1.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options1.create_if_missing = true;
- // open DB
- Status s1 = DB::Open(options1, kDBPath1, &db1);
- assert(s1.ok());
- ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
- timer.Reset();
- for (int i = 0; i < max_loop; ++i) {
- WriteBatch batch;
- for (int j = 0; j < bulk_size; ++j) {
- snprintf(key,
- 20,
- "%16lx",
- (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
- batch.Put(key, value);
- }
- s1 = db1->Write(WriteOptions(), &batch);
- assert(s1.ok());
- }
- std::cout << "Time by default : " << timer << "ms" << std::endl;
- delete db1;
- /**********************************
- use librados env
- ***********************************/
- std::string kDBPath2 = "/tmp/DBBulkLoadKeysInRandomOrder2";
- DB* db2;
- Options options2;
- // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
- options2.IncreaseParallelism();
- options2.OptimizeLevelStyleCompaction();
- // create the DB if it's not already present
- options2.create_if_missing = true;
- options2.env = env_;
- options2.wal_dir = walPath;
- // open DB
- Status s2 = DB::Open(options2, kDBPath2, &db2);
- if (!s2.ok()) {
- std::cerr << s2.ToString() << std::endl;
- }
- assert(s2.ok());
- ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
- timer.Reset();
- for (int i = 0; i < max_loop; ++i) {
- WriteBatch batch;
- for (int j = 0; j < bulk_size; ++j) {
- snprintf(key,
- 20,
- "%16lx",
- (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
- snprintf(value,
- 20,
- "%16lx",
- (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
- batch.Put(key, value);
- }
- s2 = db2->Write(WriteOptions(), &batch);
- assert(s2.ok());
- }
- std::cout << "Time by librados : " << timer << "ms" << std::endl;
- delete db2;
- }
- TEST_F(EnvLibradosMutipoolTest, DBTransactionDB) {
- std::string kDBPath = "/tmp/DBTransactionDB";
- // open DB
- Options options;
- TransactionDBOptions txn_db_options;
- options.create_if_missing = true;
- options.env = env_;
- TransactionDB* txn_db;
- Status s = TransactionDB::Open(options, txn_db_options, kDBPath, &txn_db);
- assert(s.ok());
- WriteOptions write_options;
- ReadOptions read_options;
- TransactionOptions txn_options;
- std::string value;
- ////////////////////////////////////////////////////////
- //
- // Simple OptimisticTransaction Example ("Read Committed")
- //
- ////////////////////////////////////////////////////////
- // Start a transaction
- Transaction* txn = txn_db->BeginTransaction(write_options);
- assert(txn);
- // Read a key in this transaction
- s = txn->Get(read_options, "abc", &value);
- assert(s.IsNotFound());
- // Write a key in this transaction
- s = txn->Put("abc", "def");
- assert(s.ok());
- // Read a key OUTSIDE this transaction. Does not affect txn.
- s = txn_db->Get(read_options, "abc", &value);
- // Write a key OUTSIDE of this transaction.
- // Does not affect txn since this is an unrelated key. If we wrote key 'abc'
- // here, the transaction would fail to commit.
- s = txn_db->Put(write_options, "xyz", "zzz");
- // Commit transaction
- s = txn->Commit();
- assert(s.ok());
- delete txn;
- ////////////////////////////////////////////////////////
- //
- // "Repeatable Read" (Snapshot Isolation) Example
- // -- Using a single Snapshot
- //
- ////////////////////////////////////////////////////////
- // Set a snapshot at start of transaction by setting set_snapshot=true
- txn_options.set_snapshot = true;
- txn = txn_db->BeginTransaction(write_options, txn_options);
- const Snapshot* snapshot = txn->GetSnapshot();
- // Write a key OUTSIDE of transaction
- s = txn_db->Put(write_options, "abc", "xyz");
- assert(s.ok());
- // Attempt to read a key using the snapshot. This will fail since
- // the previous write outside this txn conflicts with this read.
- read_options.snapshot = snapshot;
- s = txn->GetForUpdate(read_options, "abc", &value);
- assert(s.IsBusy());
- txn->Rollback();
- delete txn;
- // Clear snapshot from read options since it is no longer valid
- read_options.snapshot = nullptr;
- snapshot = nullptr;
- ////////////////////////////////////////////////////////
- //
- // "Read Committed" (Monotonic Atomic Views) Example
- // --Using multiple Snapshots
- //
- ////////////////////////////////////////////////////////
- // In this example, we set the snapshot multiple times. This is probably
- // only necessary if you have very strict isolation requirements to
- // implement.
- // Set a snapshot at start of transaction
- txn_options.set_snapshot = true;
- txn = txn_db->BeginTransaction(write_options, txn_options);
- // Do some reads and writes to key "x"
- read_options.snapshot = txn_db->GetSnapshot();
- s = txn->Get(read_options, "x", &value);
- txn->Put("x", "x");
- // Do a write outside of the transaction to key "y"
- s = txn_db->Put(write_options, "y", "y");
- // Set a new snapshot in the transaction
- txn->SetSnapshot();
- txn->SetSavePoint();
- read_options.snapshot = txn_db->GetSnapshot();
- // Do some reads and writes to key "y"
- // Since the snapshot was advanced, the write done outside of the
- // transaction does not conflict.
- s = txn->GetForUpdate(read_options, "y", &value);
- txn->Put("y", "y");
- // Decide we want to revert the last write from this transaction.
- txn->RollbackToSavePoint();
- // Commit.
- s = txn->Commit();
- assert(s.ok());
- delete txn;
- // Clear snapshot from read options since it is no longer valid
- read_options.snapshot = nullptr;
- // Cleanup
- delete txn_db;
- DestroyDB(kDBPath, options);
- }
- } // namespace ROCKSDB_NAMESPACE
- int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
- }
- #else
- #include <stdio.h>
- int main(int argc, char** argv) {
- fprintf(stderr, "SKIPPED as EnvMirror is not supported in ROCKSDB_LITE\n");
- return 0;
- }
- #endif // !ROCKSDB_LITE
|