// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "kvstore_tool.h" #include #include "common/errno.h" #include "common/url_escape.h" #include "include/buffer.h" #include "kv/KeyValueDB.h" StoreTool::StoreTool(const string& type, const string& path, bool to_repair, bool need_stats) : store_path(path) { if (need_stats) { g_conf()->rocksdb_perf = true; g_conf()->rocksdb_collect_compaction_stats = true; } if (type == "bluestore-kv") { #ifdef WITH_BLUESTORE if (load_bluestore(path, to_repair) != 0) exit(1); #else cerr << "bluestore not compiled in" << std::endl; exit(1); #endif } else { auto db_ptr = KeyValueDB::create(g_ceph_context, type, path); if (!to_repair) { if (int r = db_ptr->open(std::cerr); r < 0) { cerr << "failed to open type " << type << " path " << path << ": " << cpp_strerror(r) << std::endl; exit(1); } db.reset(db_ptr); } } } int StoreTool::load_bluestore(const string& path, bool to_repair) { auto bluestore = new BlueStore(g_ceph_context, path); KeyValueDB *db_ptr; int r = bluestore->open_db_environment(&db_ptr, to_repair); if (r < 0) { return -EINVAL; } db = decltype(db){db_ptr, Deleter(bluestore)}; return 0; } uint32_t StoreTool::traverse(const string& prefix, const bool do_crc, const bool do_value_dump, ostream *out) { KeyValueDB::WholeSpaceIterator iter = db->get_wholespace_iterator(); if (prefix.empty()) iter->seek_to_first(); else iter->seek_to_first(prefix); uint32_t crc = -1; while (iter->valid()) { pair rk = iter->raw_key(); if (!prefix.empty() && (rk.first != prefix)) break; if (out) *out << url_escape(rk.first) << "\t" << url_escape(rk.second); if (do_crc) { bufferlist bl; bl.append(rk.first); bl.append(rk.second); bl.append(iter->value()); crc = bl.crc32c(crc); if (out) { *out << "\t" << bl.crc32c(0); } } if (out) *out << std::endl; if (out && do_value_dump) { bufferptr bp = iter->value_as_ptr(); bufferlist value; value.append(bp); ostringstream os; value.hexdump(os); std::cout << os.str() << std::endl; } iter->next(); } return crc; } void StoreTool::list(const string& prefix, const bool do_crc, const bool do_value_dump) { traverse(prefix, do_crc, do_value_dump,& std::cout); } bool StoreTool::exists(const string& prefix) { ceph_assert(!prefix.empty()); KeyValueDB::WholeSpaceIterator iter = db->get_wholespace_iterator(); iter->seek_to_first(prefix); return (iter->valid() && (iter->raw_key().first == prefix)); } bool StoreTool::exists(const string& prefix, const string& key) { ceph_assert(!prefix.empty()); if (key.empty()) { return exists(prefix); } bool exists = false; get(prefix, key, exists); return exists; } bufferlist StoreTool::get(const string& prefix, const string& key, bool& exists) { ceph_assert(!prefix.empty() && !key.empty()); map result; std::set keys; keys.insert(key); db->get(prefix, keys, &result); if (result.count(key) > 0) { exists = true; return result[key]; } else { exists = false; return bufferlist(); } } uint64_t StoreTool::get_size() { map extras; uint64_t s = db->get_estimated_size(extras); for (auto& [name, size] : extras) { std::cout << name << " - " << size << std::endl; } std::cout << "total: " << s << std::endl; return s; } bool StoreTool::set(const string &prefix, const string &key, bufferlist &val) { ceph_assert(!prefix.empty()); ceph_assert(!key.empty()); ceph_assert(val.length() > 0); KeyValueDB::Transaction tx = db->get_transaction(); tx->set(prefix, key, val); int ret = db->submit_transaction_sync(tx); return (ret == 0); } bool StoreTool::rm(const string& prefix, const string& key) { ceph_assert(!prefix.empty()); ceph_assert(!key.empty()); KeyValueDB::Transaction tx = db->get_transaction(); tx->rmkey(prefix, key); int ret = db->submit_transaction_sync(tx); return (ret == 0); } bool StoreTool::rm_prefix(const string& prefix) { ceph_assert(!prefix.empty()); KeyValueDB::Transaction tx = db->get_transaction(); tx->rmkeys_by_prefix(prefix); int ret = db->submit_transaction_sync(tx); return (ret == 0); } void StoreTool::print_summary(const uint64_t total_keys, const uint64_t total_size, const uint64_t total_txs, const string& store_path, const string& other_path, const int duration) const { std::cout << "summary:" << std::endl; std::cout << " copied " << total_keys << " keys" << std::endl; std::cout << " used " << total_txs << " transactions" << std::endl; std::cout << " total size " << byte_u_t(total_size) << std::endl; std::cout << " from '" << store_path << "' to '" << other_path << "'" << std::endl; std::cout << " duration " << duration << " seconds" << std::endl; } int StoreTool::print_stats() const { ostringstream ostr; Formatter* f = Formatter::create("json-pretty", "json-pretty", "json-pretty"); int ret = -1; if (g_conf()->rocksdb_perf) { db->get_statistics(f); ostr << "db_statistics "; f->flush(ostr); ret = 0; } else { ostr << "db_statistics not enabled"; f->flush(ostr); } std::cout << ostr.str() << std::endl; delete f; return ret; } int StoreTool::copy_store_to(const string& type, const string& other_path, const int num_keys_per_tx, const string& other_type) { if (num_keys_per_tx <= 0) { std::cerr << "must specify a number of keys/tx > 0" << std::endl; return -EINVAL; } // open or create a leveldb store at @p other_path boost::scoped_ptr other; KeyValueDB *other_ptr = KeyValueDB::create(g_ceph_context, other_type, other_path); if (int err = other_ptr->create_and_open(std::cerr); err < 0) { return err; } other.reset(other_ptr); KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator(); it->seek_to_first(); uint64_t total_keys = 0; uint64_t total_size = 0; uint64_t total_txs = 0; auto duration = [start=coarse_mono_clock::now()] { const auto now = coarse_mono_clock::now(); auto seconds = std::chrono::duration(now - start); return seconds.count(); }; do { int num_keys = 0; KeyValueDB::Transaction tx = other->get_transaction(); while (it->valid() && num_keys < num_keys_per_tx) { auto [prefix, key] = it->raw_key(); bufferlist v = it->value(); tx->set(prefix, key, v); num_keys++; total_size += v.length(); it->next(); } total_txs++; total_keys += num_keys; if (num_keys > 0) other->submit_transaction_sync(tx); std::cout << "ts = " << duration() << "s, copied " << total_keys << " keys so far (" << byte_u_t(total_size) << ")" << std::endl; } while (it->valid()); print_summary(total_keys, total_size, total_txs, store_path, other_path, duration()); return 0; } void StoreTool::compact() { db->compact(); } void StoreTool::compact_prefix(const string& prefix) { db->compact_prefix(prefix); } void StoreTool::compact_range(const string& prefix, const string& start, const string& end) { db->compact_range(prefix, start, end); } int StoreTool::destructive_repair() { return db->repair(std::cout); }