Caffe2 - C++ API
A deep learning, cross platform ML framework
lmdb.cc
1 #include "lmdb.h" // NOLINT
2 
3 #if defined(_MSC_VER)
4 #include <direct.h>
5 #endif
6 
7 #include <sys/stat.h>
8 
9 #include <string>
10 
11 #include "caffe2/core/db.h"
12 #include "caffe2/core/logging.h"
13 
14 namespace caffe2 {
15 namespace db {
16 
17 constexpr size_t LMDB_MAP_SIZE = 1099511627776; // 1 TB
18 
19 inline void MDB_CHECK(int mdb_status) {
20  CAFFE_ENFORCE_EQ(mdb_status, MDB_SUCCESS, mdb_strerror(mdb_status));
21 }
22 
23 class LMDBCursor : public Cursor {
24  public:
25  explicit LMDBCursor(MDB_env* mdb_env)
26  : mdb_env_(mdb_env), valid_(false) {
27  MDB_CHECK(mdb_txn_begin(mdb_env_, NULL, MDB_RDONLY, &mdb_txn_));
28  MDB_CHECK(mdb_dbi_open(mdb_txn_, NULL, 0, &mdb_dbi_));
29  MDB_CHECK(mdb_cursor_open(mdb_txn_, mdb_dbi_, &mdb_cursor_));
30  SeekToFirst();
31  }
32  ~LMDBCursor() override {
33  mdb_cursor_close(mdb_cursor_);
34  mdb_dbi_close(mdb_env_, mdb_dbi_);
35  mdb_txn_abort(mdb_txn_);
36  }
37 
38  void Seek(const string& key) override {
39  if (key.size() == 0) {
40  SeekToFirst();
41  return;
42  }
43  // a key of 16k size should be enough? I am not sure though.
44  mdb_key_.mv_size = key.size();
45  mdb_key_.mv_data = const_cast<char*>(key.c_str());
46  int mdb_status = mdb_cursor_get(
47  mdb_cursor_, &mdb_key_, &mdb_value_, MDB_SET_RANGE);
48  if (mdb_status == MDB_NOTFOUND) {
49  valid_ = false;
50  } else {
51  MDB_CHECK(mdb_status);
52  valid_ = true;
53  }
54  }
55 
56  bool SupportsSeek() override { return true; }
57 
58  void SeekToFirst() override { SeekLMDB(MDB_FIRST); }
59 
60  void Next() override { SeekLMDB(MDB_NEXT); }
61 
62  string key() override {
63  return string(static_cast<const char*>(mdb_key_.mv_data), mdb_key_.mv_size);
64  }
65 
66  string value() override {
67  return string(static_cast<const char*>(mdb_value_.mv_data),
68  mdb_value_.mv_size);
69  }
70 
71  bool Valid() override { return valid_; }
72 
73  private:
74  void SeekLMDB(MDB_cursor_op op) {
75  int mdb_status = mdb_cursor_get(mdb_cursor_, &mdb_key_, &mdb_value_, op);
76  if (mdb_status == MDB_NOTFOUND) {
77  valid_ = false;
78  } else {
79  MDB_CHECK(mdb_status);
80  valid_ = true;
81  }
82  }
83 
84  MDB_env* mdb_env_;
85  MDB_txn* mdb_txn_;
86  MDB_dbi mdb_dbi_;
87  MDB_cursor* mdb_cursor_;
88  MDB_val mdb_key_, mdb_value_;
89  bool valid_;
90 };
91 
92 class LMDBTransaction final : public Transaction {
93  public:
94  explicit LMDBTransaction(MDB_env* mdb_env)
95  : mdb_env_(mdb_env) {
96  MDB_CHECK(mdb_txn_begin(mdb_env_, NULL, 0, &mdb_txn_));
97  MDB_CHECK(mdb_dbi_open(mdb_txn_, NULL, 0, &mdb_dbi_));
98  }
99  ~LMDBTransaction() override {
100  MDB_CHECK(mdb_txn_commit(mdb_txn_));
101  mdb_dbi_close(mdb_env_, mdb_dbi_);
102  }
103  void Put(const string& key, const string& value) override;
104  void Commit() override {
105  MDB_CHECK(mdb_txn_commit(mdb_txn_));
106  mdb_dbi_close(mdb_env_, mdb_dbi_);
107  // Begin a new transaction.
108  MDB_CHECK(mdb_txn_begin(mdb_env_, NULL, 0, &mdb_txn_));
109  MDB_CHECK(mdb_dbi_open(mdb_txn_, NULL, 0, &mdb_dbi_));
110  }
111 
112  private:
113  MDB_env* mdb_env_;
114  MDB_dbi mdb_dbi_;
115  MDB_txn* mdb_txn_;
116 
117  C10_DISABLE_COPY_AND_ASSIGN(LMDBTransaction);
118 };
119 
120 class LMDB : public DB {
121  public:
122  LMDB(const string& source, Mode mode);
123  ~LMDB() override {
124  Close();
125  }
126  void Close() override {
127  if (mdb_env_ != NULL) {
128  mdb_env_close(mdb_env_);
129  mdb_env_ = NULL;
130  }
131  }
132  unique_ptr<Cursor> NewCursor() override {
133  return make_unique<LMDBCursor>(mdb_env_);
134  }
135  unique_ptr<Transaction> NewTransaction() override {
136  return make_unique<LMDBTransaction>(mdb_env_);
137  }
138 
139  private:
140  MDB_env* mdb_env_;
141 };
142 
143 LMDB::LMDB(const string& source, Mode mode) : DB(source, mode) {
144  MDB_CHECK(mdb_env_create(&mdb_env_));
145  MDB_CHECK(mdb_env_set_mapsize(mdb_env_, LMDB_MAP_SIZE));
146  if (mode == NEW) {
147 #if defined(_MSC_VER)
148  CAFFE_ENFORCE_EQ(_mkdir(source.c_str()), 0, "mkdir ", source, " failed");
149 #else
150  CAFFE_ENFORCE_EQ(
151  mkdir(source.c_str(), 0744), 0, "mkdir ", source, " failed");
152 #endif
153  }
154  int flags = 0;
155  if (mode == READ) {
156  flags = MDB_RDONLY | MDB_NOTLS | MDB_NOLOCK;
157  }
158  MDB_CHECK(mdb_env_open(mdb_env_, source.c_str(), flags, 0664));
159  VLOG(1) << "Opened lmdb " << source;
160 }
161 
162 void LMDBTransaction::Put(const string& key, const string& value) {
163  MDB_val mdb_key, mdb_value;
164  mdb_key.mv_data = const_cast<char*>(key.data());
165  mdb_key.mv_size = key.size();
166  mdb_value.mv_data = const_cast<char*>(value.data());
167  mdb_value.mv_size = value.size();
168  MDB_CHECK(mdb_put(mdb_txn_, mdb_dbi_, &mdb_key, &mdb_value, 0));
169 }
170 
171 REGISTER_CAFFE2_DB(LMDB, LMDB);
172 REGISTER_CAFFE2_DB(lmdb, LMDB);
173 
174 } // namespace db
175 } // namespace caffe2
void Put(const string &key, const string &value) override
Puts the key value pair to the database.
Definition: lmdb.cc:162
An abstract class for the current database transaction while writing.
Definition: db.h:61
An abstract class for the cursor of the database while reading.
Definition: db.h:22
void SeekToFirst() override
Seek to the first key in the database.
Definition: lmdb.cc:58
void Close() override
Closes the database.
Definition: lmdb.cc:126
void Commit() override
Commits the current writes.
Definition: lmdb.cc:104
string key() override
Returns the current key.
Definition: lmdb.cc:62
string value() override
Returns the current value.
Definition: lmdb.cc:66
void Seek(const string &key) override
Seek to a specific key (or if the key does not exist, seek to the immediate next).
Definition: lmdb.cc:38
unique_ptr< Transaction > NewTransaction() override
Returns a transaction to write data to the database.
Definition: lmdb.cc:135
unique_ptr< Cursor > NewCursor() override
Returns a cursor to read the database.
Definition: lmdb.cc:132
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
An abstract class for accessing a database of key-value pairs.
Definition: db.h:80
void Next() override
Go to the next location in the database.
Definition: lmdb.cc:60
bool Valid() override
Returns whether the current location is valid - for example, if we have reached the end of the databa...
Definition: lmdb.cc:71