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  virtual ~LMDBCursor() {
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() {
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  DISABLE_COPY_AND_ASSIGN(LMDBTransaction);
118 };
119 
120 class LMDB : public DB {
121  public:
122  LMDB(const string& source, Mode mode);
123  virtual ~LMDB() { Close(); }
124  void Close() override {
125  if (mdb_env_ != NULL) {
126  mdb_env_close(mdb_env_);
127  mdb_env_ = NULL;
128  }
129  }
130  unique_ptr<Cursor> NewCursor() override {
131  return make_unique<LMDBCursor>(mdb_env_);
132  }
133  unique_ptr<Transaction> NewTransaction() override {
134  return make_unique<LMDBTransaction>(mdb_env_);
135  }
136 
137  private:
138  MDB_env* mdb_env_;
139 };
140 
141 LMDB::LMDB(const string& source, Mode mode) : DB(source, mode) {
142  MDB_CHECK(mdb_env_create(&mdb_env_));
143  MDB_CHECK(mdb_env_set_mapsize(mdb_env_, LMDB_MAP_SIZE));
144  if (mode == NEW) {
145 #if defined(_MSC_VER)
146  CAFFE_ENFORCE_EQ(_mkdir(source.c_str()), 0, "mkdir ", source, " failed");
147 #else
148  CAFFE_ENFORCE_EQ(
149  mkdir(source.c_str(), 0744), 0, "mkdir ", source, " failed");
150 #endif
151  }
152  int flags = 0;
153  if (mode == READ) {
154  flags = MDB_RDONLY | MDB_NOTLS | MDB_NOLOCK;
155  }
156  MDB_CHECK(mdb_env_open(mdb_env_, source.c_str(), flags, 0664));
157  VLOG(1) << "Opened lmdb " << source;
158 }
159 
160 void LMDBTransaction::Put(const string& key, const string& value) {
161  MDB_val mdb_key, mdb_value;
162  mdb_key.mv_data = const_cast<char*>(key.data());
163  mdb_key.mv_size = key.size();
164  mdb_value.mv_data = const_cast<char*>(value.data());
165  mdb_value.mv_size = value.size();
166  MDB_CHECK(mdb_put(mdb_txn_, mdb_dbi_, &mdb_key, &mdb_value, 0));
167 }
168 
169 REGISTER_CAFFE2_DB(LMDB, LMDB);
170 REGISTER_CAFFE2_DB(lmdb, LMDB);
171 
172 } // namespace db
173 } // namespace caffe2
void Put(const string &key, const string &value) override
Puts the key value pair to the database.
Definition: lmdb.cc:160
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:124
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:133
unique_ptr< Cursor > NewCursor() override
Returns a cursor to read the database.
Definition: lmdb.cc:130
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
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