]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/kvstore_tool.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / tools / kvstore_tool.cc
CommitLineData
11fdf7f2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include "kvstore_tool.h"
5
6#include <iostream>
7
8#include "common/errno.h"
9#include "common/url_escape.h"
20effc67 10#include "common/pretty_binary.h"
11fdf7f2
TL
11#include "include/buffer.h"
12#include "kv/KeyValueDB.h"
20effc67
TL
13#include "kv/KeyValueHistogram.h"
14
15using namespace std;
11fdf7f2 16
494da23a
TL
17StoreTool::StoreTool(const string& type,
18 const string& path,
f67539c2 19 bool to_repair,
494da23a 20 bool need_stats)
11fdf7f2
TL
21 : store_path(path)
22{
494da23a
TL
23
24 if (need_stats) {
25 g_conf()->rocksdb_perf = true;
26 g_conf()->rocksdb_collect_compaction_stats = true;
27 }
28
11fdf7f2
TL
29 if (type == "bluestore-kv") {
30#ifdef WITH_BLUESTORE
f67539c2 31 if (load_bluestore(path, to_repair) != 0)
11fdf7f2
TL
32 exit(1);
33#else
34 cerr << "bluestore not compiled in" << std::endl;
35 exit(1);
36#endif
37 } else {
38 auto db_ptr = KeyValueDB::create(g_ceph_context, type, path);
f67539c2 39 if (!to_repair) {
11fdf7f2
TL
40 if (int r = db_ptr->open(std::cerr); r < 0) {
41 cerr << "failed to open type " << type << " path " << path << ": "
42 << cpp_strerror(r) << std::endl;
43 exit(1);
44 }
11fdf7f2 45 }
20effc67 46 db.reset(db_ptr);
11fdf7f2
TL
47 }
48}
49
f67539c2 50int StoreTool::load_bluestore(const string& path, bool to_repair)
11fdf7f2
TL
51{
52 auto bluestore = new BlueStore(g_ceph_context, path);
53 KeyValueDB *db_ptr;
f67539c2 54 int r = bluestore->open_db_environment(&db_ptr, to_repair);
11fdf7f2
TL
55 if (r < 0) {
56 return -EINVAL;
57 }
58 db = decltype(db){db_ptr, Deleter(bluestore)};
59 return 0;
60}
61
62uint32_t StoreTool::traverse(const string& prefix,
63 const bool do_crc,
64 const bool do_value_dump,
65 ostream *out)
66{
67 KeyValueDB::WholeSpaceIterator iter = db->get_wholespace_iterator();
68
69 if (prefix.empty())
70 iter->seek_to_first();
71 else
72 iter->seek_to_first(prefix);
73
74 uint32_t crc = -1;
75
76 while (iter->valid()) {
77 pair<string,string> rk = iter->raw_key();
78 if (!prefix.empty() && (rk.first != prefix))
79 break;
80
81 if (out)
82 *out << url_escape(rk.first) << "\t" << url_escape(rk.second);
83 if (do_crc) {
84 bufferlist bl;
85 bl.append(rk.first);
86 bl.append(rk.second);
87 bl.append(iter->value());
88
89 crc = bl.crc32c(crc);
90 if (out) {
91 *out << "\t" << bl.crc32c(0);
92 }
93 }
94 if (out)
95 *out << std::endl;
96 if (out && do_value_dump) {
97 bufferptr bp = iter->value_as_ptr();
98 bufferlist value;
99 value.append(bp);
100 ostringstream os;
101 value.hexdump(os);
102 std::cout << os.str() << std::endl;
103 }
104 iter->next();
105 }
106
107 return crc;
108}
109
110void StoreTool::list(const string& prefix, const bool do_crc,
111 const bool do_value_dump)
112{
113 traverse(prefix, do_crc, do_value_dump,& std::cout);
114}
115
116bool StoreTool::exists(const string& prefix)
117{
118 ceph_assert(!prefix.empty());
119 KeyValueDB::WholeSpaceIterator iter = db->get_wholespace_iterator();
120 iter->seek_to_first(prefix);
121 return (iter->valid() && (iter->raw_key().first == prefix));
122}
123
124bool StoreTool::exists(const string& prefix, const string& key)
125{
126 ceph_assert(!prefix.empty());
127
128 if (key.empty()) {
129 return exists(prefix);
130 }
131 bool exists = false;
132 get(prefix, key, exists);
133 return exists;
134}
135
136bufferlist StoreTool::get(const string& prefix,
137 const string& key,
138 bool& exists)
139{
140 ceph_assert(!prefix.empty() && !key.empty());
141
142 map<string,bufferlist> result;
143 std::set<std::string> keys;
144 keys.insert(key);
145 db->get(prefix, keys, &result);
146
147 if (result.count(key) > 0) {
148 exists = true;
149 return result[key];
150 } else {
151 exists = false;
152 return bufferlist();
153 }
154}
155
156uint64_t StoreTool::get_size()
157{
158 map<string,uint64_t> extras;
159 uint64_t s = db->get_estimated_size(extras);
160 for (auto& [name, size] : extras) {
161 std::cout << name << " - " << size << std::endl;
162 }
163 std::cout << "total: " << s << std::endl;
164 return s;
165}
166
167bool StoreTool::set(const string &prefix, const string &key, bufferlist &val)
168{
169 ceph_assert(!prefix.empty());
170 ceph_assert(!key.empty());
171 ceph_assert(val.length() > 0);
172
173 KeyValueDB::Transaction tx = db->get_transaction();
174 tx->set(prefix, key, val);
175 int ret = db->submit_transaction_sync(tx);
176
177 return (ret == 0);
178}
179
180bool StoreTool::rm(const string& prefix, const string& key)
181{
182 ceph_assert(!prefix.empty());
183 ceph_assert(!key.empty());
184
185 KeyValueDB::Transaction tx = db->get_transaction();
186 tx->rmkey(prefix, key);
187 int ret = db->submit_transaction_sync(tx);
188
189 return (ret == 0);
190}
191
192bool StoreTool::rm_prefix(const string& prefix)
193{
194 ceph_assert(!prefix.empty());
195
196 KeyValueDB::Transaction tx = db->get_transaction();
197 tx->rmkeys_by_prefix(prefix);
198 int ret = db->submit_transaction_sync(tx);
199
200 return (ret == 0);
201}
202
203void StoreTool::print_summary(const uint64_t total_keys, const uint64_t total_size,
204 const uint64_t total_txs, const string& store_path,
205 const string& other_path, const int duration) const
206{
207 std::cout << "summary:" << std::endl;
208 std::cout << " copied " << total_keys << " keys" << std::endl;
209 std::cout << " used " << total_txs << " transactions" << std::endl;
210 std::cout << " total size " << byte_u_t(total_size) << std::endl;
211 std::cout << " from '" << store_path << "' to '" << other_path << "'"
212 << std::endl;
213 std::cout << " duration " << duration << " seconds" << std::endl;
214}
215
494da23a
TL
216int StoreTool::print_stats() const
217{
218 ostringstream ostr;
219 Formatter* f = Formatter::create("json-pretty", "json-pretty", "json-pretty");
220 int ret = -1;
221 if (g_conf()->rocksdb_perf) {
222 db->get_statistics(f);
223 ostr << "db_statistics ";
224 f->flush(ostr);
225 ret = 0;
226 } else {
227 ostr << "db_statistics not enabled";
228 f->flush(ostr);
229 }
230 std::cout << ostr.str() << std::endl;
231 delete f;
232 return ret;
233}
234
20effc67
TL
235//Itrerates through the db and collects the stats
236int StoreTool::build_size_histogram(const string& prefix0) const
237{
238 ostringstream ostr;
239 Formatter* f = Formatter::create("json-pretty", "json-pretty", "json-pretty");
240
241 const size_t MAX_PREFIX = 256;
242 uint64_t num[MAX_PREFIX] = {0};
243
244 size_t max_key_size = 0, max_value_size = 0;
245 uint64_t total_key_size = 0, total_value_size = 0;
246 size_t key_size = 0, value_size = 0;
247 KeyValueHistogram hist;
248
249 auto start = coarse_mono_clock::now();
250
251 auto iter = db->get_iterator(prefix0, KeyValueDB::ITERATOR_NOCACHE);
252 iter->seek_to_first();
253 while (iter->valid()) {
254 pair<string, string> key(iter->raw_key());
255 key_size = key.first.size() + key.second.size();
256 value_size = iter->value().length();
257 hist.value_hist[hist.get_value_slab(value_size)]++;
258 max_key_size = std::max(max_key_size, key_size);
259 max_value_size = std::max(max_value_size, value_size);
260 total_key_size += key_size;
261 total_value_size += value_size;
262
263
264 unsigned prefix = key.first[0];
265 ceph_assert(prefix < MAX_PREFIX);
266 num[prefix]++;
267 hist.update_hist_entry(hist.key_hist, key.first, key_size, value_size);
268 iter->next();
269 }
270
271 ceph::timespan duration = coarse_mono_clock::now() - start;
272 f->open_object_section("rocksdb_key_value_stats");
273 for (size_t i = 0; i < MAX_PREFIX; ++i) {
274 if (num[i]) {
275 string key = "Records for prefix: ";
276 key += pretty_binary_string(string(1, char(i)));
277 f->dump_unsigned(key, num[i]);
278 }
279 }
280 f->dump_unsigned("max_key_size", max_key_size);
281 f->dump_unsigned("max_value_size", max_value_size);
282 f->dump_unsigned("total_key_size", total_key_size);
283 f->dump_unsigned("total_value_size", total_value_size);
284 hist.dump(f);
285 f->close_section();
286
287 f->flush(ostr);
288 delete f;
289
290 std::cout << ostr.str() << std::endl;
291 std::cout << __func__ << " finished in " << duration << " seconds" << std::endl;
292 return 0;
293}
294
11fdf7f2
TL
295int StoreTool::copy_store_to(const string& type, const string& other_path,
296 const int num_keys_per_tx,
297 const string& other_type)
298{
299 if (num_keys_per_tx <= 0) {
300 std::cerr << "must specify a number of keys/tx > 0" << std::endl;
301 return -EINVAL;
302 }
303
304 // open or create a leveldb store at @p other_path
305 boost::scoped_ptr<KeyValueDB> other;
306 KeyValueDB *other_ptr = KeyValueDB::create(g_ceph_context,
307 other_type,
308 other_path);
309 if (int err = other_ptr->create_and_open(std::cerr); err < 0) {
310 return err;
311 }
312 other.reset(other_ptr);
313
314 KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();
315 it->seek_to_first();
316 uint64_t total_keys = 0;
317 uint64_t total_size = 0;
318 uint64_t total_txs = 0;
319
320 auto duration = [start=coarse_mono_clock::now()] {
321 const auto now = coarse_mono_clock::now();
322 auto seconds = std::chrono::duration<double>(now - start);
323 return seconds.count();
324 };
325
326 do {
327 int num_keys = 0;
328
329 KeyValueDB::Transaction tx = other->get_transaction();
330
331 while (it->valid() && num_keys < num_keys_per_tx) {
332 auto [prefix, key] = it->raw_key();
333 bufferlist v = it->value();
334 tx->set(prefix, key, v);
335
336 num_keys++;
337 total_size += v.length();
338
339 it->next();
340 }
341
342 total_txs++;
343 total_keys += num_keys;
344
345 if (num_keys > 0)
346 other->submit_transaction_sync(tx);
347
348 std::cout << "ts = " << duration() << "s, copied " << total_keys
349 << " keys so far (" << byte_u_t(total_size) << ")"
350 << std::endl;
351
352 } while (it->valid());
353
354 print_summary(total_keys, total_size, total_txs, store_path, other_path,
355 duration());
356
357 return 0;
358}
359
360void StoreTool::compact()
361{
362 db->compact();
363}
364
365void StoreTool::compact_prefix(const string& prefix)
366{
367 db->compact_prefix(prefix);
368}
369
370void StoreTool::compact_range(const string& prefix,
371 const string& start,
372 const string& end)
373{
374 db->compact_range(prefix, start, end);
375}
376
377int StoreTool::destructive_repair()
378{
379 return db->repair(std::cout);
380}