env_librados_test.cc 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  1. // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  2. // Copyright (c) 2016, Red Hat, Inc. All rights reserved.
  3. // This source code is licensed under both the GPLv2 (found in the
  4. // COPYING file in the root directory) and Apache 2.0 License
  5. // (found in the LICENSE.Apache file in the root directory).
  6. #ifndef ROCKSDB_LITE
  7. #include "rocksdb/utilities/env_librados.h"
  8. #include <rados/librados.hpp>
  9. #include "env/mock_env.h"
  10. #include "test_util/testharness.h"
  11. #include "rocksdb/db.h"
  12. #include "rocksdb/slice.h"
  13. #include "rocksdb/options.h"
  14. #include "util/random.h"
  15. #include <chrono>
  16. #include <ostream>
  17. #include "rocksdb/utilities/transaction_db.h"
  18. class Timer {
  19. typedef std::chrono::high_resolution_clock high_resolution_clock;
  20. typedef std::chrono::milliseconds milliseconds;
  21. public:
  22. explicit Timer(bool run = false)
  23. {
  24. if (run)
  25. Reset();
  26. }
  27. void Reset()
  28. {
  29. _start = high_resolution_clock::now();
  30. }
  31. milliseconds Elapsed() const
  32. {
  33. return std::chrono::duration_cast<milliseconds>(high_resolution_clock::now() - _start);
  34. }
  35. template <typename T, typename Traits>
  36. friend std::basic_ostream<T, Traits>& operator<<(std::basic_ostream<T, Traits>& out, const Timer& timer)
  37. {
  38. return out << timer.Elapsed().count();
  39. }
  40. private:
  41. high_resolution_clock::time_point _start;
  42. };
  43. namespace ROCKSDB_NAMESPACE {
  44. class EnvLibradosTest : public testing::Test {
  45. public:
  46. // we will use all of these below
  47. const std::string db_name = "env_librados_test_db";
  48. const std::string db_pool = db_name + "_pool";
  49. const char *keyring = "admin";
  50. const char *config = "../ceph/src/ceph.conf";
  51. EnvLibrados* env_;
  52. const EnvOptions soptions_;
  53. EnvLibradosTest()
  54. : env_(new EnvLibrados(db_name, config, db_pool)) {
  55. }
  56. ~EnvLibradosTest() {
  57. delete env_;
  58. librados::Rados rados;
  59. int ret = 0;
  60. do {
  61. ret = rados.init("admin"); // just use the client.admin keyring
  62. if (ret < 0) { // let's handle any error that might have come back
  63. std::cerr << "couldn't initialize rados! error " << ret << std::endl;
  64. ret = EXIT_FAILURE;
  65. break;
  66. }
  67. ret = rados.conf_read_file(config);
  68. if (ret < 0) {
  69. // This could fail if the config file is malformed, but it'd be hard.
  70. std::cerr << "failed to parse config file " << config
  71. << "! error" << ret << std::endl;
  72. ret = EXIT_FAILURE;
  73. break;
  74. }
  75. /*
  76. * next, we actually connect to the cluster
  77. */
  78. ret = rados.connect();
  79. if (ret < 0) {
  80. std::cerr << "couldn't connect to cluster! error " << ret << std::endl;
  81. ret = EXIT_FAILURE;
  82. break;
  83. }
  84. /*
  85. * And now we're done, so let's remove our pool and then
  86. * shut down the connection gracefully.
  87. */
  88. int delete_ret = rados.pool_delete(db_pool.c_str());
  89. if (delete_ret < 0) {
  90. // be careful not to
  91. std::cerr << "We failed to delete our test pool!" << db_pool << delete_ret << std::endl;
  92. ret = EXIT_FAILURE;
  93. }
  94. } while (0);
  95. }
  96. };
  97. TEST_F(EnvLibradosTest, Basics) {
  98. uint64_t file_size;
  99. std::unique_ptr<WritableFile> writable_file;
  100. std::vector<std::string> children;
  101. ASSERT_OK(env_->CreateDir("/dir"));
  102. // Check that the directory is empty.
  103. ASSERT_EQ(Status::NotFound(), env_->FileExists("/dir/non_existent"));
  104. ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok());
  105. ASSERT_OK(env_->GetChildren("/dir", &children));
  106. ASSERT_EQ(0U, children.size());
  107. // Create a file.
  108. ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file, soptions_));
  109. writable_file.reset();
  110. // Check that the file exists.
  111. ASSERT_OK(env_->FileExists("/dir/f"));
  112. ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
  113. ASSERT_EQ(0U, file_size);
  114. ASSERT_OK(env_->GetChildren("/dir", &children));
  115. ASSERT_EQ(1U, children.size());
  116. ASSERT_EQ("f", children[0]);
  117. // Write to the file.
  118. ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file, soptions_));
  119. ASSERT_OK(writable_file->Append("abc"));
  120. writable_file.reset();
  121. // Check for expected size.
  122. ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
  123. ASSERT_EQ(3U, file_size);
  124. // Check that renaming works.
  125. ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok());
  126. ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g"));
  127. ASSERT_EQ(Status::NotFound(), env_->FileExists("/dir/f"));
  128. ASSERT_OK(env_->FileExists("/dir/g"));
  129. ASSERT_OK(env_->GetFileSize("/dir/g", &file_size));
  130. ASSERT_EQ(3U, file_size);
  131. // Check that opening non-existent file fails.
  132. std::unique_ptr<SequentialFile> seq_file;
  133. std::unique_ptr<RandomAccessFile> rand_file;
  134. ASSERT_TRUE(
  135. !env_->NewSequentialFile("/dir/non_existent", &seq_file, soptions_).ok());
  136. ASSERT_TRUE(!seq_file);
  137. ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file,
  138. soptions_).ok());
  139. ASSERT_TRUE(!rand_file);
  140. // Check that deleting works.
  141. ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok());
  142. ASSERT_OK(env_->DeleteFile("/dir/g"));
  143. ASSERT_EQ(Status::NotFound(), env_->FileExists("/dir/g"));
  144. ASSERT_OK(env_->GetChildren("/dir", &children));
  145. ASSERT_EQ(0U, children.size());
  146. ASSERT_OK(env_->DeleteDir("/dir"));
  147. }
  148. TEST_F(EnvLibradosTest, ReadWrite) {
  149. std::unique_ptr<WritableFile> writable_file;
  150. std::unique_ptr<SequentialFile> seq_file;
  151. std::unique_ptr<RandomAccessFile> rand_file;
  152. Slice result;
  153. char scratch[100];
  154. ASSERT_OK(env_->CreateDir("/dir"));
  155. ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file, soptions_));
  156. ASSERT_OK(writable_file->Append("hello "));
  157. ASSERT_OK(writable_file->Append("world"));
  158. writable_file.reset();
  159. // Read sequentially.
  160. ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file, soptions_));
  161. ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello".
  162. ASSERT_EQ(0, result.compare("hello"));
  163. ASSERT_OK(seq_file->Skip(1));
  164. ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world".
  165. ASSERT_EQ(0, result.compare("world"));
  166. ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF.
  167. ASSERT_EQ(0U, result.size());
  168. ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file.
  169. ASSERT_OK(seq_file->Read(1000, &result, scratch));
  170. ASSERT_EQ(0U, result.size());
  171. // Random reads.
  172. ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file, soptions_));
  173. ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world".
  174. ASSERT_EQ(0, result.compare("world"));
  175. ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello".
  176. ASSERT_EQ(0, result.compare("hello"));
  177. ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d".
  178. ASSERT_EQ(0, result.compare("d"));
  179. // Too high offset.
  180. ASSERT_OK(rand_file->Read(1000, 5, &result, scratch));
  181. }
  182. TEST_F(EnvLibradosTest, Locks) {
  183. FileLock* lock = nullptr;
  184. std::unique_ptr<WritableFile> writable_file;
  185. ASSERT_OK(env_->CreateDir("/dir"));
  186. ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file, soptions_));
  187. // These are no-ops, but we test they return success.
  188. ASSERT_OK(env_->LockFile("some file", &lock));
  189. ASSERT_OK(env_->UnlockFile(lock));
  190. ASSERT_OK(env_->LockFile("/dir/f", &lock));
  191. ASSERT_OK(env_->UnlockFile(lock));
  192. }
  193. TEST_F(EnvLibradosTest, Misc) {
  194. std::string test_dir;
  195. ASSERT_OK(env_->GetTestDirectory(&test_dir));
  196. ASSERT_TRUE(!test_dir.empty());
  197. std::unique_ptr<WritableFile> writable_file;
  198. ASSERT_TRUE(!env_->NewWritableFile("/a/b", &writable_file, soptions_).ok());
  199. ASSERT_OK(env_->NewWritableFile("/a", &writable_file, soptions_));
  200. // These are no-ops, but we test they return success.
  201. ASSERT_OK(writable_file->Sync());
  202. ASSERT_OK(writable_file->Flush());
  203. ASSERT_OK(writable_file->Close());
  204. writable_file.reset();
  205. }
  206. TEST_F(EnvLibradosTest, LargeWrite) {
  207. const size_t kWriteSize = 300 * 1024;
  208. char* scratch = new char[kWriteSize * 2];
  209. std::string write_data;
  210. for (size_t i = 0; i < kWriteSize; ++i) {
  211. write_data.append(1, 'h');
  212. }
  213. std::unique_ptr<WritableFile> writable_file;
  214. ASSERT_OK(env_->CreateDir("/dir"));
  215. ASSERT_OK(env_->NewWritableFile("/dir/g", &writable_file, soptions_));
  216. ASSERT_OK(writable_file->Append("foo"));
  217. ASSERT_OK(writable_file->Append(write_data));
  218. writable_file.reset();
  219. std::unique_ptr<SequentialFile> seq_file;
  220. Slice result;
  221. ASSERT_OK(env_->NewSequentialFile("/dir/g", &seq_file, soptions_));
  222. ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo".
  223. ASSERT_EQ(0, result.compare("foo"));
  224. size_t read = 0;
  225. std::string read_data;
  226. while (read < kWriteSize) {
  227. ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch));
  228. read_data.append(result.data(), result.size());
  229. read += result.size();
  230. }
  231. ASSERT_TRUE(write_data == read_data);
  232. delete[] scratch;
  233. }
  234. TEST_F(EnvLibradosTest, FrequentlySmallWrite) {
  235. const size_t kWriteSize = 1 << 10;
  236. char* scratch = new char[kWriteSize * 2];
  237. std::string write_data;
  238. for (size_t i = 0; i < kWriteSize; ++i) {
  239. write_data.append(1, 'h');
  240. }
  241. std::unique_ptr<WritableFile> writable_file;
  242. ASSERT_OK(env_->CreateDir("/dir"));
  243. ASSERT_OK(env_->NewWritableFile("/dir/g", &writable_file, soptions_));
  244. ASSERT_OK(writable_file->Append("foo"));
  245. for (size_t i = 0; i < kWriteSize; ++i) {
  246. ASSERT_OK(writable_file->Append("h"));
  247. }
  248. writable_file.reset();
  249. std::unique_ptr<SequentialFile> seq_file;
  250. Slice result;
  251. ASSERT_OK(env_->NewSequentialFile("/dir/g", &seq_file, soptions_));
  252. ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo".
  253. ASSERT_EQ(0, result.compare("foo"));
  254. size_t read = 0;
  255. std::string read_data;
  256. while (read < kWriteSize) {
  257. ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch));
  258. read_data.append(result.data(), result.size());
  259. read += result.size();
  260. }
  261. ASSERT_TRUE(write_data == read_data);
  262. delete[] scratch;
  263. }
  264. TEST_F(EnvLibradosTest, Truncate) {
  265. const size_t kWriteSize = 300 * 1024;
  266. const size_t truncSize = 1024;
  267. std::string write_data;
  268. for (size_t i = 0; i < kWriteSize; ++i) {
  269. write_data.append(1, 'h');
  270. }
  271. std::unique_ptr<WritableFile> writable_file;
  272. ASSERT_OK(env_->CreateDir("/dir"));
  273. ASSERT_OK(env_->NewWritableFile("/dir/g", &writable_file, soptions_));
  274. ASSERT_OK(writable_file->Append(write_data));
  275. ASSERT_EQ(writable_file->GetFileSize(), kWriteSize);
  276. ASSERT_OK(writable_file->Truncate(truncSize));
  277. ASSERT_EQ(writable_file->GetFileSize(), truncSize);
  278. writable_file.reset();
  279. }
  280. TEST_F(EnvLibradosTest, DBBasics) {
  281. std::string kDBPath = "/tmp/DBBasics";
  282. DB* db;
  283. Options options;
  284. // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
  285. options.IncreaseParallelism();
  286. options.OptimizeLevelStyleCompaction();
  287. // create the DB if it's not already present
  288. options.create_if_missing = true;
  289. options.env = env_;
  290. // open DB
  291. Status s = DB::Open(options, kDBPath, &db);
  292. assert(s.ok());
  293. // Put key-value
  294. s = db->Put(WriteOptions(), "key1", "value");
  295. assert(s.ok());
  296. std::string value;
  297. // get value
  298. s = db->Get(ReadOptions(), "key1", &value);
  299. assert(s.ok());
  300. assert(value == "value");
  301. // atomically apply a set of updates
  302. {
  303. WriteBatch batch;
  304. batch.Delete("key1");
  305. batch.Put("key2", value);
  306. s = db->Write(WriteOptions(), &batch);
  307. }
  308. s = db->Get(ReadOptions(), "key1", &value);
  309. assert(s.IsNotFound());
  310. db->Get(ReadOptions(), "key2", &value);
  311. assert(value == "value");
  312. delete db;
  313. }
  314. TEST_F(EnvLibradosTest, DBLoadKeysInRandomOrder) {
  315. char key[20] = {0}, value[20] = {0};
  316. int max_loop = 1 << 10;
  317. Timer timer(false);
  318. std::cout << "Test size : loop(" << max_loop << ")" << std::endl;
  319. /**********************************
  320. use default env
  321. ***********************************/
  322. std::string kDBPath1 = "/tmp/DBLoadKeysInRandomOrder1";
  323. DB* db1;
  324. Options options1;
  325. // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
  326. options1.IncreaseParallelism();
  327. options1.OptimizeLevelStyleCompaction();
  328. // create the DB if it's not already present
  329. options1.create_if_missing = true;
  330. // open DB
  331. Status s1 = DB::Open(options1, kDBPath1, &db1);
  332. assert(s1.ok());
  333. ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
  334. timer.Reset();
  335. for (int i = 0; i < max_loop; ++i) {
  336. snprintf(key,
  337. 20,
  338. "%16lx",
  339. (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
  340. snprintf(value,
  341. 20,
  342. "%16lx",
  343. (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
  344. // Put key-value
  345. s1 = db1->Put(WriteOptions(), key, value);
  346. assert(s1.ok());
  347. }
  348. std::cout << "Time by default : " << timer << "ms" << std::endl;
  349. delete db1;
  350. /**********************************
  351. use librados env
  352. ***********************************/
  353. std::string kDBPath2 = "/tmp/DBLoadKeysInRandomOrder2";
  354. DB* db2;
  355. Options options2;
  356. // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
  357. options2.IncreaseParallelism();
  358. options2.OptimizeLevelStyleCompaction();
  359. // create the DB if it's not already present
  360. options2.create_if_missing = true;
  361. options2.env = env_;
  362. // open DB
  363. Status s2 = DB::Open(options2, kDBPath2, &db2);
  364. assert(s2.ok());
  365. ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
  366. timer.Reset();
  367. for (int i = 0; i < max_loop; ++i) {
  368. snprintf(key,
  369. 20,
  370. "%16lx",
  371. (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
  372. snprintf(value,
  373. 20,
  374. "%16lx",
  375. (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
  376. // Put key-value
  377. s2 = db2->Put(WriteOptions(), key, value);
  378. assert(s2.ok());
  379. }
  380. std::cout << "Time by librados : " << timer << "ms" << std::endl;
  381. delete db2;
  382. }
  383. TEST_F(EnvLibradosTest, DBBulkLoadKeysInRandomOrder) {
  384. char key[20] = {0}, value[20] = {0};
  385. int max_loop = 1 << 6;
  386. int bulk_size = 1 << 15;
  387. Timer timer(false);
  388. std::cout << "Test size : loop(" << max_loop << "); bulk_size(" << bulk_size << ")" << std::endl;
  389. /**********************************
  390. use default env
  391. ***********************************/
  392. std::string kDBPath1 = "/tmp/DBBulkLoadKeysInRandomOrder1";
  393. DB* db1;
  394. Options options1;
  395. // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
  396. options1.IncreaseParallelism();
  397. options1.OptimizeLevelStyleCompaction();
  398. // create the DB if it's not already present
  399. options1.create_if_missing = true;
  400. // open DB
  401. Status s1 = DB::Open(options1, kDBPath1, &db1);
  402. assert(s1.ok());
  403. ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
  404. timer.Reset();
  405. for (int i = 0; i < max_loop; ++i) {
  406. WriteBatch batch;
  407. for (int j = 0; j < bulk_size; ++j) {
  408. snprintf(key,
  409. 20,
  410. "%16lx",
  411. (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
  412. snprintf(value,
  413. 20,
  414. "%16lx",
  415. (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
  416. batch.Put(key, value);
  417. }
  418. s1 = db1->Write(WriteOptions(), &batch);
  419. assert(s1.ok());
  420. }
  421. std::cout << "Time by default : " << timer << "ms" << std::endl;
  422. delete db1;
  423. /**********************************
  424. use librados env
  425. ***********************************/
  426. std::string kDBPath2 = "/tmp/DBBulkLoadKeysInRandomOrder2";
  427. DB* db2;
  428. Options options2;
  429. // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
  430. options2.IncreaseParallelism();
  431. options2.OptimizeLevelStyleCompaction();
  432. // create the DB if it's not already present
  433. options2.create_if_missing = true;
  434. options2.env = env_;
  435. // open DB
  436. Status s2 = DB::Open(options2, kDBPath2, &db2);
  437. assert(s2.ok());
  438. ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
  439. timer.Reset();
  440. for (int i = 0; i < max_loop; ++i) {
  441. WriteBatch batch;
  442. for (int j = 0; j < bulk_size; ++j) {
  443. snprintf(key,
  444. 20,
  445. "%16lx",
  446. (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
  447. snprintf(value,
  448. 20,
  449. "%16lx",
  450. (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
  451. batch.Put(key, value);
  452. }
  453. s2 = db2->Write(WriteOptions(), &batch);
  454. assert(s2.ok());
  455. }
  456. std::cout << "Time by librados : " << timer << "ms" << std::endl;
  457. delete db2;
  458. }
  459. TEST_F(EnvLibradosTest, DBBulkLoadKeysInSequentialOrder) {
  460. char key[20] = {0}, value[20] = {0};
  461. int max_loop = 1 << 6;
  462. int bulk_size = 1 << 15;
  463. Timer timer(false);
  464. std::cout << "Test size : loop(" << max_loop << "); bulk_size(" << bulk_size << ")" << std::endl;
  465. /**********************************
  466. use default env
  467. ***********************************/
  468. std::string kDBPath1 = "/tmp/DBBulkLoadKeysInSequentialOrder1";
  469. DB* db1;
  470. Options options1;
  471. // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
  472. options1.IncreaseParallelism();
  473. options1.OptimizeLevelStyleCompaction();
  474. // create the DB if it's not already present
  475. options1.create_if_missing = true;
  476. // open DB
  477. Status s1 = DB::Open(options1, kDBPath1, &db1);
  478. assert(s1.ok());
  479. ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
  480. timer.Reset();
  481. for (int i = 0; i < max_loop; ++i) {
  482. WriteBatch batch;
  483. for (int j = 0; j < bulk_size; ++j) {
  484. snprintf(key,
  485. 20,
  486. "%019lld",
  487. (long long)(i * bulk_size + j));
  488. snprintf(value,
  489. 20,
  490. "%16lx",
  491. (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
  492. batch.Put(key, value);
  493. }
  494. s1 = db1->Write(WriteOptions(), &batch);
  495. assert(s1.ok());
  496. }
  497. std::cout << "Time by default : " << timer << "ms" << std::endl;
  498. delete db1;
  499. /**********************************
  500. use librados env
  501. ***********************************/
  502. std::string kDBPath2 = "/tmp/DBBulkLoadKeysInSequentialOrder2";
  503. DB* db2;
  504. Options options2;
  505. // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
  506. options2.IncreaseParallelism();
  507. options2.OptimizeLevelStyleCompaction();
  508. // create the DB if it's not already present
  509. options2.create_if_missing = true;
  510. options2.env = env_;
  511. // open DB
  512. Status s2 = DB::Open(options2, kDBPath2, &db2);
  513. assert(s2.ok());
  514. ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
  515. timer.Reset();
  516. for (int i = 0; i < max_loop; ++i) {
  517. WriteBatch batch;
  518. for (int j = 0; j < bulk_size; ++j) {
  519. snprintf(key,
  520. 20,
  521. "%16lx",
  522. (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
  523. snprintf(value,
  524. 20,
  525. "%16lx",
  526. (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
  527. batch.Put(key, value);
  528. }
  529. s2 = db2->Write(WriteOptions(), &batch);
  530. assert(s2.ok());
  531. }
  532. std::cout << "Time by librados : " << timer << "ms" << std::endl;
  533. delete db2;
  534. }
  535. TEST_F(EnvLibradosTest, DBRandomRead) {
  536. char key[20] = {0}, value[20] = {0};
  537. int max_loop = 1 << 6;
  538. int bulk_size = 1 << 10;
  539. int read_loop = 1 << 20;
  540. Timer timer(false);
  541. std::cout << "Test size : keys_num(" << max_loop << ", " << bulk_size << "); read_loop(" << read_loop << ")" << std::endl;
  542. /**********************************
  543. use default env
  544. ***********************************/
  545. std::string kDBPath1 = "/tmp/DBRandomRead1";
  546. DB* db1;
  547. Options options1;
  548. // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
  549. options1.IncreaseParallelism();
  550. options1.OptimizeLevelStyleCompaction();
  551. // create the DB if it's not already present
  552. options1.create_if_missing = true;
  553. // open DB
  554. Status s1 = DB::Open(options1, kDBPath1, &db1);
  555. assert(s1.ok());
  556. ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
  557. for (int i = 0; i < max_loop; ++i) {
  558. WriteBatch batch;
  559. for (int j = 0; j < bulk_size; ++j) {
  560. snprintf(key,
  561. 20,
  562. "%019lld",
  563. (long long)(i * bulk_size + j));
  564. snprintf(value,
  565. 20,
  566. "%16lx",
  567. (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
  568. batch.Put(key, value);
  569. }
  570. s1 = db1->Write(WriteOptions(), &batch);
  571. assert(s1.ok());
  572. }
  573. timer.Reset();
  574. int base1 = 0, offset1 = 0;
  575. for (int i = 0; i < read_loop; ++i) {
  576. base1 = r1.Uniform(max_loop);
  577. offset1 = r1.Uniform(bulk_size);
  578. std::string value1;
  579. snprintf(key,
  580. 20,
  581. "%019lld",
  582. (long long)(base1 * bulk_size + offset1));
  583. s1 = db1->Get(ReadOptions(), key, &value1);
  584. assert(s1.ok());
  585. }
  586. std::cout << "Time by default : " << timer << "ms" << std::endl;
  587. delete db1;
  588. /**********************************
  589. use librados env
  590. ***********************************/
  591. std::string kDBPath2 = "/tmp/DBRandomRead2";
  592. DB* db2;
  593. Options options2;
  594. // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
  595. options2.IncreaseParallelism();
  596. options2.OptimizeLevelStyleCompaction();
  597. // create the DB if it's not already present
  598. options2.create_if_missing = true;
  599. options2.env = env_;
  600. // open DB
  601. Status s2 = DB::Open(options2, kDBPath2, &db2);
  602. assert(s2.ok());
  603. ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
  604. for (int i = 0; i < max_loop; ++i) {
  605. WriteBatch batch;
  606. for (int j = 0; j < bulk_size; ++j) {
  607. snprintf(key,
  608. 20,
  609. "%019lld",
  610. (long long)(i * bulk_size + j));
  611. snprintf(value,
  612. 20,
  613. "%16lx",
  614. (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
  615. batch.Put(key, value);
  616. }
  617. s2 = db2->Write(WriteOptions(), &batch);
  618. assert(s2.ok());
  619. }
  620. timer.Reset();
  621. int base2 = 0, offset2 = 0;
  622. for (int i = 0; i < read_loop; ++i) {
  623. base2 = r2.Uniform(max_loop);
  624. offset2 = r2.Uniform(bulk_size);
  625. std::string value2;
  626. snprintf(key,
  627. 20,
  628. "%019lld",
  629. (long long)(base2 * bulk_size + offset2));
  630. s2 = db2->Get(ReadOptions(), key, &value2);
  631. if (!s2.ok()) {
  632. std::cout << s2.ToString() << std::endl;
  633. }
  634. assert(s2.ok());
  635. }
  636. std::cout << "Time by librados : " << timer << "ms" << std::endl;
  637. delete db2;
  638. }
  639. class EnvLibradosMutipoolTest : public testing::Test {
  640. public:
  641. // we will use all of these below
  642. const std::string client_name = "client.admin";
  643. const std::string cluster_name = "ceph";
  644. const uint64_t flags = 0;
  645. const std::string db_name = "env_librados_test_db";
  646. const std::string db_pool = db_name + "_pool";
  647. const std::string wal_dir = "/wal";
  648. const std::string wal_pool = db_name + "_wal_pool";
  649. const size_t write_buffer_size = 1 << 20;
  650. const char *keyring = "admin";
  651. const char *config = "../ceph/src/ceph.conf";
  652. EnvLibrados* env_;
  653. const EnvOptions soptions_;
  654. EnvLibradosMutipoolTest() {
  655. env_ = new EnvLibrados(client_name, cluster_name, flags, db_name, config, db_pool, wal_dir, wal_pool, write_buffer_size);
  656. }
  657. ~EnvLibradosMutipoolTest() {
  658. delete env_;
  659. librados::Rados rados;
  660. int ret = 0;
  661. do {
  662. ret = rados.init("admin"); // just use the client.admin keyring
  663. if (ret < 0) { // let's handle any error that might have come back
  664. std::cerr << "couldn't initialize rados! error " << ret << std::endl;
  665. ret = EXIT_FAILURE;
  666. break;
  667. }
  668. ret = rados.conf_read_file(config);
  669. if (ret < 0) {
  670. // This could fail if the config file is malformed, but it'd be hard.
  671. std::cerr << "failed to parse config file " << config
  672. << "! error" << ret << std::endl;
  673. ret = EXIT_FAILURE;
  674. break;
  675. }
  676. /*
  677. * next, we actually connect to the cluster
  678. */
  679. ret = rados.connect();
  680. if (ret < 0) {
  681. std::cerr << "couldn't connect to cluster! error " << ret << std::endl;
  682. ret = EXIT_FAILURE;
  683. break;
  684. }
  685. /*
  686. * And now we're done, so let's remove our pool and then
  687. * shut down the connection gracefully.
  688. */
  689. int delete_ret = rados.pool_delete(db_pool.c_str());
  690. if (delete_ret < 0) {
  691. // be careful not to
  692. std::cerr << "We failed to delete our test pool!" << db_pool << delete_ret << std::endl;
  693. ret = EXIT_FAILURE;
  694. }
  695. delete_ret = rados.pool_delete(wal_pool.c_str());
  696. if (delete_ret < 0) {
  697. // be careful not to
  698. std::cerr << "We failed to delete our test pool!" << wal_pool << delete_ret << std::endl;
  699. ret = EXIT_FAILURE;
  700. }
  701. } while (0);
  702. }
  703. };
  704. TEST_F(EnvLibradosMutipoolTest, Basics) {
  705. uint64_t file_size;
  706. std::unique_ptr<WritableFile> writable_file;
  707. std::vector<std::string> children;
  708. std::vector<std::string> v = {"/tmp/dir1", "/tmp/dir2", "/tmp/dir3", "/tmp/dir4", "dir"};
  709. for (size_t i = 0; i < v.size(); ++i) {
  710. std::string dir = v[i];
  711. std::string dir_non_existent = dir + "/non_existent";
  712. std::string dir_f = dir + "/f";
  713. std::string dir_g = dir + "/g";
  714. ASSERT_OK(env_->CreateDir(dir.c_str()));
  715. // Check that the directory is empty.
  716. ASSERT_EQ(Status::NotFound(), env_->FileExists(dir_non_existent.c_str()));
  717. ASSERT_TRUE(!env_->GetFileSize(dir_non_existent.c_str(), &file_size).ok());
  718. ASSERT_OK(env_->GetChildren(dir.c_str(), &children));
  719. ASSERT_EQ(0U, children.size());
  720. // Create a file.
  721. ASSERT_OK(env_->NewWritableFile(dir_f.c_str(), &writable_file, soptions_));
  722. writable_file.reset();
  723. // Check that the file exists.
  724. ASSERT_OK(env_->FileExists(dir_f.c_str()));
  725. ASSERT_OK(env_->GetFileSize(dir_f.c_str(), &file_size));
  726. ASSERT_EQ(0U, file_size);
  727. ASSERT_OK(env_->GetChildren(dir.c_str(), &children));
  728. ASSERT_EQ(1U, children.size());
  729. ASSERT_EQ("f", children[0]);
  730. // Write to the file.
  731. ASSERT_OK(env_->NewWritableFile(dir_f.c_str(), &writable_file, soptions_));
  732. ASSERT_OK(writable_file->Append("abc"));
  733. writable_file.reset();
  734. // Check for expected size.
  735. ASSERT_OK(env_->GetFileSize(dir_f.c_str(), &file_size));
  736. ASSERT_EQ(3U, file_size);
  737. // Check that renaming works.
  738. ASSERT_TRUE(!env_->RenameFile(dir_non_existent.c_str(), dir_g.c_str()).ok());
  739. ASSERT_OK(env_->RenameFile(dir_f.c_str(), dir_g.c_str()));
  740. ASSERT_EQ(Status::NotFound(), env_->FileExists(dir_f.c_str()));
  741. ASSERT_OK(env_->FileExists(dir_g.c_str()));
  742. ASSERT_OK(env_->GetFileSize(dir_g.c_str(), &file_size));
  743. ASSERT_EQ(3U, file_size);
  744. // Check that opening non-existent file fails.
  745. std::unique_ptr<SequentialFile> seq_file;
  746. std::unique_ptr<RandomAccessFile> rand_file;
  747. ASSERT_TRUE(
  748. !env_->NewSequentialFile(dir_non_existent.c_str(), &seq_file, soptions_).ok());
  749. ASSERT_TRUE(!seq_file);
  750. ASSERT_TRUE(!env_->NewRandomAccessFile(dir_non_existent.c_str(), &rand_file,
  751. soptions_).ok());
  752. ASSERT_TRUE(!rand_file);
  753. // Check that deleting works.
  754. ASSERT_TRUE(!env_->DeleteFile(dir_non_existent.c_str()).ok());
  755. ASSERT_OK(env_->DeleteFile(dir_g.c_str()));
  756. ASSERT_EQ(Status::NotFound(), env_->FileExists(dir_g.c_str()));
  757. ASSERT_OK(env_->GetChildren(dir.c_str(), &children));
  758. ASSERT_EQ(0U, children.size());
  759. ASSERT_OK(env_->DeleteDir(dir.c_str()));
  760. }
  761. }
  762. TEST_F(EnvLibradosMutipoolTest, DBBasics) {
  763. std::string kDBPath = "/tmp/DBBasics";
  764. std::string walPath = "/tmp/wal";
  765. DB* db;
  766. Options options;
  767. // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
  768. options.IncreaseParallelism();
  769. options.OptimizeLevelStyleCompaction();
  770. // create the DB if it's not already present
  771. options.create_if_missing = true;
  772. options.env = env_;
  773. options.wal_dir = walPath;
  774. // open DB
  775. Status s = DB::Open(options, kDBPath, &db);
  776. assert(s.ok());
  777. // Put key-value
  778. s = db->Put(WriteOptions(), "key1", "value");
  779. assert(s.ok());
  780. std::string value;
  781. // get value
  782. s = db->Get(ReadOptions(), "key1", &value);
  783. assert(s.ok());
  784. assert(value == "value");
  785. // atomically apply a set of updates
  786. {
  787. WriteBatch batch;
  788. batch.Delete("key1");
  789. batch.Put("key2", value);
  790. s = db->Write(WriteOptions(), &batch);
  791. }
  792. s = db->Get(ReadOptions(), "key1", &value);
  793. assert(s.IsNotFound());
  794. db->Get(ReadOptions(), "key2", &value);
  795. assert(value == "value");
  796. delete db;
  797. }
  798. TEST_F(EnvLibradosMutipoolTest, DBBulkLoadKeysInRandomOrder) {
  799. char key[20] = {0}, value[20] = {0};
  800. int max_loop = 1 << 6;
  801. int bulk_size = 1 << 15;
  802. Timer timer(false);
  803. std::cout << "Test size : loop(" << max_loop << "); bulk_size(" << bulk_size << ")" << std::endl;
  804. /**********************************
  805. use default env
  806. ***********************************/
  807. std::string kDBPath1 = "/tmp/DBBulkLoadKeysInRandomOrder1";
  808. std::string walPath = "/tmp/wal";
  809. DB* db1;
  810. Options options1;
  811. // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
  812. options1.IncreaseParallelism();
  813. options1.OptimizeLevelStyleCompaction();
  814. // create the DB if it's not already present
  815. options1.create_if_missing = true;
  816. // open DB
  817. Status s1 = DB::Open(options1, kDBPath1, &db1);
  818. assert(s1.ok());
  819. ROCKSDB_NAMESPACE::Random64 r1(time(nullptr));
  820. timer.Reset();
  821. for (int i = 0; i < max_loop; ++i) {
  822. WriteBatch batch;
  823. for (int j = 0; j < bulk_size; ++j) {
  824. snprintf(key,
  825. 20,
  826. "%16lx",
  827. (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
  828. snprintf(value,
  829. 20,
  830. "%16lx",
  831. (unsigned long)r1.Uniform(std::numeric_limits<uint64_t>::max()));
  832. batch.Put(key, value);
  833. }
  834. s1 = db1->Write(WriteOptions(), &batch);
  835. assert(s1.ok());
  836. }
  837. std::cout << "Time by default : " << timer << "ms" << std::endl;
  838. delete db1;
  839. /**********************************
  840. use librados env
  841. ***********************************/
  842. std::string kDBPath2 = "/tmp/DBBulkLoadKeysInRandomOrder2";
  843. DB* db2;
  844. Options options2;
  845. // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
  846. options2.IncreaseParallelism();
  847. options2.OptimizeLevelStyleCompaction();
  848. // create the DB if it's not already present
  849. options2.create_if_missing = true;
  850. options2.env = env_;
  851. options2.wal_dir = walPath;
  852. // open DB
  853. Status s2 = DB::Open(options2, kDBPath2, &db2);
  854. if (!s2.ok()) {
  855. std::cerr << s2.ToString() << std::endl;
  856. }
  857. assert(s2.ok());
  858. ROCKSDB_NAMESPACE::Random64 r2(time(nullptr));
  859. timer.Reset();
  860. for (int i = 0; i < max_loop; ++i) {
  861. WriteBatch batch;
  862. for (int j = 0; j < bulk_size; ++j) {
  863. snprintf(key,
  864. 20,
  865. "%16lx",
  866. (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
  867. snprintf(value,
  868. 20,
  869. "%16lx",
  870. (unsigned long)r2.Uniform(std::numeric_limits<uint64_t>::max()));
  871. batch.Put(key, value);
  872. }
  873. s2 = db2->Write(WriteOptions(), &batch);
  874. assert(s2.ok());
  875. }
  876. std::cout << "Time by librados : " << timer << "ms" << std::endl;
  877. delete db2;
  878. }
  879. TEST_F(EnvLibradosMutipoolTest, DBTransactionDB) {
  880. std::string kDBPath = "/tmp/DBTransactionDB";
  881. // open DB
  882. Options options;
  883. TransactionDBOptions txn_db_options;
  884. options.create_if_missing = true;
  885. options.env = env_;
  886. TransactionDB* txn_db;
  887. Status s = TransactionDB::Open(options, txn_db_options, kDBPath, &txn_db);
  888. assert(s.ok());
  889. WriteOptions write_options;
  890. ReadOptions read_options;
  891. TransactionOptions txn_options;
  892. std::string value;
  893. ////////////////////////////////////////////////////////
  894. //
  895. // Simple OptimisticTransaction Example ("Read Committed")
  896. //
  897. ////////////////////////////////////////////////////////
  898. // Start a transaction
  899. Transaction* txn = txn_db->BeginTransaction(write_options);
  900. assert(txn);
  901. // Read a key in this transaction
  902. s = txn->Get(read_options, "abc", &value);
  903. assert(s.IsNotFound());
  904. // Write a key in this transaction
  905. s = txn->Put("abc", "def");
  906. assert(s.ok());
  907. // Read a key OUTSIDE this transaction. Does not affect txn.
  908. s = txn_db->Get(read_options, "abc", &value);
  909. // Write a key OUTSIDE of this transaction.
  910. // Does not affect txn since this is an unrelated key. If we wrote key 'abc'
  911. // here, the transaction would fail to commit.
  912. s = txn_db->Put(write_options, "xyz", "zzz");
  913. // Commit transaction
  914. s = txn->Commit();
  915. assert(s.ok());
  916. delete txn;
  917. ////////////////////////////////////////////////////////
  918. //
  919. // "Repeatable Read" (Snapshot Isolation) Example
  920. // -- Using a single Snapshot
  921. //
  922. ////////////////////////////////////////////////////////
  923. // Set a snapshot at start of transaction by setting set_snapshot=true
  924. txn_options.set_snapshot = true;
  925. txn = txn_db->BeginTransaction(write_options, txn_options);
  926. const Snapshot* snapshot = txn->GetSnapshot();
  927. // Write a key OUTSIDE of transaction
  928. s = txn_db->Put(write_options, "abc", "xyz");
  929. assert(s.ok());
  930. // Attempt to read a key using the snapshot. This will fail since
  931. // the previous write outside this txn conflicts with this read.
  932. read_options.snapshot = snapshot;
  933. s = txn->GetForUpdate(read_options, "abc", &value);
  934. assert(s.IsBusy());
  935. txn->Rollback();
  936. delete txn;
  937. // Clear snapshot from read options since it is no longer valid
  938. read_options.snapshot = nullptr;
  939. snapshot = nullptr;
  940. ////////////////////////////////////////////////////////
  941. //
  942. // "Read Committed" (Monotonic Atomic Views) Example
  943. // --Using multiple Snapshots
  944. //
  945. ////////////////////////////////////////////////////////
  946. // In this example, we set the snapshot multiple times. This is probably
  947. // only necessary if you have very strict isolation requirements to
  948. // implement.
  949. // Set a snapshot at start of transaction
  950. txn_options.set_snapshot = true;
  951. txn = txn_db->BeginTransaction(write_options, txn_options);
  952. // Do some reads and writes to key "x"
  953. read_options.snapshot = txn_db->GetSnapshot();
  954. s = txn->Get(read_options, "x", &value);
  955. txn->Put("x", "x");
  956. // Do a write outside of the transaction to key "y"
  957. s = txn_db->Put(write_options, "y", "y");
  958. // Set a new snapshot in the transaction
  959. txn->SetSnapshot();
  960. txn->SetSavePoint();
  961. read_options.snapshot = txn_db->GetSnapshot();
  962. // Do some reads and writes to key "y"
  963. // Since the snapshot was advanced, the write done outside of the
  964. // transaction does not conflict.
  965. s = txn->GetForUpdate(read_options, "y", &value);
  966. txn->Put("y", "y");
  967. // Decide we want to revert the last write from this transaction.
  968. txn->RollbackToSavePoint();
  969. // Commit.
  970. s = txn->Commit();
  971. assert(s.ok());
  972. delete txn;
  973. // Clear snapshot from read options since it is no longer valid
  974. read_options.snapshot = nullptr;
  975. // Cleanup
  976. delete txn_db;
  977. DestroyDB(kDBPath, options);
  978. }
  979. } // namespace ROCKSDB_NAMESPACE
  980. int main(int argc, char** argv) {
  981. ::testing::InitGoogleTest(&argc, argv);
  982. return RUN_ALL_TESTS();
  983. }
  984. #else
  985. #include <stdio.h>
  986. int main(int argc, char** argv) {
  987. fprintf(stderr, "SKIPPED as EnvMirror is not supported in ROCKSDB_LITE\n");
  988. return 0;
  989. }
  990. #endif // !ROCKSDB_LITE