Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / objectstore / test_kv.cc
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
32 class KVTest : public ::testing::TestWithParam<const char*> {
33 public:
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
73 TEST_P(KVTest, OpenClose) {
74   ASSERT_EQ(0, db->create_and_open(cout));
75   fini();
76 }
77
78 TEST_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  */
89 TEST_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
117 TEST_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
159 TEST_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
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);
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.
207   string name() const override {
208     return "Append";
209   }
210 };
211
212 string tostr(bufferlist& b) {
213   return string(b.c_str(),b.length());
214 }
215
216 TEST_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
258 TEST_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
312 INSTANTIATE_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.
325 TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {}
326
327 #endif
328
329 int 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 }