diff options
author | Aaron Seigo <aseigo@kde.org> | 2014-12-05 09:17:46 +0100 |
---|---|---|
committer | Aaron Seigo <aseigo@kde.org> | 2014-12-05 09:17:46 +0100 |
commit | 767312e2063f4e58af3de0f27aba52de49e14295 (patch) | |
tree | 8375b55e3496ece33f59de486f3354d731b6bc1e /common/storage_lmdb.cpp | |
parent | 2b4e5743cca6a59e6e1a32b03863bf5ec4a4c30f (diff) | |
download | sink-767312e2063f4e58af3de0f27aba52de49e14295.tar.gz sink-767312e2063f4e58af3de0f27aba52de49e14295.zip |
major reorg that puts Storage (previously Database) into common
there is now a top-level tests dir, and a compile time switch for
lmdb vs kyotocabinet
Diffstat (limited to 'common/storage_lmdb.cpp')
-rw-r--r-- | common/storage_lmdb.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp new file mode 100644 index 0000000..6c25448 --- /dev/null +++ b/common/storage_lmdb.cpp | |||
@@ -0,0 +1,266 @@ | |||
1 | #include "storage.h" | ||
2 | |||
3 | #include <iostream> | ||
4 | |||
5 | #include <QAtomicInt> | ||
6 | #include <QDebug> | ||
7 | #include <QDir> | ||
8 | #include <QReadWriteLock> | ||
9 | #include <QString> | ||
10 | #include <QTime> | ||
11 | |||
12 | #include <lmdb.h> | ||
13 | |||
14 | class Storage::Private | ||
15 | { | ||
16 | public: | ||
17 | Private(const QString &p); | ||
18 | ~Private(); | ||
19 | |||
20 | QString path; | ||
21 | MDB_dbi dbi; | ||
22 | MDB_env *env; | ||
23 | MDB_txn *transaction; | ||
24 | bool readTransaction; | ||
25 | bool firstOpen; | ||
26 | }; | ||
27 | |||
28 | Storage::Private::Private(const QString &p) | ||
29 | : path(p), | ||
30 | transaction(0), | ||
31 | readTransaction(false), | ||
32 | firstOpen(true) | ||
33 | { | ||
34 | QDir dir; | ||
35 | dir.mkdir(path); | ||
36 | |||
37 | //create file | ||
38 | if (mdb_env_create(&env)) { | ||
39 | // TODO: handle error | ||
40 | } else { | ||
41 | int rc = mdb_env_open(env, path.toStdString().data(), 0, 0664); | ||
42 | |||
43 | if (rc) { | ||
44 | std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl; | ||
45 | mdb_env_close(env); | ||
46 | env = 0; | ||
47 | } else { | ||
48 | const size_t dbSize = 10485760 * 100; //10MB * 100 | ||
49 | mdb_env_set_mapsize(env, dbSize); | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | Storage::Private::~Private() | ||
55 | { | ||
56 | if (transaction) { | ||
57 | mdb_txn_abort(transaction); | ||
58 | } | ||
59 | |||
60 | // it is still there and still unused, so we can shut it down | ||
61 | mdb_dbi_close(env, dbi); | ||
62 | mdb_env_close(env); | ||
63 | } | ||
64 | |||
65 | Storage::Storage(const QString &storageRoot, const QString &name) | ||
66 | : d(new Private(storageRoot + '/' + name)) | ||
67 | { | ||
68 | } | ||
69 | |||
70 | Storage::~Storage() | ||
71 | { | ||
72 | delete d; | ||
73 | } | ||
74 | |||
75 | bool Storage::isInTransaction() const | ||
76 | { | ||
77 | return d->transaction; | ||
78 | } | ||
79 | |||
80 | bool Storage::startTransaction(TransactionType type) | ||
81 | { | ||
82 | if (!d->env) { | ||
83 | return false; | ||
84 | } | ||
85 | |||
86 | bool requestedRead = type == ReadOnly; | ||
87 | if (d->transaction && (!d->readTransaction || requestedRead)) { | ||
88 | return true; | ||
89 | } | ||
90 | |||
91 | if (d->transaction) { | ||
92 | // we are about to turn a read transaction into a writable one | ||
93 | abortTransaction(); | ||
94 | } | ||
95 | |||
96 | if (d->firstOpen && requestedRead) { | ||
97 | //A write transaction is at least required the first time | ||
98 | mdb_txn_begin(d->env, nullptr, 0, &d->transaction); | ||
99 | //Open the database | ||
100 | //With this we could open multiple named databases if we wanted to | ||
101 | mdb_dbi_open(d->transaction, nullptr, 0, &d->dbi); | ||
102 | mdb_txn_abort(d->transaction); | ||
103 | } | ||
104 | |||
105 | int rc; | ||
106 | rc = mdb_txn_begin(d->env, NULL, requestedRead ? MDB_RDONLY : 0, &d->transaction); | ||
107 | if (!rc) { | ||
108 | rc = mdb_dbi_open(d->transaction, NULL, 0, &d->dbi); | ||
109 | } | ||
110 | |||
111 | d->firstOpen = false; | ||
112 | return !rc; | ||
113 | } | ||
114 | |||
115 | bool Storage::commitTransaction() | ||
116 | { | ||
117 | if (!d->env) { | ||
118 | return false; | ||
119 | } | ||
120 | |||
121 | if (!d->transaction) { | ||
122 | return false; | ||
123 | } | ||
124 | |||
125 | int rc; | ||
126 | rc = mdb_txn_commit(d->transaction); | ||
127 | d->transaction = 0; | ||
128 | |||
129 | if (rc) { | ||
130 | std::cerr << "mdb_txn_commit: " << rc << " " << mdb_strerror(rc) << std::endl; | ||
131 | } | ||
132 | |||
133 | return !rc; | ||
134 | } | ||
135 | |||
136 | void Storage::abortTransaction() | ||
137 | { | ||
138 | if (!d->env || !d->transaction) { | ||
139 | return; | ||
140 | } | ||
141 | |||
142 | mdb_txn_abort(d->transaction); | ||
143 | d->transaction = 0; | ||
144 | } | ||
145 | |||
146 | bool Storage::write(const std::string &sKey, const std::string &sValue) | ||
147 | { | ||
148 | if (!d->env) { | ||
149 | return false; | ||
150 | } | ||
151 | |||
152 | const bool implicitTransaction = !d->transaction || d->readTransaction; | ||
153 | if (implicitTransaction) { | ||
154 | // TODO: if this fails, still try the write below? | ||
155 | if (!startTransaction()) { | ||
156 | return false; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | int rc; | ||
161 | MDB_val key, data; | ||
162 | key.mv_size = sKey.size(); | ||
163 | key.mv_data = (void*)sKey.data(); | ||
164 | data.mv_size = sValue.size(); | ||
165 | data.mv_data = (void*)sValue.data(); | ||
166 | rc = mdb_put(d->transaction, d->dbi, &key, &data, 0); | ||
167 | |||
168 | if (rc) { | ||
169 | std::cerr << "mdb_put: " << rc << " " << mdb_strerror(rc) << std::endl; | ||
170 | } | ||
171 | |||
172 | if (implicitTransaction) { | ||
173 | if (rc) { | ||
174 | abortTransaction(); | ||
175 | } else { | ||
176 | rc = commitTransaction(); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | return !rc; | ||
181 | } | ||
182 | |||
183 | void Storage::read(const std::string &sKey, const std::function<void(const std::string &value)> &resultHandler) | ||
184 | { | ||
185 | read(sKey, | ||
186 | [&](void *ptr, int size) { | ||
187 | const std::string resultValue(static_cast<char*>(ptr), size); | ||
188 | resultHandler(resultValue); | ||
189 | }); | ||
190 | // std::cout << "key: " << resultKey << " data: " << resultValue << std::endl; | ||
191 | } | ||
192 | |||
193 | void Storage::read(const std::string &sKey, const std::function<void(void *ptr, int size)> &resultHandler) | ||
194 | { | ||
195 | if (!d->env) { | ||
196 | return; | ||
197 | } | ||
198 | |||
199 | int rc; | ||
200 | MDB_val key; | ||
201 | MDB_val data; | ||
202 | MDB_cursor *cursor; | ||
203 | |||
204 | key.mv_size = sKey.size(); | ||
205 | key.mv_data = (void*)sKey.data(); | ||
206 | |||
207 | const bool implicitTransaction = !d->transaction; | ||
208 | if (implicitTransaction) { | ||
209 | // TODO: if this fails, still try the write below? | ||
210 | if (!startTransaction(ReadOnly)) { | ||
211 | return; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | rc = mdb_cursor_open(d->transaction, d->dbi, &cursor); | ||
216 | if (rc) { | ||
217 | std::cerr << "mdb_cursor_get: " << rc << " " << mdb_strerror(rc) << std::endl; | ||
218 | return; | ||
219 | } | ||
220 | |||
221 | if (sKey.empty()) { | ||
222 | std::cout << "Iterating over all values of store!" << std::endl; | ||
223 | rc = mdb_cursor_get(cursor, &key, &data, MDB_FIRST); | ||
224 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { | ||
225 | resultHandler(key.mv_data, data.mv_size); | ||
226 | } | ||
227 | |||
228 | //We never find the last value | ||
229 | if (rc == MDB_NOTFOUND) { | ||
230 | rc = 0; | ||
231 | } | ||
232 | } else { | ||
233 | if ((rc = mdb_cursor_get(cursor, &key, &data, MDB_SET)) == 0) { | ||
234 | resultHandler(data.mv_data, data.mv_size); | ||
235 | } else { | ||
236 | std::cout << "couldn't find value " << sKey << " " << std::endl; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | if (rc) { | ||
241 | std::cerr << "mdb_cursor_get: " << rc << " " << mdb_strerror(rc) << std::endl; | ||
242 | } | ||
243 | |||
244 | mdb_cursor_close(cursor); | ||
245 | |||
246 | /** | ||
247 | we don't abort the transaction since we need it for reading the values | ||
248 | if (implicitTransaction) { | ||
249 | abortTransaction(); | ||
250 | } | ||
251 | */ | ||
252 | } | ||
253 | |||
254 | qint64 Storage::diskUsage() const | ||
255 | { | ||
256 | QFileInfo info(d->path, "data.mdb"); | ||
257 | return info.size(); | ||
258 | } | ||
259 | |||
260 | void Storage::removeFromDisk() const | ||
261 | { | ||
262 | QDir dir(d->path); | ||
263 | dir.remove("data.mdb"); | ||
264 | dir.remove("lock.mdb"); | ||
265 | } | ||
266 | |||