1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
19 #include <sys/mount.h>
20 #include "kv/KeyValueDB.h"
21 #include "include/Context.h"
22 #include "common/ceph_argparse.h"
23 #include "global/global_init.h"
24 #include "common/Mutex.h"
25 #include "common/Cond.h"
26 #include "common/errno.h"
27 #include "include/stringify.h"
28 #include <gtest/gtest.h>
30 #if GTEST_HAS_PARAM_TEST
32 class KVTest
: public ::testing::TestWithParam
<const char*> {
34 boost::scoped_ptr
<KeyValueDB
> db
;
38 void rm_r(string path
) {
39 string cmd
= string("rm -r ") + path
;
40 cout
<< "==> " << cmd
<< std::endl
;
41 int r
= ::system(cmd
.c_str());
43 cerr
<< "failed with exit code " << r
44 << ", continuing anyway" << std::endl
;
49 cout
<< "Creating " << string(GetParam()) << "\n";
50 db
.reset(KeyValueDB::create(g_ceph_context
, string(GetParam()),
57 void SetUp() override
{
58 int r
= ::mkdir("kv_test_temp_dir", 0777);
59 if (r
< 0 && errno
!= EEXIST
) {
61 cerr
<< __func__
<< ": unable to create kv_test_temp_dir: "
62 << cpp_strerror(r
) << std::endl
;
67 void TearDown() override
{
69 rm_r("kv_test_temp_dir");
73 TEST_P(KVTest
, OpenClose
) {
74 ASSERT_EQ(0, db
->create_and_open(cout
));
78 TEST_P(KVTest
, OpenCloseReopenClose
) {
79 ASSERT_EQ(0, db
->create_and_open(cout
));
82 ASSERT_EQ(0, db
->open(cout
));
87 * Basic write and read test case in same database session.
89 TEST_P(KVTest
, OpenWriteRead
) {
90 ASSERT_EQ(0, db
->create_and_open(cout
));
92 KeyValueDB::Transaction t
= db
->get_transaction();
94 value
.append("value");
95 t
->set("prefix", "key", value
);
97 value
.append("value2");
98 t
->set("prefix", "key2", value
);
100 value
.append("value3");
101 t
->set("prefix", "key3", value
);
102 db
->submit_transaction_sync(t
);
105 ASSERT_EQ(0, db
->get("prefix", "key", &v1
));
106 ASSERT_EQ(v1
.length(), 5u);
107 (v1
.c_str())[v1
.length()] = 0x0;
108 ASSERT_EQ(std::string(v1
.c_str()), std::string("value"));
109 ASSERT_EQ(0, db
->get("prefix", "key2", &v2
));
110 ASSERT_EQ(v2
.length(), 6u);
111 (v2
.c_str())[v2
.length()] = 0x0;
112 ASSERT_EQ(std::string(v2
.c_str()), std::string("value2"));
117 TEST_P(KVTest
, PutReopen
) {
118 ASSERT_EQ(0, db
->create_and_open(cout
));
120 KeyValueDB::Transaction t
= db
->get_transaction();
122 value
.append("value");
123 t
->set("prefix", "key", value
);
124 t
->set("prefix", "key2", value
);
125 t
->set("prefix", "key3", value
);
126 db
->submit_transaction_sync(t
);
131 ASSERT_EQ(0, db
->open(cout
));
134 ASSERT_EQ(0, db
->get("prefix", "key", &v1
));
135 ASSERT_EQ(v1
.length(), 5u);
136 ASSERT_EQ(0, db
->get("prefix", "key2", &v2
));
137 ASSERT_EQ(v2
.length(), 5u);
140 KeyValueDB::Transaction t
= db
->get_transaction();
141 t
->rmkey("prefix", "key");
142 t
->rmkey("prefix", "key3");
143 db
->submit_transaction_sync(t
);
148 ASSERT_EQ(0, db
->open(cout
));
150 bufferlist v1
, v2
, v3
;
151 ASSERT_EQ(-ENOENT
, db
->get("prefix", "key", &v1
));
152 ASSERT_EQ(0, db
->get("prefix", "key2", &v2
));
153 ASSERT_EQ(v2
.length(), 5u);
154 ASSERT_EQ(-ENOENT
, db
->get("prefix", "key3", &v3
));
159 TEST_P(KVTest
, BenchCommit
) {
161 ASSERT_EQ(0, db
->create_and_open(cout
));
162 utime_t start
= ceph_clock_now();
164 cout
<< "priming" << std::endl
;
167 bufferptr
bp(1048576);
170 for (int i
=0; i
<30; ++i
) {
171 KeyValueDB::Transaction t
= db
->get_transaction();
172 t
->set("prefix", "big" + stringify(i
), big
);
173 db
->submit_transaction_sync(t
);
176 cout
<< "now doing small writes" << std::endl
;
181 for (int i
=0; i
<n
; ++i
) {
182 KeyValueDB::Transaction t
= db
->get_transaction();
183 t
->set("prefix", "key" + stringify(i
), data
);
184 db
->submit_transaction_sync(t
);
186 utime_t end
= ceph_clock_now();
187 utime_t dur
= end
- start
;
188 cout
<< n
<< " commits in " << dur
<< ", avg latency " << (dur
/ (double)n
)
193 struct AppendMOP
: public KeyValueDB::MergeOperator
{
194 void merge_nonexistent(
195 const char *rdata
, size_t rlen
, std::string
*new_value
) override
{
196 *new_value
= "?" + std::string(rdata
, rlen
);
199 const char *ldata
, size_t llen
,
200 const char *rdata
, size_t rlen
,
201 std::string
*new_value
) override
{
203 *new_value
= std::string(ldata
, llen
) + std::string(rdata
, rlen
);
205 // We use each operator name and each prefix to construct the
206 // overall RocksDB operator name for consistency check at open time.
207 string
name() const override
{
212 string
tostr(bufferlist
& b
) {
213 return string(b
.c_str(),b
.length());
216 TEST_P(KVTest
, Merge
) {
217 shared_ptr
<KeyValueDB::MergeOperator
> p(new AppendMOP
);
218 int r
= db
->set_merge_operator("A",p
);
220 return; // No merge operators for this database type
221 ASSERT_EQ(0, db
->create_and_open(cout
));
223 KeyValueDB::Transaction t
= db
->get_transaction();
224 bufferlist v1
, v2
, v3
;
225 v1
.append(string("1"));
226 v2
.append(string("2"));
227 v3
.append(string("3"));
228 t
->set("P", "K1", v1
);
229 t
->set("A", "A1", v2
);
231 t
->merge("A", "A2", v3
);
232 db
->submit_transaction_sync(t
);
235 bufferlist v1
, v2
, v3
;
236 ASSERT_EQ(0, db
->get("P", "K1", &v1
));
237 ASSERT_EQ(tostr(v1
), "1");
238 ASSERT_EQ(0, db
->get("A", "A1", &v2
));
239 ASSERT_EQ(tostr(v2
), "2");
240 ASSERT_EQ(0, db
->get("A", "A2", &v3
));
241 ASSERT_EQ(tostr(v3
), "?3");
244 KeyValueDB::Transaction t
= db
->get_transaction();
246 v1
.append(string("1"));
247 t
->merge("A", "A2", v1
);
248 db
->submit_transaction_sync(t
);
252 ASSERT_EQ(0, db
->get("A", "A2", &v
));
253 ASSERT_EQ(tostr(v
), "?31");
258 TEST_P(KVTest
, RMRange
) {
259 ASSERT_EQ(0, db
->create_and_open(cout
));
261 value
.append("value");
263 KeyValueDB::Transaction t
= db
->get_transaction();
264 t
->set("prefix", "key1", value
);
265 t
->set("prefix", "key2", value
);
266 t
->set("prefix", "key3", value
);
267 t
->set("prefix", "key4", value
);
268 t
->set("prefix", "key45", value
);
269 t
->set("prefix", "key5", value
);
270 t
->set("prefix", "key6", value
);
271 db
->submit_transaction_sync(t
);
275 KeyValueDB::Transaction t
= db
->get_transaction();
276 t
->set("prefix", "key7", value
);
277 t
->set("prefix", "key8", value
);
278 t
->rm_range_keys("prefix", "key2", "key7");
279 db
->submit_transaction_sync(t
);
281 ASSERT_EQ(0, db
->get("prefix", "key1", &v1
));
283 ASSERT_EQ(-ENOENT
, db
->get("prefix", "key45", &v1
));
284 ASSERT_EQ(0, db
->get("prefix", "key8", &v1
));
286 ASSERT_EQ(-ENOENT
, db
->get("prefix", "key2", &v1
));
287 ASSERT_EQ(0, db
->get("prefix", "key7", &v2
));
291 KeyValueDB::Transaction t
= db
->get_transaction();
292 t
->rm_range_keys("prefix", "key", "key");
293 db
->submit_transaction_sync(t
);
295 ASSERT_EQ(0, db
->get("prefix", "key1", &v1
));
296 ASSERT_EQ(0, db
->get("prefix", "key8", &v2
));
300 KeyValueDB::Transaction t
= db
->get_transaction();
301 t
->rm_range_keys("prefix", "key-", "key~");
302 db
->submit_transaction_sync(t
);
304 ASSERT_EQ(-ENOENT
, db
->get("prefix", "key1", &v1
));
305 ASSERT_EQ(-ENOENT
, db
->get("prefix", "key8", &v2
));
312 INSTANTIATE_TEST_CASE_P(
315 ::testing::Values("leveldb", "rocksdb", "memdb"));
319 // Google Test may not support value-parameterized tests with some
320 // compilers. If we use conditional compilation to compile out all
321 // code referring to the gtest_main library, MSVC linker will not link
322 // that library at all and consequently complain about missing entry
323 // point defined in that library (fatal error LNK1561: entry point
324 // must be defined). This dummy test keeps gtest_main linked in.
325 TEST(DummyTest
, ValueParameterizedTestsAreNotSupportedOnThisPlatform
) {}
329 int main(int argc
, char **argv
) {
330 vector
<const char*> args
;
331 argv_to_vec(argc
, (const char **)argv
, args
);
334 auto cct
= global_init(NULL
, args
, CEPH_ENTITY_TYPE_CLIENT
,
335 CODE_ENVIRONMENT_UTILITY
, 0);
336 common_init_finish(g_ceph_context
);
337 g_ceph_context
->_conf
->set_val(
338 "enable_experimental_unrecoverable_data_corrupting_features",
340 g_ceph_context
->_conf
->apply_changes(NULL
);
342 ::testing::InitGoogleTest(&argc
, argv
);
343 return RUN_ALL_TESTS();