1 #include "bmbase.c"
2 #include <lmdb.h>
3 #include <unistd.h>
4 #include <errno.h>
5
6 static_assert(sizeof(size_t) == 8, "sizeof(size_t) == 8 bytes");
7
8 #define E(expr_, ret_) \
9 if ((rc = (expr_)) != MDB_SUCCESS) { \
10 fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, mdb_strerror(rc)); \
11 return ret_; \
12 }
13
14 #define B(expr_) E(expr_, 0)
15
16 #define DEFAULT_DB "lmdb_bench.db"
17
18 typedef struct BM_LEVELDB {
19 MDB_env *env;
20 MDB_dbi dbi;
21 } BM_LMDB;
22
env_setup()23 static void env_setup() {
24 int major, minor, patch;
25 mdb_version(&major, &minor, &patch);
26 fprintf(stderr, " engine: LMDB %d.%d.%d\n", major, minor, patch);
27 }
28
db_size_bytes(BMCTX * ctx)29 uint64_t db_size_bytes(BMCTX *ctx) {
30 const char *path = bm.param_db ? bm.param_db : DEFAULT_DB;
31 IWP_FILE_STAT fst;
32 iwrc rc = iwp_fstat(path, &fst);
33 if (rc) {
34 iwlog_ecode_error3(rc);
35 return 0;
36 }
37 return fst.size;
38 }
39
db_open(BMCTX * ctx)40 static void *db_open(BMCTX *ctx) {
41 int rc;
42 if (ctx->db) {
43 return 0; // db is not closed properly
44 }
45 const char *path = bm.param_db ? bm.param_db : DEFAULT_DB;
46 if (ctx->freshdb) { // completely remove db folder
47 rc = unlink(path);
48 if (rc && (errno != ENOENT)) {
49 E(errno, 0);
50 }
51 }
52 MDB_txn *txn;
53 BM_LMDB *bmdb = malloc(sizeof(*bmdb));
54 E(mdb_env_create(&bmdb->env), 0);
55 E(mdb_env_set_maxreaders(bmdb->env, 1), 0);
56 E(mdb_env_set_mapsize(bmdb->env, 1024ULL * 1024 * 1024 * 100), 0); // 100 GB
57 E(mdb_env_open(bmdb->env, path, MDB_FIXEDMAP | MDB_NOSUBDIR | MDB_NOSYNC, 0664), 0);
58 E(mdb_txn_begin(bmdb->env, NULL, 0, &txn), 0);
59 E(mdb_dbi_open(txn, NULL, 0, &bmdb->dbi), 0);
60 E(mdb_txn_commit(txn), 0);
61 return bmdb;
62 }
63
db_close(BMCTX * ctx)64 static bool db_close(BMCTX *ctx) {
65 int rc;
66 if (!ctx->db) {
67 return false;
68 }
69 BM_LMDB *bmdb = ctx->db;
70 B(mdb_env_sync(bmdb->env, 1));
71 mdb_dbi_close(bmdb->env, bmdb->dbi);
72 mdb_env_close(bmdb->env);
73 free(bmdb);
74 return true;
75 }
76
db_put(BMCTX * ctx,const IWKV_val * key,const IWKV_val * val,bool sync)77 static bool db_put(BMCTX *ctx, const IWKV_val *key, const IWKV_val *val, bool sync) {
78 int rc;
79 BM_LMDB *bmdb = ctx->db;
80 MDB_txn *txn;
81 MDB_val mkey, mval;
82 mkey.mv_data = key->data;
83 mkey.mv_size = key->size;
84 mval.mv_data = val->data;
85 mval.mv_size = val->size;
86 B(mdb_txn_begin(bmdb->env, NULL, 0, &txn));
87 B(mdb_put(txn, bmdb->dbi, &mkey, &mval, 0));
88 B(mdb_txn_commit(txn));
89 if (sync) {
90 B(mdb_env_sync(bmdb->env, 1));
91 }
92 return true;
93 }
94
db_get(BMCTX * ctx,const IWKV_val * key,IWKV_val * val,bool * found)95 static bool db_get(BMCTX *ctx, const IWKV_val *key, IWKV_val *val, bool *found) {
96 int rc;
97 MDB_txn *txn;
98 MDB_val mkey, mval;
99 BM_LMDB *bmdb = ctx->db;
100 mkey.mv_data = key->data;
101 mkey.mv_size = key->size;
102 B(mdb_txn_begin(bmdb->env, NULL, MDB_RDONLY, &txn));
103 rc = mdb_get(txn, bmdb->dbi, &mkey, &mval);
104 if (!rc) {
105 *found = true;
106 val->size = mval.mv_size;
107 val->data = malloc(val->size);
108 memcpy(val->data, mval.mv_data, mval.mv_size);
109 } else if (rc == MDB_NOTFOUND) {
110 *found = false;
111 rc = 0;
112 }
113 mdb_txn_abort(txn);
114 B(rc);
115 return true;
116 }
117
db_del(BMCTX * ctx,const IWKV_val * key,bool sync)118 static bool db_del(BMCTX *ctx, const IWKV_val *key, bool sync) {
119 int rc;
120 MDB_txn *txn;
121 MDB_val mkey;
122 BM_LMDB *bmdb = ctx->db;
123 mkey.mv_data = key->data;
124 mkey.mv_size = key->size;
125 B(mdb_txn_begin(bmdb->env, NULL, 0, &txn));
126 rc = mdb_del(txn, bmdb->dbi, &mkey, 0);
127 if (rc == MDB_NOTFOUND) {
128 rc = 0;
129 }
130 B(mdb_txn_commit(txn));
131 if (sync) {
132 B(mdb_env_sync(bmdb->env, 1));
133 }
134 return true;
135 }
136
db_read_seq(BMCTX * ctx,bool reverse)137 static bool db_read_seq(BMCTX *ctx, bool reverse) {
138 int rc = 0;
139 MDB_txn *txn;
140 MDB_cursor *cur;
141 MDB_val mkey, mval;
142 BM_LMDB *bmdb = ctx->db;
143 B(mdb_txn_begin(bmdb->env, NULL, MDB_RDONLY, &txn));
144 B(mdb_cursor_open(txn, bmdb->dbi, &cur));
145 B(mdb_cursor_get(cur, &mkey, &mval, reverse ? MDB_LAST : MDB_FIRST));
146 for (int i = 0; i < bm.param_num - 1 && !rc; ++i) {
147 rc = mdb_cursor_get(cur, &mkey, &mval, reverse ? MDB_PREV : MDB_NEXT);
148 }
149 if (rc == MDB_NOTFOUND) {
150 rc = 0;
151 }
152 mdb_cursor_close(cur);
153 mdb_txn_abort(txn);
154 B(rc);
155 return true;
156 }
157
db_cursor_to_key(BMCTX * ctx,const IWKV_val * key,IWKV_val * val,bool * found)158 static bool db_cursor_to_key(BMCTX *ctx, const IWKV_val *key, IWKV_val *val, bool *found) {
159 int rc = 0;
160 MDB_txn *txn;
161 MDB_cursor *cur;
162 MDB_val mkey, mval;
163 BM_LMDB *bmdb = ctx->db;
164 mkey.mv_data = key->data;
165 mkey.mv_size = key->size;
166 B(mdb_txn_begin(bmdb->env, NULL, MDB_RDONLY, &txn));
167 B(mdb_cursor_open(txn, bmdb->dbi, &cur));
168 rc = mdb_cursor_get(cur, &mkey, &mval, MDB_SET);
169 if (rc == MDB_NOTFOUND) {
170 *found = false;
171 val->data = 0;
172 val->size = 0;
173 rc = 0;
174 } else if (!rc) {
175 *found = true;
176 val->data = malloc(mval.mv_size);
177 ;
178 memcpy(val->data, mval.mv_data, mval.mv_size);
179 }
180 mdb_cursor_close(cur);
181 mdb_txn_abort(txn);
182 B(rc);
183 return true;
184 }
185
main(int argc,char ** argv)186 int main(int argc, char **argv) {
187 if (argc < 1) {
188 return -1;
189 }
190 g_program = argv[0];
191 bm.env_setup = env_setup;
192 bm.db_size_bytes = db_size_bytes;
193 bm.db_open = db_open;
194 bm.db_close = db_close;
195 bm.db_put = db_put;
196 bm.db_get = db_get;
197 bm.db_del = db_del;
198 bm.db_read_seq = db_read_seq;
199 bm.db_cursor_to_key = db_cursor_to_key;
200 if (!bm_bench_run(argc, argv)) {
201 return 1;
202 }
203 return 0;
204 }
205