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).
9 #include "rocksdb/utilities/env_librados.h"
10 #include <rados/librados.hpp>
11 #include "env/mock_env.h"
12 #include "test_util/testharness.h"
14 #include "rocksdb/db.h"
15 #include "rocksdb/slice.h"
16 #include "rocksdb/options.h"
17 #include "util/random.h"
20 #include "rocksdb/utilities/transaction_db.h"
23 typedef std::chrono::high_resolution_clock high_resolution_clock
;
24 typedef std::chrono::milliseconds milliseconds
;
26 explicit Timer(bool run
= false)
33 _start
= high_resolution_clock::now();
35 milliseconds
Elapsed() const
37 return std::chrono::duration_cast
<milliseconds
>(high_resolution_clock::now() - _start
);
39 template <typename T
, typename Traits
>
40 friend std::basic_ostream
<T
, Traits
>& operator<<(std::basic_ostream
<T
, Traits
>& out
, const Timer
& timer
)
42 return out
<< timer
.Elapsed().count();
45 high_resolution_clock::time_point _start
;
48 namespace ROCKSDB_NAMESPACE
{
50 class EnvLibradosTest
: public testing::Test
{
52 // we will use all of these below
53 const std::string db_name
= "env_librados_test_db";
54 const std::string db_pool
= db_name
+ "_pool";
55 const char *keyring
= "admin";
56 const char *config
= "../ceph/src/ceph.conf";
59 const EnvOptions soptions_
;
62 : env_(new EnvLibrados(db_name
, config
, db_pool
)) {
66 librados::Rados rados
;
69 ret
= rados
.init("admin"); // just use the client.admin keyring
70 if (ret
< 0) { // let's handle any error that might have come back
71 std::cerr
<< "couldn't initialize rados! error " << ret
<< std::endl
;
76 ret
= rados
.conf_read_file(config
);
78 // This could fail if the config file is malformed, but it'd be hard.
79 std::cerr
<< "failed to parse config file " << config
80 << "! error" << ret
<< std::endl
;
86 * next, we actually connect to the cluster
89 ret
= rados
.connect();
91 std::cerr
<< "couldn't connect to cluster! error " << ret
<< std::endl
;
97 * And now we're done, so let's remove our pool and then
98 * shut down the connection gracefully.
100 int delete_ret
= rados
.pool_delete(db_pool
.c_str());
101 if (delete_ret
< 0) {
103 std::cerr
<< "We failed to delete our test pool!" << db_pool
<< delete_ret
<< std::endl
;
110 TEST_F(EnvLibradosTest
, Basics
) {
112 std::unique_ptr
<WritableFile
> writable_file
;
113 std::vector
<std::string
> children
;
115 ASSERT_OK(env_
->CreateDir("/dir"));
116 // Check that the directory is empty.
117 ASSERT_EQ(Status::NotFound(), env_
->FileExists("/dir/non_existent"));
118 ASSERT_TRUE(!env_
->GetFileSize("/dir/non_existent", &file_size
).ok());
119 ASSERT_OK(env_
->GetChildren("/dir", &children
));
120 ASSERT_EQ(0U, children
.size());
123 ASSERT_OK(env_
->NewWritableFile("/dir/f", &writable_file
, soptions_
));
124 writable_file
.reset();
126 // Check that the file exists.
127 ASSERT_OK(env_
->FileExists("/dir/f"));
128 ASSERT_OK(env_
->GetFileSize("/dir/f", &file_size
));
129 ASSERT_EQ(0U, file_size
);
130 ASSERT_OK(env_
->GetChildren("/dir", &children
));
131 ASSERT_EQ(1U, children
.size());
132 ASSERT_EQ("f", children
[0]);
134 // Write to the file.
135 ASSERT_OK(env_
->NewWritableFile("/dir/f", &writable_file
, soptions_
));
136 ASSERT_OK(writable_file
->Append("abc"));
137 writable_file
.reset();
140 // Check for expected size.
141 ASSERT_OK(env_
->GetFileSize("/dir/f", &file_size
));
142 ASSERT_EQ(3U, file_size
);
145 // Check that renaming works.
146 ASSERT_TRUE(!env_
->RenameFile("/dir/non_existent", "/dir/g").ok());
147 ASSERT_OK(env_
->RenameFile("/dir/f", "/dir/g"));
148 ASSERT_EQ(Status::NotFound(), env_
->FileExists("/dir/f"));
149 ASSERT_OK(env_
->FileExists("/dir/g"));
150 ASSERT_OK(env_
->GetFileSize("/dir/g", &file_size
));
151 ASSERT_EQ(3U, file_size
);
153 // Check that opening non-existent file fails.
154 std::unique_ptr
<SequentialFile
> seq_file
;
155 std::unique_ptr
<RandomAccessFile
> rand_file
;
157 !env_
->NewSequentialFile("/dir/non_existent", &seq_file
, soptions_
).ok());
158 ASSERT_TRUE(!seq_file
);
159 ASSERT_TRUE(!env_
->NewRandomAccessFile("/dir/non_existent", &rand_file
,
161 ASSERT_TRUE(!rand_file
);
163 // Check that deleting works.
164 ASSERT_TRUE(!env_
->DeleteFile("/dir/non_existent").ok());
165 ASSERT_OK(env_
->DeleteFile("/dir/g"));
166 ASSERT_EQ(Status::NotFound(), env_
->FileExists("/dir/g"));
167 ASSERT_OK(env_
->GetChildren("/dir", &children
));
168 ASSERT_EQ(0U, children
.size());
169 ASSERT_OK(env_
->DeleteDir("/dir"));
172 TEST_F(EnvLibradosTest
, ReadWrite
) {
173 std::unique_ptr
<WritableFile
> writable_file
;
174 std::unique_ptr
<SequentialFile
> seq_file
;
175 std::unique_ptr
<RandomAccessFile
> rand_file
;
179 ASSERT_OK(env_
->CreateDir("/dir"));
181 ASSERT_OK(env_
->NewWritableFile("/dir/f", &writable_file
, soptions_
));
182 ASSERT_OK(writable_file
->Append("hello "));
183 ASSERT_OK(writable_file
->Append("world"));
184 writable_file
.reset();
186 // Read sequentially.
187 ASSERT_OK(env_
->NewSequentialFile("/dir/f", &seq_file
, soptions_
));
188 ASSERT_OK(seq_file
->Read(5, &result
, scratch
)); // Read "hello".
189 ASSERT_EQ(0, result
.compare("hello"));
190 ASSERT_OK(seq_file
->Skip(1));
191 ASSERT_OK(seq_file
->Read(1000, &result
, scratch
)); // Read "world".
192 ASSERT_EQ(0, result
.compare("world"));
193 ASSERT_OK(seq_file
->Read(1000, &result
, scratch
)); // Try reading past EOF.
194 ASSERT_EQ(0U, result
.size());
195 ASSERT_OK(seq_file
->Skip(100)); // Try to skip past end of file.
196 ASSERT_OK(seq_file
->Read(1000, &result
, scratch
));
197 ASSERT_EQ(0U, result
.size());
200 ASSERT_OK(env_
->NewRandomAccessFile("/dir/f", &rand_file
, soptions_
));
201 ASSERT_OK(rand_file
->Read(6, 5, &result
, scratch
)); // Read "world".
202 ASSERT_EQ(0, result
.compare("world"));
203 ASSERT_OK(rand_file
->Read(0, 5, &result
, scratch
)); // Read "hello".
204 ASSERT_EQ(0, result
.compare("hello"));
205 ASSERT_OK(rand_file
->Read(10, 100, &result
, scratch
)); // Read "d".
206 ASSERT_EQ(0, result
.compare("d"));
209 ASSERT_OK(rand_file
->Read(1000, 5, &result
, scratch
));
212 TEST_F(EnvLibradosTest
, Locks
) {
213 FileLock
* lock
= nullptr;
214 std::unique_ptr
<WritableFile
> writable_file
;
216 ASSERT_OK(env_
->CreateDir("/dir"));
218 ASSERT_OK(env_
->NewWritableFile("/dir/f", &writable_file
, soptions_
));
220 // These are no-ops, but we test they return success.
221 ASSERT_OK(env_
->LockFile("some file", &lock
));
222 ASSERT_OK(env_
->UnlockFile(lock
));
224 ASSERT_OK(env_
->LockFile("/dir/f", &lock
));
225 ASSERT_OK(env_
->UnlockFile(lock
));
228 TEST_F(EnvLibradosTest
, Misc
) {
229 std::string test_dir
;
230 ASSERT_OK(env_
->GetTestDirectory(&test_dir
));
231 ASSERT_TRUE(!test_dir
.empty());
233 std::unique_ptr
<WritableFile
> writable_file
;
234 ASSERT_TRUE(!env_
->NewWritableFile("/a/b", &writable_file
, soptions_
).ok());
236 ASSERT_OK(env_
->NewWritableFile("/a", &writable_file
, soptions_
));
237 // These are no-ops, but we test they return success.
238 ASSERT_OK(writable_file
->Sync());
239 ASSERT_OK(writable_file
->Flush());
240 ASSERT_OK(writable_file
->Close());
241 writable_file
.reset();
244 TEST_F(EnvLibradosTest
, LargeWrite
) {
245 const size_t kWriteSize
= 300 * 1024;
246 char* scratch
= new char[kWriteSize
* 2];
248 std::string write_data
;
249 for (size_t i
= 0; i
< kWriteSize
; ++i
) {
250 write_data
.append(1, 'h');
253 std::unique_ptr
<WritableFile
> writable_file
;
254 ASSERT_OK(env_
->CreateDir("/dir"));
255 ASSERT_OK(env_
->NewWritableFile("/dir/g", &writable_file
, soptions_
));
256 ASSERT_OK(writable_file
->Append("foo"));
257 ASSERT_OK(writable_file
->Append(write_data
));
258 writable_file
.reset();
260 std::unique_ptr
<SequentialFile
> seq_file
;
262 ASSERT_OK(env_
->NewSequentialFile("/dir/g", &seq_file
, soptions_
));
263 ASSERT_OK(seq_file
->Read(3, &result
, scratch
)); // Read "foo".
264 ASSERT_EQ(0, result
.compare("foo"));
267 std::string read_data
;
268 while (read
< kWriteSize
) {
269 ASSERT_OK(seq_file
->Read(kWriteSize
- read
, &result
, scratch
));
270 read_data
.append(result
.data(), result
.size());
271 read
+= result
.size();
273 ASSERT_TRUE(write_data
== read_data
);
277 TEST_F(EnvLibradosTest
, FrequentlySmallWrite
) {
278 const size_t kWriteSize
= 1 << 10;
279 char* scratch
= new char[kWriteSize
* 2];
281 std::string write_data
;
282 for (size_t i
= 0; i
< kWriteSize
; ++i
) {
283 write_data
.append(1, 'h');
286 std::unique_ptr
<WritableFile
> writable_file
;
287 ASSERT_OK(env_
->CreateDir("/dir"));
288 ASSERT_OK(env_
->NewWritableFile("/dir/g", &writable_file
, soptions_
));
289 ASSERT_OK(writable_file
->Append("foo"));
291 for (size_t i
= 0; i
< kWriteSize
; ++i
) {
292 ASSERT_OK(writable_file
->Append("h"));
294 writable_file
.reset();
296 std::unique_ptr
<SequentialFile
> seq_file
;
298 ASSERT_OK(env_
->NewSequentialFile("/dir/g", &seq_file
, soptions_
));
299 ASSERT_OK(seq_file
->Read(3, &result
, scratch
)); // Read "foo".
300 ASSERT_EQ(0, result
.compare("foo"));
303 std::string read_data
;
304 while (read
< kWriteSize
) {
305 ASSERT_OK(seq_file
->Read(kWriteSize
- read
, &result
, scratch
));
306 read_data
.append(result
.data(), result
.size());
307 read
+= result
.size();
309 ASSERT_TRUE(write_data
== read_data
);
313 TEST_F(EnvLibradosTest
, Truncate
) {
314 const size_t kWriteSize
= 300 * 1024;
315 const size_t truncSize
= 1024;
316 std::string write_data
;
317 for (size_t i
= 0; i
< kWriteSize
; ++i
) {
318 write_data
.append(1, 'h');
321 std::unique_ptr
<WritableFile
> writable_file
;
322 ASSERT_OK(env_
->CreateDir("/dir"));
323 ASSERT_OK(env_
->NewWritableFile("/dir/g", &writable_file
, soptions_
));
324 ASSERT_OK(writable_file
->Append(write_data
));
325 ASSERT_EQ(writable_file
->GetFileSize(), kWriteSize
);
326 ASSERT_OK(writable_file
->Truncate(truncSize
));
327 ASSERT_EQ(writable_file
->GetFileSize(), truncSize
);
328 writable_file
.reset();
331 TEST_F(EnvLibradosTest
, DBBasics
) {
332 std::string kDBPath
= "/tmp/DBBasics";
335 // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
336 options
.IncreaseParallelism();
337 options
.OptimizeLevelStyleCompaction();
338 // create the DB if it's not already present
339 options
.create_if_missing
= true;
343 Status s
= DB::Open(options
, kDBPath
, &db
);
347 s
= db
->Put(WriteOptions(), "key1", "value");
351 s
= db
->Get(ReadOptions(), "key1", &value
);
353 assert(value
== "value");
355 // atomically apply a set of updates
358 batch
.Delete("key1");
359 batch
.Put("key2", value
);
360 s
= db
->Write(WriteOptions(), &batch
);
363 s
= db
->Get(ReadOptions(), "key1", &value
);
364 assert(s
.IsNotFound());
366 db
->Get(ReadOptions(), "key2", &value
);
367 assert(value
== "value");
372 TEST_F(EnvLibradosTest
, DBLoadKeysInRandomOrder
) {
373 char key
[20] = {0}, value
[20] = {0};
374 int max_loop
= 1 << 10;
376 std::cout
<< "Test size : loop(" << max_loop
<< ")" << std::endl
;
377 /**********************************
379 ***********************************/
380 std::string kDBPath1
= "/tmp/DBLoadKeysInRandomOrder1";
383 // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
384 options1
.IncreaseParallelism();
385 options1
.OptimizeLevelStyleCompaction();
386 // create the DB if it's not already present
387 options1
.create_if_missing
= true;
390 Status s1
= DB::Open(options1
, kDBPath1
, &db1
);
393 ROCKSDB_NAMESPACE::Random64
r1(time(nullptr));
396 for (int i
= 0; i
< max_loop
; ++i
) {
400 (unsigned long)r1
.Uniform(std::numeric_limits
<uint64_t>::max()));
404 (unsigned long)r1
.Uniform(std::numeric_limits
<uint64_t>::max()));
406 s1
= db1
->Put(WriteOptions(), key
, value
);
409 std::cout
<< "Time by default : " << timer
<< "ms" << std::endl
;
412 /**********************************
414 ***********************************/
415 std::string kDBPath2
= "/tmp/DBLoadKeysInRandomOrder2";
418 // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
419 options2
.IncreaseParallelism();
420 options2
.OptimizeLevelStyleCompaction();
421 // create the DB if it's not already present
422 options2
.create_if_missing
= true;
426 Status s2
= DB::Open(options2
, kDBPath2
, &db2
);
429 ROCKSDB_NAMESPACE::Random64
r2(time(nullptr));
432 for (int i
= 0; i
< max_loop
; ++i
) {
436 (unsigned long)r2
.Uniform(std::numeric_limits
<uint64_t>::max()));
440 (unsigned long)r2
.Uniform(std::numeric_limits
<uint64_t>::max()));
442 s2
= db2
->Put(WriteOptions(), key
, value
);
445 std::cout
<< "Time by librados : " << timer
<< "ms" << std::endl
;
449 TEST_F(EnvLibradosTest
, DBBulkLoadKeysInRandomOrder
) {
450 char key
[20] = {0}, value
[20] = {0};
451 int max_loop
= 1 << 6;
452 int bulk_size
= 1 << 15;
454 std::cout
<< "Test size : loop(" << max_loop
<< "); bulk_size(" << bulk_size
<< ")" << std::endl
;
455 /**********************************
457 ***********************************/
458 std::string kDBPath1
= "/tmp/DBBulkLoadKeysInRandomOrder1";
461 // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
462 options1
.IncreaseParallelism();
463 options1
.OptimizeLevelStyleCompaction();
464 // create the DB if it's not already present
465 options1
.create_if_missing
= true;
468 Status s1
= DB::Open(options1
, kDBPath1
, &db1
);
471 ROCKSDB_NAMESPACE::Random64
r1(time(nullptr));
474 for (int i
= 0; i
< max_loop
; ++i
) {
476 for (int j
= 0; j
< bulk_size
; ++j
) {
480 (unsigned long)r1
.Uniform(std::numeric_limits
<uint64_t>::max()));
484 (unsigned long)r1
.Uniform(std::numeric_limits
<uint64_t>::max()));
485 batch
.Put(key
, value
);
487 s1
= db1
->Write(WriteOptions(), &batch
);
490 std::cout
<< "Time by default : " << timer
<< "ms" << std::endl
;
493 /**********************************
495 ***********************************/
496 std::string kDBPath2
= "/tmp/DBBulkLoadKeysInRandomOrder2";
499 // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
500 options2
.IncreaseParallelism();
501 options2
.OptimizeLevelStyleCompaction();
502 // create the DB if it's not already present
503 options2
.create_if_missing
= true;
507 Status s2
= DB::Open(options2
, kDBPath2
, &db2
);
510 ROCKSDB_NAMESPACE::Random64
r2(time(nullptr));
513 for (int i
= 0; i
< max_loop
; ++i
) {
515 for (int j
= 0; j
< bulk_size
; ++j
) {
519 (unsigned long)r2
.Uniform(std::numeric_limits
<uint64_t>::max()));
523 (unsigned long)r2
.Uniform(std::numeric_limits
<uint64_t>::max()));
524 batch
.Put(key
, value
);
526 s2
= db2
->Write(WriteOptions(), &batch
);
529 std::cout
<< "Time by librados : " << timer
<< "ms" << std::endl
;
533 TEST_F(EnvLibradosTest
, DBBulkLoadKeysInSequentialOrder
) {
534 char key
[20] = {0}, value
[20] = {0};
535 int max_loop
= 1 << 6;
536 int bulk_size
= 1 << 15;
538 std::cout
<< "Test size : loop(" << max_loop
<< "); bulk_size(" << bulk_size
<< ")" << std::endl
;
539 /**********************************
541 ***********************************/
542 std::string kDBPath1
= "/tmp/DBBulkLoadKeysInSequentialOrder1";
545 // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
546 options1
.IncreaseParallelism();
547 options1
.OptimizeLevelStyleCompaction();
548 // create the DB if it's not already present
549 options1
.create_if_missing
= true;
552 Status s1
= DB::Open(options1
, kDBPath1
, &db1
);
555 ROCKSDB_NAMESPACE::Random64
r1(time(nullptr));
558 for (int i
= 0; i
< max_loop
; ++i
) {
560 for (int j
= 0; j
< bulk_size
; ++j
) {
564 (long long)(i
* bulk_size
+ j
));
568 (unsigned long)r1
.Uniform(std::numeric_limits
<uint64_t>::max()));
569 batch
.Put(key
, value
);
571 s1
= db1
->Write(WriteOptions(), &batch
);
574 std::cout
<< "Time by default : " << timer
<< "ms" << std::endl
;
577 /**********************************
579 ***********************************/
580 std::string kDBPath2
= "/tmp/DBBulkLoadKeysInSequentialOrder2";
583 // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
584 options2
.IncreaseParallelism();
585 options2
.OptimizeLevelStyleCompaction();
586 // create the DB if it's not already present
587 options2
.create_if_missing
= true;
591 Status s2
= DB::Open(options2
, kDBPath2
, &db2
);
594 ROCKSDB_NAMESPACE::Random64
r2(time(nullptr));
597 for (int i
= 0; i
< max_loop
; ++i
) {
599 for (int j
= 0; j
< bulk_size
; ++j
) {
603 (unsigned long)r2
.Uniform(std::numeric_limits
<uint64_t>::max()));
607 (unsigned long)r2
.Uniform(std::numeric_limits
<uint64_t>::max()));
608 batch
.Put(key
, value
);
610 s2
= db2
->Write(WriteOptions(), &batch
);
613 std::cout
<< "Time by librados : " << timer
<< "ms" << std::endl
;
617 TEST_F(EnvLibradosTest
, DBRandomRead
) {
618 char key
[20] = {0}, value
[20] = {0};
619 int max_loop
= 1 << 6;
620 int bulk_size
= 1 << 10;
621 int read_loop
= 1 << 20;
623 std::cout
<< "Test size : keys_num(" << max_loop
<< ", " << bulk_size
<< "); read_loop(" << read_loop
<< ")" << std::endl
;
624 /**********************************
626 ***********************************/
627 std::string kDBPath1
= "/tmp/DBRandomRead1";
630 // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
631 options1
.IncreaseParallelism();
632 options1
.OptimizeLevelStyleCompaction();
633 // create the DB if it's not already present
634 options1
.create_if_missing
= true;
637 Status s1
= DB::Open(options1
, kDBPath1
, &db1
);
640 ROCKSDB_NAMESPACE::Random64
r1(time(nullptr));
642 for (int i
= 0; i
< max_loop
; ++i
) {
644 for (int j
= 0; j
< bulk_size
; ++j
) {
648 (long long)(i
* bulk_size
+ j
));
652 (unsigned long)r1
.Uniform(std::numeric_limits
<uint64_t>::max()));
653 batch
.Put(key
, value
);
655 s1
= db1
->Write(WriteOptions(), &batch
);
659 int base1
= 0, offset1
= 0;
660 for (int i
= 0; i
< read_loop
; ++i
) {
661 base1
= r1
.Uniform(max_loop
);
662 offset1
= r1
.Uniform(bulk_size
);
667 (long long)(base1
* bulk_size
+ offset1
));
668 s1
= db1
->Get(ReadOptions(), key
, &value1
);
671 std::cout
<< "Time by default : " << timer
<< "ms" << std::endl
;
674 /**********************************
676 ***********************************/
677 std::string kDBPath2
= "/tmp/DBRandomRead2";
680 // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
681 options2
.IncreaseParallelism();
682 options2
.OptimizeLevelStyleCompaction();
683 // create the DB if it's not already present
684 options2
.create_if_missing
= true;
688 Status s2
= DB::Open(options2
, kDBPath2
, &db2
);
691 ROCKSDB_NAMESPACE::Random64
r2(time(nullptr));
693 for (int i
= 0; i
< max_loop
; ++i
) {
695 for (int j
= 0; j
< bulk_size
; ++j
) {
699 (long long)(i
* bulk_size
+ j
));
703 (unsigned long)r2
.Uniform(std::numeric_limits
<uint64_t>::max()));
704 batch
.Put(key
, value
);
706 s2
= db2
->Write(WriteOptions(), &batch
);
711 int base2
= 0, offset2
= 0;
712 for (int i
= 0; i
< read_loop
; ++i
) {
713 base2
= r2
.Uniform(max_loop
);
714 offset2
= r2
.Uniform(bulk_size
);
719 (long long)(base2
* bulk_size
+ offset2
));
720 s2
= db2
->Get(ReadOptions(), key
, &value2
);
722 std::cout
<< s2
.ToString() << std::endl
;
726 std::cout
<< "Time by librados : " << timer
<< "ms" << std::endl
;
730 class EnvLibradosMutipoolTest
: public testing::Test
{
732 // we will use all of these below
733 const std::string client_name
= "client.admin";
734 const std::string cluster_name
= "ceph";
735 const uint64_t flags
= 0;
736 const std::string db_name
= "env_librados_test_db";
737 const std::string db_pool
= db_name
+ "_pool";
738 const std::string wal_dir
= "/wal";
739 const std::string wal_pool
= db_name
+ "_wal_pool";
740 const size_t write_buffer_size
= 1 << 20;
741 const char *keyring
= "admin";
742 const char *config
= "../ceph/src/ceph.conf";
745 const EnvOptions soptions_
;
747 EnvLibradosMutipoolTest() {
748 env_
= new EnvLibrados(client_name
, cluster_name
, flags
, db_name
, config
, db_pool
, wal_dir
, wal_pool
, write_buffer_size
);
750 ~EnvLibradosMutipoolTest() {
752 librados::Rados rados
;
755 ret
= rados
.init("admin"); // just use the client.admin keyring
756 if (ret
< 0) { // let's handle any error that might have come back
757 std::cerr
<< "couldn't initialize rados! error " << ret
<< std::endl
;
762 ret
= rados
.conf_read_file(config
);
764 // This could fail if the config file is malformed, but it'd be hard.
765 std::cerr
<< "failed to parse config file " << config
766 << "! error" << ret
<< std::endl
;
772 * next, we actually connect to the cluster
775 ret
= rados
.connect();
777 std::cerr
<< "couldn't connect to cluster! error " << ret
<< std::endl
;
783 * And now we're done, so let's remove our pool and then
784 * shut down the connection gracefully.
786 int delete_ret
= rados
.pool_delete(db_pool
.c_str());
787 if (delete_ret
< 0) {
789 std::cerr
<< "We failed to delete our test pool!" << db_pool
<< delete_ret
<< std::endl
;
792 delete_ret
= rados
.pool_delete(wal_pool
.c_str());
793 if (delete_ret
< 0) {
795 std::cerr
<< "We failed to delete our test pool!" << wal_pool
<< delete_ret
<< std::endl
;
802 TEST_F(EnvLibradosMutipoolTest
, Basics
) {
804 std::unique_ptr
<WritableFile
> writable_file
;
805 std::vector
<std::string
> children
;
806 std::vector
<std::string
> v
= {"/tmp/dir1", "/tmp/dir2", "/tmp/dir3", "/tmp/dir4", "dir"};
808 for (size_t i
= 0; i
< v
.size(); ++i
) {
809 std::string dir
= v
[i
];
810 std::string dir_non_existent
= dir
+ "/non_existent";
811 std::string dir_f
= dir
+ "/f";
812 std::string dir_g
= dir
+ "/g";
814 ASSERT_OK(env_
->CreateDir(dir
.c_str()));
815 // Check that the directory is empty.
816 ASSERT_EQ(Status::NotFound(), env_
->FileExists(dir_non_existent
.c_str()));
817 ASSERT_TRUE(!env_
->GetFileSize(dir_non_existent
.c_str(), &file_size
).ok());
818 ASSERT_OK(env_
->GetChildren(dir
.c_str(), &children
));
819 ASSERT_EQ(0U, children
.size());
822 ASSERT_OK(env_
->NewWritableFile(dir_f
.c_str(), &writable_file
, soptions_
));
823 writable_file
.reset();
825 // Check that the file exists.
826 ASSERT_OK(env_
->FileExists(dir_f
.c_str()));
827 ASSERT_OK(env_
->GetFileSize(dir_f
.c_str(), &file_size
));
828 ASSERT_EQ(0U, file_size
);
829 ASSERT_OK(env_
->GetChildren(dir
.c_str(), &children
));
830 ASSERT_EQ(1U, children
.size());
831 ASSERT_EQ("f", children
[0]);
833 // Write to the file.
834 ASSERT_OK(env_
->NewWritableFile(dir_f
.c_str(), &writable_file
, soptions_
));
835 ASSERT_OK(writable_file
->Append("abc"));
836 writable_file
.reset();
839 // Check for expected size.
840 ASSERT_OK(env_
->GetFileSize(dir_f
.c_str(), &file_size
));
841 ASSERT_EQ(3U, file_size
);
844 // Check that renaming works.
845 ASSERT_TRUE(!env_
->RenameFile(dir_non_existent
.c_str(), dir_g
.c_str()).ok());
846 ASSERT_OK(env_
->RenameFile(dir_f
.c_str(), dir_g
.c_str()));
847 ASSERT_EQ(Status::NotFound(), env_
->FileExists(dir_f
.c_str()));
848 ASSERT_OK(env_
->FileExists(dir_g
.c_str()));
849 ASSERT_OK(env_
->GetFileSize(dir_g
.c_str(), &file_size
));
850 ASSERT_EQ(3U, file_size
);
852 // Check that opening non-existent file fails.
853 std::unique_ptr
<SequentialFile
> seq_file
;
854 std::unique_ptr
<RandomAccessFile
> rand_file
;
856 !env_
->NewSequentialFile(dir_non_existent
.c_str(), &seq_file
, soptions_
).ok());
857 ASSERT_TRUE(!seq_file
);
858 ASSERT_TRUE(!env_
->NewRandomAccessFile(dir_non_existent
.c_str(), &rand_file
,
860 ASSERT_TRUE(!rand_file
);
862 // Check that deleting works.
863 ASSERT_TRUE(!env_
->DeleteFile(dir_non_existent
.c_str()).ok());
864 ASSERT_OK(env_
->DeleteFile(dir_g
.c_str()));
865 ASSERT_EQ(Status::NotFound(), env_
->FileExists(dir_g
.c_str()));
866 ASSERT_OK(env_
->GetChildren(dir
.c_str(), &children
));
867 ASSERT_EQ(0U, children
.size());
868 ASSERT_OK(env_
->DeleteDir(dir
.c_str()));
872 TEST_F(EnvLibradosMutipoolTest
, DBBasics
) {
873 std::string kDBPath
= "/tmp/DBBasics";
874 std::string walPath
= "/tmp/wal";
877 // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
878 options
.IncreaseParallelism();
879 options
.OptimizeLevelStyleCompaction();
880 // create the DB if it's not already present
881 options
.create_if_missing
= true;
883 options
.wal_dir
= walPath
;
886 Status s
= DB::Open(options
, kDBPath
, &db
);
890 s
= db
->Put(WriteOptions(), "key1", "value");
894 s
= db
->Get(ReadOptions(), "key1", &value
);
896 assert(value
== "value");
898 // atomically apply a set of updates
901 batch
.Delete("key1");
902 batch
.Put("key2", value
);
903 s
= db
->Write(WriteOptions(), &batch
);
906 s
= db
->Get(ReadOptions(), "key1", &value
);
907 assert(s
.IsNotFound());
909 db
->Get(ReadOptions(), "key2", &value
);
910 assert(value
== "value");
915 TEST_F(EnvLibradosMutipoolTest
, DBBulkLoadKeysInRandomOrder
) {
916 char key
[20] = {0}, value
[20] = {0};
917 int max_loop
= 1 << 6;
918 int bulk_size
= 1 << 15;
920 std::cout
<< "Test size : loop(" << max_loop
<< "); bulk_size(" << bulk_size
<< ")" << std::endl
;
921 /**********************************
923 ***********************************/
924 std::string kDBPath1
= "/tmp/DBBulkLoadKeysInRandomOrder1";
925 std::string walPath
= "/tmp/wal";
928 // Optimize Rocksdb. This is the easiest way to get RocksDB to perform well
929 options1
.IncreaseParallelism();
930 options1
.OptimizeLevelStyleCompaction();
931 // create the DB if it's not already present
932 options1
.create_if_missing
= true;
935 Status s1
= DB::Open(options1
, kDBPath1
, &db1
);
938 ROCKSDB_NAMESPACE::Random64
r1(time(nullptr));
941 for (int i
= 0; i
< max_loop
; ++i
) {
943 for (int j
= 0; j
< bulk_size
; ++j
) {
947 (unsigned long)r1
.Uniform(std::numeric_limits
<uint64_t>::max()));
951 (unsigned long)r1
.Uniform(std::numeric_limits
<uint64_t>::max()));
952 batch
.Put(key
, value
);
954 s1
= db1
->Write(WriteOptions(), &batch
);
957 std::cout
<< "Time by default : " << timer
<< "ms" << std::endl
;
960 /**********************************
962 ***********************************/
963 std::string kDBPath2
= "/tmp/DBBulkLoadKeysInRandomOrder2";
966 // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
967 options2
.IncreaseParallelism();
968 options2
.OptimizeLevelStyleCompaction();
969 // create the DB if it's not already present
970 options2
.create_if_missing
= true;
972 options2
.wal_dir
= walPath
;
975 Status s2
= DB::Open(options2
, kDBPath2
, &db2
);
977 std::cerr
<< s2
.ToString() << std::endl
;
981 ROCKSDB_NAMESPACE::Random64
r2(time(nullptr));
984 for (int i
= 0; i
< max_loop
; ++i
) {
986 for (int j
= 0; j
< bulk_size
; ++j
) {
990 (unsigned long)r2
.Uniform(std::numeric_limits
<uint64_t>::max()));
994 (unsigned long)r2
.Uniform(std::numeric_limits
<uint64_t>::max()));
995 batch
.Put(key
, value
);
997 s2
= db2
->Write(WriteOptions(), &batch
);
1000 std::cout
<< "Time by librados : " << timer
<< "ms" << std::endl
;
1004 TEST_F(EnvLibradosMutipoolTest
, DBTransactionDB
) {
1005 std::string kDBPath
= "/tmp/DBTransactionDB";
1008 TransactionDBOptions txn_db_options
;
1009 options
.create_if_missing
= true;
1011 TransactionDB
* txn_db
;
1013 Status s
= TransactionDB::Open(options
, txn_db_options
, kDBPath
, &txn_db
);
1016 WriteOptions write_options
;
1017 ReadOptions read_options
;
1018 TransactionOptions txn_options
;
1021 ////////////////////////////////////////////////////////
1023 // Simple OptimisticTransaction Example ("Read Committed")
1025 ////////////////////////////////////////////////////////
1027 // Start a transaction
1028 Transaction
* txn
= txn_db
->BeginTransaction(write_options
);
1031 // Read a key in this transaction
1032 s
= txn
->Get(read_options
, "abc", &value
);
1033 assert(s
.IsNotFound());
1035 // Write a key in this transaction
1036 s
= txn
->Put("abc", "def");
1039 // Read a key OUTSIDE this transaction. Does not affect txn.
1040 s
= txn_db
->Get(read_options
, "abc", &value
);
1042 // Write a key OUTSIDE of this transaction.
1043 // Does not affect txn since this is an unrelated key. If we wrote key 'abc'
1044 // here, the transaction would fail to commit.
1045 s
= txn_db
->Put(write_options
, "xyz", "zzz");
1047 // Commit transaction
1052 ////////////////////////////////////////////////////////
1054 // "Repeatable Read" (Snapshot Isolation) Example
1055 // -- Using a single Snapshot
1057 ////////////////////////////////////////////////////////
1059 // Set a snapshot at start of transaction by setting set_snapshot=true
1060 txn_options
.set_snapshot
= true;
1061 txn
= txn_db
->BeginTransaction(write_options
, txn_options
);
1063 const Snapshot
* snapshot
= txn
->GetSnapshot();
1065 // Write a key OUTSIDE of transaction
1066 s
= txn_db
->Put(write_options
, "abc", "xyz");
1069 // Attempt to read a key using the snapshot. This will fail since
1070 // the previous write outside this txn conflicts with this read.
1071 read_options
.snapshot
= snapshot
;
1072 s
= txn
->GetForUpdate(read_options
, "abc", &value
);
1078 // Clear snapshot from read options since it is no longer valid
1079 read_options
.snapshot
= nullptr;
1082 ////////////////////////////////////////////////////////
1084 // "Read Committed" (Monotonic Atomic Views) Example
1085 // --Using multiple Snapshots
1087 ////////////////////////////////////////////////////////
1089 // In this example, we set the snapshot multiple times. This is probably
1090 // only necessary if you have very strict isolation requirements to
1093 // Set a snapshot at start of transaction
1094 txn_options
.set_snapshot
= true;
1095 txn
= txn_db
->BeginTransaction(write_options
, txn_options
);
1097 // Do some reads and writes to key "x"
1098 read_options
.snapshot
= txn_db
->GetSnapshot();
1099 s
= txn
->Get(read_options
, "x", &value
);
1102 // Do a write outside of the transaction to key "y"
1103 s
= txn_db
->Put(write_options
, "y", "y");
1105 // Set a new snapshot in the transaction
1107 txn
->SetSavePoint();
1108 read_options
.snapshot
= txn_db
->GetSnapshot();
1110 // Do some reads and writes to key "y"
1111 // Since the snapshot was advanced, the write done outside of the
1112 // transaction does not conflict.
1113 s
= txn
->GetForUpdate(read_options
, "y", &value
);
1116 // Decide we want to revert the last write from this transaction.
1117 txn
->RollbackToSavePoint();
1123 // Clear snapshot from read options since it is no longer valid
1124 read_options
.snapshot
= nullptr;
1128 DestroyDB(kDBPath
, options
);
1131 } // namespace ROCKSDB_NAMESPACE
1133 int main(int argc
, char** argv
) {
1134 ::testing::InitGoogleTest(&argc
, argv
);
1135 return RUN_ALL_TESTS();
1141 int main(int argc
, char** argv
) {
1142 fprintf(stderr
, "SKIPPED as EnvMirror is not supported in ROCKSDB_LITE\n");
1146 #endif // !ROCKSDB_LITE