]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/objectstore/test_kv.cc
bump version to 12.2.12-pve1
[ceph.git] / ceph / src / test / objectstore / test_kv.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
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.
12 *
13 */
14
15#include <stdio.h>
16#include <string.h>
17#include <iostream>
18#include <time.h>
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>
29
30#if GTEST_HAS_PARAM_TEST
31
32class KVTest : public ::testing::TestWithParam<const char*> {
33public:
34 boost::scoped_ptr<KeyValueDB> db;
35
36 KVTest() : db(0) {}
37
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());
42 if (r) {
43 cerr << "failed with exit code " << r
44 << ", continuing anyway" << std::endl;
45 }
46 }
47
48 void init() {
49 cout << "Creating " << string(GetParam()) << "\n";
50 db.reset(KeyValueDB::create(g_ceph_context, string(GetParam()),
51 "kv_test_temp_dir"));
52 }
53 void fini() {
54 db.reset(NULL);
55 }
56
57 void SetUp() override {
58 int r = ::mkdir("kv_test_temp_dir", 0777);
59 if (r < 0 && errno != EEXIST) {
60 r = -errno;
61 cerr << __func__ << ": unable to create kv_test_temp_dir: "
62 << cpp_strerror(r) << std::endl;
63 return;
64 }
65 init();
66 }
67 void TearDown() override {
68 fini();
69 rm_r("kv_test_temp_dir");
70 }
71};
72
73TEST_P(KVTest, OpenClose) {
74 ASSERT_EQ(0, db->create_and_open(cout));
75 fini();
76}
77
78TEST_P(KVTest, OpenCloseReopenClose) {
79 ASSERT_EQ(0, db->create_and_open(cout));
80 fini();
81 init();
82 ASSERT_EQ(0, db->open(cout));
83 fini();
84}
85
86/*
87 * Basic write and read test case in same database session.
88 */
89TEST_P(KVTest, OpenWriteRead) {
90 ASSERT_EQ(0, db->create_and_open(cout));
91 {
92 KeyValueDB::Transaction t = db->get_transaction();
93 bufferlist value;
94 value.append("value");
95 t->set("prefix", "key", value);
96 value.clear();
97 value.append("value2");
98 t->set("prefix", "key2", value);
99 value.clear();
100 value.append("value3");
101 t->set("prefix", "key3", value);
102 db->submit_transaction_sync(t);
103
104 bufferlist v1, v2;
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"));
113 }
114 fini();
115}
116
117TEST_P(KVTest, PutReopen) {
118 ASSERT_EQ(0, db->create_and_open(cout));
119 {
120 KeyValueDB::Transaction t = db->get_transaction();
121 bufferlist value;
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);
127 }
128 fini();
129
130 init();
131 ASSERT_EQ(0, db->open(cout));
132 {
133 bufferlist v1, v2;
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);
138 }
139 {
140 KeyValueDB::Transaction t = db->get_transaction();
141 t->rmkey("prefix", "key");
142 t->rmkey("prefix", "key3");
143 db->submit_transaction_sync(t);
144 }
145 fini();
146
147 init();
148 ASSERT_EQ(0, db->open(cout));
149 {
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));
155 }
156 fini();
157}
158
159TEST_P(KVTest, BenchCommit) {
160 int n = 1024;
161 ASSERT_EQ(0, db->create_and_open(cout));
162 utime_t start = ceph_clock_now();
163 {
164 cout << "priming" << std::endl;
165 // prime
166 bufferlist big;
167 bufferptr bp(1048576);
168 bp.zero();
169 big.append(bp);
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);
174 }
175 }
176 cout << "now doing small writes" << std::endl;
177 bufferlist data;
178 bufferptr bp(1024);
179 bp.zero();
180 data.append(bp);
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);
185 }
186 utime_t end = ceph_clock_now();
187 utime_t dur = end - start;
188 cout << n << " commits in " << dur << ", avg latency " << (dur / (double)n)
189 << std::endl;
190 fini();
191}
192
193struct 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);
197 }
198 void merge(
199 const char *ldata, size_t llen,
200 const char *rdata, size_t rlen,
201 std::string *new_value) override {
202
203 *new_value = std::string(ldata, llen) + std::string(rdata, rlen);
204 }
205 // We use each operator name and each prefix to construct the
206 // overall RocksDB operator name for consistency check at open time.
91327a77 207 const char *name() const override {
7c673cae
FG
208 return "Append";
209 }
210};
211
212string tostr(bufferlist& b) {
213 return string(b.c_str(),b.length());
214}
215
216TEST_P(KVTest, Merge) {
217 shared_ptr<KeyValueDB::MergeOperator> p(new AppendMOP);
218 int r = db->set_merge_operator("A",p);
219 if (r < 0)
220 return; // No merge operators for this database type
221 ASSERT_EQ(0, db->create_and_open(cout));
222 {
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);
230 t->rmkey("A", "A2");
231 t->merge("A", "A2", v3);
232 db->submit_transaction_sync(t);
233 }
234 {
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");
242 }
243 {
244 KeyValueDB::Transaction t = db->get_transaction();
245 bufferlist v1;
246 v1.append(string("1"));
247 t->merge("A", "A2", v1);
248 db->submit_transaction_sync(t);
249 }
250 {
251 bufferlist v;
252 ASSERT_EQ(0, db->get("A", "A2", &v));
253 ASSERT_EQ(tostr(v), "?31");
254 }
255 fini();
256}
257
258TEST_P(KVTest, RMRange) {
259 ASSERT_EQ(0, db->create_and_open(cout));
260 bufferlist value;
261 value.append("value");
262 {
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);
272 }
273
274 {
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);
280 bufferlist v1, v2;
281 ASSERT_EQ(0, db->get("prefix", "key1", &v1));
282 v1.clear();
283 ASSERT_EQ(-ENOENT, db->get("prefix", "key45", &v1));
284 ASSERT_EQ(0, db->get("prefix", "key8", &v1));
285 v1.clear();
286 ASSERT_EQ(-ENOENT, db->get("prefix", "key2", &v1));
287 ASSERT_EQ(0, db->get("prefix", "key7", &v2));
288 }
289
290 {
291 KeyValueDB::Transaction t = db->get_transaction();
292 t->rm_range_keys("prefix", "key", "key");
293 db->submit_transaction_sync(t);
294 bufferlist v1, v2;
295 ASSERT_EQ(0, db->get("prefix", "key1", &v1));
296 ASSERT_EQ(0, db->get("prefix", "key8", &v2));
297 }
298
299 {
300 KeyValueDB::Transaction t = db->get_transaction();
301 t->rm_range_keys("prefix", "key-", "key~");
302 db->submit_transaction_sync(t);
303 bufferlist v1, v2;
304 ASSERT_EQ(-ENOENT, db->get("prefix", "key1", &v1));
305 ASSERT_EQ(-ENOENT, db->get("prefix", "key8", &v2));
306 }
307
308 fini();
309}
310
311
312INSTANTIATE_TEST_CASE_P(
313 KeyValueDB,
314 KVTest,
315 ::testing::Values("leveldb", "rocksdb", "memdb"));
316
317#else
318
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.
325TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {}
326
327#endif
328
329int main(int argc, char **argv) {
330 vector<const char*> args;
331 argv_to_vec(argc, (const char **)argv, args);
332 env_to_vec(args);
333
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",
339 "rocksdb, memdb");
340 g_ceph_context->_conf->apply_changes(NULL);
341
342 ::testing::InitGoogleTest(&argc, argv);
343 return RUN_ALL_TESTS();
344}