summaryrefslogtreecommitdiffstats
path: root/store/database.cpp
diff options
context:
space:
mode:
authorAaron Seigo <aseigo@kde.org>2014-12-04 15:49:02 +0100
committerAaron Seigo <aseigo@kde.org>2014-12-04 15:49:02 +0100
commitef3b24358e508c5220d8b2548ec7207936794c66 (patch)
treea6cdaf531e9ba3b7d7c6c8030bad0fed102ca1a5 /store/database.cpp
parent66f871ca418e099dbb13614a613e78e556292c0b (diff)
downloadsink-ef3b24358e508c5220d8b2548ec7207936794c66.tar.gz
sink-ef3b24358e508c5220d8b2548ec7207936794c66.zip
hide the implementation detail of which key/value store we use
also makes transactions "implicit" where necessary. this will make trying out other k/v stores a bit easier now as well.
Diffstat (limited to 'store/database.cpp')
-rw-r--r--store/database.cpp239
1 files changed, 156 insertions, 83 deletions
diff --git a/store/database.cpp b/store/database.cpp
index 2d93266..22d0693 100644
--- a/store/database.cpp
+++ b/store/database.cpp
@@ -1,134 +1,188 @@
1#include "database.h" 1#include "database.h"
2 2
3#include <iostream> 3#include <iostream>
4
5#include <QAtomicInt>
6#include <QDebug>
4#include <QDir> 7#include <QDir>
8#include <QReadWriteLock>
5#include <QString> 9#include <QString>
6#include <QTime> 10#include <QTime>
7#include <qdebug.h>
8 11
9Database::Database(const QString &path) 12#include <lmdb.h>
13
14class Database::Private
10{ 15{
11 int rc; 16public:
17 Private(const QString &path);
18 ~Private();
12 19
20 MDB_dbi dbi;
21 MDB_env *env;
22 MDB_txn *transaction;
23 bool readTransaction;
24};
25
26Database::Private::Private(const QString &path)
27 : transaction(0),
28 readTransaction(false)
29{
13 QDir dir; 30 QDir dir;
14 dir.mkdir(path); 31 dir.mkdir(path);
15 32
16 //create file 33 //create file
17 rc = mdb_env_create(&env); 34 if (mdb_env_create(&env)) {
18 rc = mdb_env_open(env, path.toStdString().data(), 0, 0664); 35 // TODO: handle error
19 const int dbSize = 10485760*100; //10MB * 100 36 } else {
20 mdb_env_set_mapsize(env, dbSize); 37 int rc = mdb_env_open(env, path.toStdString().data(), 0, 0664);
21 38
22 if (rc) { 39 if (rc) {
23 std::cerr << "mdb_env_open: " << rc << mdb_strerror(rc) << std::endl; 40 std::cerr << "mdb_env_open: " << rc << mdb_strerror(rc) << std::endl;
41 mdb_env_close(env);
42 env = 0;
43 } else {
44 const int dbSize = 10485760*100; //10MB * 100
45 mdb_env_set_mapsize(env, dbSize);
46 }
24 } 47 }
25} 48}
26 49
27Database::~Database() 50Database::Private::~Private()
28{ 51{
29 mdb_close(env, dbi); 52 if (transaction) {
53 mdb_txn_abort(transaction);
54 }
55
56 // it is still there and still unused, so we can shut it down
57 mdb_dbi_close(env, dbi);
30 mdb_env_close(env); 58 mdb_env_close(env);
31} 59}
32 60
33MDB_txn *Database::startTransaction() 61Database::Database(const QString &path)
62 : d(new Private(path))
63{
64}
65
66Database::~Database()
67{
68 delete d;
69}
70
71bool Database::isInTransaction() const
72{
73 return d->transaction;
74}
75
76bool Database::startTransaction(TransactionType type)
34{ 77{
78 if (!d->env) {
79 return false;
80 }
81
82 bool requestedRead = type == ReadOnly;
83 if (d->transaction && (!d->readTransaction || requestedRead)) {
84 return true;
85 }
86
87 if (d->transaction) {
88 // we are about to turn a read transaction into a writable one
89 abortTransaction();
90 }
91
92 //TODO handle errors
35 int rc; 93 int rc;
36 MDB_txn *transaction; 94 rc = mdb_txn_begin(d->env, NULL, requestedRead ? MDB_RDONLY : 0, &d->transaction);
37 rc = mdb_txn_begin(env, NULL, 0, &transaction); 95 if (!rc) {
38 rc = mdb_open(transaction, NULL, 0, &dbi); 96 rc = mdb_dbi_open(d->transaction, NULL, 0, &d->dbi);
39 return transaction; 97 }
98
99 return !rc;
40} 100}
41 101
42void Database::endTransaction(MDB_txn *transaction) 102bool Database::commitTransaction()
43{ 103{
104 if (!d->env) {
105 return false;
106 }
107
108 if (!d->transaction) {
109 return false;
110 }
111
44 int rc; 112 int rc;
45 rc = mdb_txn_commit(transaction); 113 rc = mdb_txn_commit(d->transaction);
114 d->transaction = 0;
115
46 if (rc) { 116 if (rc) {
47 std::cerr << "mdb_txn_commit: " << rc << mdb_strerror(rc) << std::endl; 117 std::cerr << "mdb_txn_commit: " << rc << mdb_strerror(rc) << std::endl;
48 } 118 }
119
120 return !rc;
49} 121}
50 122
123void Database::abortTransaction()
124{
125 if (!d->env || !d->transaction) {
126 return;
127 }
128
129 mdb_txn_abort(d->transaction);
130 d->transaction = 0;
131}
51 132
52void Database::write(const std::string &sKey, const std::string &sValue, MDB_txn *transaction) 133bool Database::write(const std::string &sKey, const std::string &sValue)
53{ 134{
135 if (!d->env) {
136 return false;
137 }
138
139 const bool implicitTransaction = !d->transaction || d->readTransaction;
140 if (implicitTransaction) {
141 // TODO: if this fails, still try the write below?
142 if (!startTransaction()) {
143 return false;
144 }
145 }
146
54 int rc; 147 int rc;
55 MDB_val key, data; 148 MDB_val key, data;
56 key.mv_size = sKey.size(); 149 key.mv_size = sKey.size();
57 key.mv_data = (void*)sKey.data(); 150 key.mv_data = (void*)sKey.data();
58 data.mv_size = sValue.size(); 151 data.mv_size = sValue.size();
59 data.mv_data = (void*)sValue.data(); 152 data.mv_data = (void*)sValue.data();
60 rc = mdb_put(transaction, dbi, &key, &data, 0); 153 rc = mdb_put(d->transaction, d->dbi, &key, &data, 0);
154
61 if (rc) { 155 if (rc) {
62 std::cerr << "mdb_put: " << rc << mdb_strerror(rc) << std::endl; 156 std::cerr << "mdb_put: " << rc << mdb_strerror(rc) << std::endl;
63 } 157 }
64}
65 158
66void Database::read(const std::string &sKey, const std::function<void(const std::string)> &resultHandler) 159 if (implicitTransaction) {
67{ 160 if (rc) {
68 int rc; 161 abortTransaction();
69 MDB_txn *txn;
70 MDB_val key;
71 MDB_val data;
72 MDB_cursor *cursor;
73
74 key.mv_size = sKey.size();
75 key.mv_data = (void*)sKey.data();
76
77 rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
78 rc = mdb_cursor_open(txn, dbi, &cursor);
79 if (sKey.empty()) {
80 std::cout << "Iterating over all values of store!" << std::endl;
81 while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
82 const std::string resultKey(static_cast<char*>(key.mv_data), key.mv_size);
83 const std::string resultValue(static_cast<char*>(data.mv_data), data.mv_size);
84 // std::cout << "key: " << resultKey << " data: " << resultValue << std::endl;
85 resultHandler(resultValue);
86 }
87 } else {
88 if ((rc = mdb_cursor_get(cursor, &key, &data, MDB_SET)) == 0) {
89 const std::string resultKey(static_cast<char*>(key.mv_data), key.mv_size);
90 const std::string resultValue(static_cast<char*>(data.mv_data), data.mv_size);
91 // std::cout << "key: " << resultKey << " data: " << resultValue << std::endl;
92 resultHandler(resultValue);
93 } else { 162 } else {
94 std::cout << "couldn't find value " << sKey << std::endl; 163 rc = commitTransaction();
95 } 164 }
96 } 165 }
97 if (rc) {
98 std::cerr << "mdb_cursor_get: " << rc << mdb_strerror(rc) << std::endl;
99 }
100 mdb_cursor_close(cursor);
101 mdb_txn_abort(txn);
102}
103 166
104 167 return !rc;
105
106ReadTransaction::ReadTransaction(const QString &path)
107{
108 int rc;
109
110 //create file
111 rc = mdb_env_create(&env);
112 //FIXME MDB_NOTLS required to so we can have multiple read-only transactions per resource?
113 rc = mdb_env_open(env, path.toStdString().data(), 0, 0664);
114 const int dbSize = 10485760*100; //10MB * 100
115 mdb_env_set_mapsize(env, dbSize);
116
117 if (rc) {
118 std::cerr << "mdb_env_open: " << rc << mdb_strerror(rc) << std::endl;
119 }
120} 168}
121 169
122ReadTransaction::~ReadTransaction() 170void Database::read(const std::string &sKey, const std::function<void(const std::string &value)> &resultHandler)
123{ 171{
124 mdb_txn_abort(txn); 172 read(sKey,
125 173 [&](void *ptr, int size) {
126 mdb_dbi_close(env, dbi); 174 const std::string resultValue(static_cast<char*>(ptr), size);
127 mdb_env_close(env); 175 resultHandler(resultValue);
176 });
177// std::cout << "key: " << resultKey << " data: " << resultValue << std::endl;
128} 178}
129 179
130void ReadTransaction::read(const std::string &sKey, const std::function<void(void *data, int size)> &resultHandler) 180void Database::read(const std::string &sKey, const std::function<void(void *ptr, int size)> &resultHandler)
131{ 181{
182 if (!d->env) {
183 return;
184 }
185
132 int rc; 186 int rc;
133 MDB_val key; 187 MDB_val key;
134 MDB_val data; 188 MDB_val data;
@@ -137,6 +191,8 @@ void ReadTransaction::read(const std::string &sKey, const std::function<void(voi
137 key.mv_size = sKey.size(); 191 key.mv_size = sKey.size();
138 key.mv_data = (void*)sKey.data(); 192 key.mv_data = (void*)sKey.data();
139 193
194 /*
195TODO: do we need a write transaction before a read transaction? only relevant for implicitTransactions in any case
140 { 196 {
141 //A write transaction is at least required the first time 197 //A write transaction is at least required the first time
142 rc = mdb_txn_begin(env, nullptr, 0, &txn); 198 rc = mdb_txn_begin(env, nullptr, 0, &txn);
@@ -145,18 +201,27 @@ void ReadTransaction::read(const std::string &sKey, const std::function<void(voi
145 rc = mdb_dbi_open(txn, nullptr, 0, &dbi); 201 rc = mdb_dbi_open(txn, nullptr, 0, &dbi);
146 mdb_txn_abort(txn); 202 mdb_txn_abort(txn);
147 } 203 }
204 */
205 const bool implicitTransaction = !d->transaction;
206 if (implicitTransaction) {
207 // TODO: if this fails, still try the write below?
208 if (!startTransaction(ReadOnly)) {
209 return;
210 }
211 }
148 212
149 rc = mdb_txn_begin(env, nullptr, MDB_RDONLY, &txn); 213 rc = mdb_cursor_open(d->transaction, d->dbi, &cursor);
150 rc = mdb_cursor_open(txn, dbi, &cursor);
151 if (rc) { 214 if (rc) {
152 std::cerr << "mdb_cursor_open: " << rc << mdb_strerror(rc) << std::endl; 215 std::cerr << "mdb_cursor_get: " << rc << mdb_strerror(rc) << std::endl;
216 return;
153 } 217 }
218
154 if (sKey.empty()) { 219 if (sKey.empty()) {
155 std::cout << "Iterating over all values of store!" << std::endl; 220 std::cout << "Iterating over all values of store!" << std::endl;
156 rc = mdb_cursor_get(cursor, &key, &data, MDB_FIRST);
157 while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 221 while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
158 resultHandler(data.mv_data, data.mv_size); 222 resultHandler(key.mv_data, data.mv_size);
159 } 223 }
224
160 //We never find the last value 225 //We never find the last value
161 if (rc == MDB_NOTFOUND) { 226 if (rc == MDB_NOTFOUND) {
162 rc = 0; 227 rc = 0;
@@ -168,10 +233,18 @@ void ReadTransaction::read(const std::string &sKey, const std::function<void(voi
168 std::cout << "couldn't find value " << sKey << std::endl; 233 std::cout << "couldn't find value " << sKey << std::endl;
169 } 234 }
170 } 235 }
236
171 if (rc) { 237 if (rc) {
172 std::cerr << "mdb_cursor_get: " << rc << mdb_strerror(rc) << std::endl; 238 std::cerr << "mdb_cursor_get: " << rc << mdb_strerror(rc) << std::endl;
173 } 239 }
240
174 mdb_cursor_close(cursor); 241 mdb_cursor_close(cursor);
175 //We keep the transaction open since we want to keep the returned values alive 242
243 /**
244 we don't abort the transaction since we need it for reading the values
245 if (implicitTransaction) {
246 abortTransaction();
247 }
248 */
176} 249}
177 250