Kagome
Polkadot Runtime Engine in C++17
rocksdb.cpp
Go to the documentation of this file.
1 
7 
8 #include <boost/filesystem.hpp>
9 
15 
16 namespace kagome::storage {
17  namespace fs = boost::filesystem;
18 
19  RocksDB::RocksDB(bool prevent_destruction)
20  : prevent_destruction_(prevent_destruction) {
21  ro_.fill_cache = false;
22  }
23 
26  /*
27  * The following is a dirty workaround for a bug of rocksdb impl.
28  * Luckily, this happens when the whole app destroys, that is why
29  * the intentional memory leak would not affect normal operation.
30  *
31  * The hack is used to mitigate an issue of recovery mode run.
32  */
33  std::ignore = db_.release();
34  }
35  }
36 
37  outcome::result<std::unique_ptr<RocksDB>> RocksDB::create(
38  const boost::filesystem::path &path,
39  rocksdb::Options options,
40  bool prevent_destruction) {
43  }
44 
45  rocksdb::DB *db = nullptr;
46 
47  auto log = log::createLogger("RocksDB", "storage");
48  auto absolute_path = fs::absolute(path, fs::current_path());
49 
50  boost::system::error_code ec;
51  if (not fs::create_directory(absolute_path.native(), ec) and ec.value()) {
52  log->error("Can't create directory {} for database: {}",
53  absolute_path.native(),
54  ec.message());
56  }
57  if (not fs::is_directory(absolute_path.native())) {
58  log->error("Can't open {} for database: is not a directory",
59  absolute_path.native());
61  }
62 
63  auto status = rocksdb::DB::Open(options, path.native(), &db);
64  if (status.ok()) {
65  std::unique_ptr<RocksDB> l{new RocksDB(prevent_destruction)};
66  l->db_ = std::unique_ptr<rocksdb::DB>(db);
67  l->logger_ = std::move(log);
68  return l;
69  }
70 
71  SL_ERROR(log,
72  "Can't open database in {}: {}",
73  absolute_path.native(),
74  status.ToString());
75 
76  return status_as_error(status);
77  }
78 
79  std::unique_ptr<BufferBatch> RocksDB::batch() {
80  return std::make_unique<Batch>(*this);
81  }
82 
83  size_t RocksDB::size() const {
84  size_t usage_bytes = 0;
85  if (db_) {
86  std::string usage;
87  bool result = db_->GetProperty("rocksdb.cur-size-all-mem-tables", &usage);
88  if (result) {
89  try {
90  usage_bytes = std::stoul(usage);
91  } catch (...) {
92  logger_->error("Unable to parse memory usage value");
93  }
94  } else {
95  logger_->error("Unable to retrieve memory usage value");
96  }
97  }
98  return usage_bytes;
99  }
100 
101  std::unique_ptr<RocksDB::Cursor> RocksDB::cursor() {
102  auto it = std::unique_ptr<rocksdb::Iterator>(db_->NewIterator(ro_));
103  return std::make_unique<RocksDBCursor>(std::move(it));
104  }
105 
106  outcome::result<bool> RocksDB::contains(const BufferView &key) const {
107  std::string value;
108  auto status = db_->Get(ro_, make_slice(key), &value);
109  if (status.ok()) {
110  return true;
111  }
112 
113  if (status.IsNotFound()) {
114  return false;
115  }
116 
117  return status_as_error(status);
118  }
119 
120  bool RocksDB::empty() const {
121  auto it = std::unique_ptr<rocksdb::Iterator>(db_->NewIterator(ro_));
122  it->SeekToFirst();
123  return it->Valid();
124  }
125 
126  outcome::result<Buffer> RocksDB::load(const BufferView &key) const {
127  std::string value;
128  auto status = db_->Get(ro_, make_slice(key), &value);
129  if (status.ok()) {
130  // cannot move string content to a buffer
131  return Buffer(
132  reinterpret_cast<uint8_t *>(value.data()), // NOLINT
133  reinterpret_cast<uint8_t *>(value.data()) + value.size()); // NOLINT
134  }
135  return status_as_error(status);
136  }
137 
138  outcome::result<std::optional<Buffer>> RocksDB::tryLoad(
139  const BufferView &key) const {
140  std::string value;
141  auto status = db_->Get(ro_, make_slice(key), &value);
142  if (status.ok()) {
143  return std::make_optional(Buffer(
144  reinterpret_cast<uint8_t *>(value.data()), // NOLINT
145  reinterpret_cast<uint8_t *>(value.data()) + value.size())); // NOLINT
146  }
147 
148  if (status.IsNotFound()) {
149  return std::nullopt;
150  }
151 
152  return status_as_error(status);
153  }
154 
155  outcome::result<void> RocksDB::put(const BufferView &key,
156  const Buffer &value) {
157  auto status = db_->Put(wo_, make_slice(key), make_slice(value));
158  if (status.ok()) {
159  return outcome::success();
160  }
161 
162  return status_as_error(status);
163  }
164 
165  outcome::result<void> RocksDB::put(const BufferView &key, Buffer &&value) {
166  Buffer copy(std::move(value));
167  return put(key, copy);
168  }
169 
170  outcome::result<void> RocksDB::remove(const BufferView &key) {
171  auto status = db_->Delete(wo_, make_slice(key));
172  if (status.ok()) {
173  return outcome::success();
174  }
175 
176  return status_as_error(status);
177  }
178 
179  void RocksDB::compact(const Buffer &first, const Buffer &last) {
180  if (db_) {
181  std::unique_ptr<rocksdb::Iterator> begin(db_->NewIterator(ro_));
182  first.empty() ? begin->SeekToFirst() : begin->Seek(make_slice(first));
183  auto bk = begin->key();
184  std::unique_ptr<rocksdb::Iterator> end(db_->NewIterator(ro_));
185  last.empty() ? end->SeekToLast() : end->Seek(make_slice(last));
186  auto ek = end->key();
187  rocksdb::CompactRangeOptions options;
188  db_->CompactRange(options, &bk, &ek);
189  }
190  }
191 } // namespace kagome::storage
void compact(const Buffer &first, const Buffer &last)
Definition: rocksdb.cpp:179
Class represents arbitrary (including empty) byte buffer.
Definition: buffer.hpp:29
rocksdb::WriteOptions wo_
Definition: rocksdb.hpp:67
outcome::result< void > remove(const BufferView &key) override
Definition: rocksdb.cpp:170
outcome::result< kagome::storage::Buffer > load(const Key &key) const override
Load value by key.
Definition: rocksdb.cpp:126
void usage()
DatabaseError status_as_error(const rocksdb::Status &s)
rocksdb::ReadOptions ro_
Definition: rocksdb.hpp:66
outcome::result< bool > contains(const Key &key) const override
Checks if given key-value binding exists in the storage.
Definition: rocksdb.cpp:106
std::unique_ptr< Cursor > cursor() override
Returns new key-value iterator.
Definition: rocksdb.cpp:101
outcome::result< std::optional< Buffer > > tryLoad(const Key &key) const override
Load value by key.
Definition: rocksdb.cpp:138
outcome::result< void > put(const BufferView &key, const Buffer &value) override
Definition: rocksdb.cpp:155
size_t size() const override
Definition: rocksdb.cpp:83
rocksdb::Slice make_slice(const common::BufferView &buf)
bool createDirectoryRecursive(const path &path)
Definition: directories.hpp:18
std::unique_ptr< rocksdb::DB > db_
Definition: rocksdb.hpp:65
common::SLBuffer< std::numeric_limits< size_t >::max()> Buffer
std::unique_ptr< BufferBatch > batch() override
Creates new Write Batch - an object, which can be used to efficiently write bulk data.
Definition: rocksdb.cpp:79
bool empty() const override
Returns true if the storage is empty.
Definition: rocksdb.cpp:120
RocksDB(bool prevent_destruction)
Definition: rocksdb.cpp:19
static outcome::result< std::unique_ptr< RocksDB > > create(const boost::filesystem::path &path, rocksdb::Options options=rocksdb::Options(), bool prevent_destruction=false)
Factory method to create an instance of RocksDB class.
Definition: rocksdb.cpp:37
Logger createLogger(const std::string &tag)
Definition: logger.cpp:112