summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--store/database.cpp220
-rw-r--r--store/database.h5
-rw-r--r--store/test/CMakeLists.txt2
-rw-r--r--store/test/storagebenchmark.cpp21
4 files changed, 81 insertions, 167 deletions
diff --git a/store/database.cpp b/store/database.cpp
index 71ec47d..542667a 100644
--- a/store/database.cpp
+++ b/store/database.cpp
@@ -5,61 +5,46 @@
5#include <QAtomicInt> 5#include <QAtomicInt>
6#include <QDebug> 6#include <QDebug>
7#include <QDir> 7#include <QDir>
8#include <QFileInfo>
8#include <QReadWriteLock> 9#include <QReadWriteLock>
9#include <QString> 10#include <QString>
10#include <QTime> 11#include <QTime>
11 12
12#include <lmdb.h> 13#include <kchashdb.h>
13 14
14class Database::Private 15class Database::Private
15{ 16{
16public: 17public:
17 Private(const QString &path); 18 Private(const QString &storageRoot, const QString &name);
18 ~Private(); 19 ~Private();
19 20
20 MDB_dbi dbi; 21 kyotocabinet::TreeDB db;
21 MDB_env *env; 22 bool dbOpen;
22 MDB_txn *transaction; 23 bool inTransaction;
23 bool readTransaction;
24}; 24};
25 25
26Database::Private::Private(const QString &path) 26Database::Private::Private(const QString &storageRoot, const QString &name)
27 : transaction(0), 27 : inTransaction(false)
28 readTransaction(false)
29{ 28{
30 QDir dir; 29 QDir dir;
31 dir.mkdir(path); 30 dir.mkdir(storageRoot);
32 31
33 //create file 32 //create file
34 if (mdb_env_create(&env)) { 33 dbOpen = db.open((storageRoot + "/" + name + ".kch").toStdString(), kyotocabinet::BasicDB::OWRITER | kyotocabinet::BasicDB::OCREATE);
34 if (!dbOpen) {
35 // TODO: handle error 35 // TODO: handle error
36 } else {
37 int rc = mdb_env_open(env, path.toStdString().data(), 0, 0664);
38
39 if (rc) {
40 std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl;
41 mdb_env_close(env);
42 env = 0;
43 } else {
44 const size_t dbSize = 10485760 * 100; //10MB * 100
45 mdb_env_set_mapsize(env, dbSize);
46 }
47 } 36 }
48} 37}
49 38
50Database::Private::~Private() 39Database::Private::~Private()
51{ 40{
52 if (transaction) { 41 if (dbOpen && inTransaction) {
53 mdb_txn_abort(transaction); 42 db.end_transaction(false);
54 } 43 }
55
56 // it is still there and still unused, so we can shut it down
57 mdb_dbi_close(env, dbi);
58 mdb_env_close(env);
59} 44}
60 45
61Database::Database(const QString &path) 46Database::Database(const QString &storageRoot, const QString &name)
62 : d(new Private(path)) 47 : d(new Private(storageRoot, name))
63{ 48{
64} 49}
65 50
@@ -70,181 +55,110 @@ Database::~Database()
70 55
71bool Database::isInTransaction() const 56bool Database::isInTransaction() const
72{ 57{
73 return d->transaction; 58 return d->inTransaction;
74} 59}
75 60
76bool Database::startTransaction(TransactionType type) 61bool Database::startTransaction(TransactionType type)
77{ 62{
78 if (!d->env) { 63 if (!d->dbOpen) {
79 return false; 64 return false;
80 } 65 }
81 66
82 bool requestedRead = type == ReadOnly; 67 if (d->inTransaction) {
83 if (d->transaction && (!d->readTransaction || requestedRead)) {
84 return true; 68 return true;
85 } 69 }
86 70
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 71 //TODO handle errors
93 int rc; 72 d->inTransaction = d->db.begin_transaction();
94 rc = mdb_txn_begin(d->env, NULL, requestedRead ? MDB_RDONLY : 0, &d->transaction); 73 return d->inTransaction;
95 if (!rc) {
96 rc = mdb_dbi_open(d->transaction, NULL, 0, &d->dbi);
97 }
98
99 return !rc;
100} 74}
101 75
102bool Database::commitTransaction() 76bool Database::commitTransaction()
103{ 77{
104 if (!d->env) { 78 if (!d->dbOpen) {
105 return false; 79 return false;
106 } 80 }
107 81
108 if (!d->transaction) { 82 if (!d->inTransaction) {
109 return false; 83 return false;
110 } 84 }
111 85
112 int rc; 86 bool success = d->db.end_transaction(true);
113 rc = mdb_txn_commit(d->transaction); 87 d->inTransaction = false;
114 d->transaction = 0; 88 return success;
115
116 if (rc) {
117 std::cerr << "mdb_txn_commit: " << rc << " " << mdb_strerror(rc) << std::endl;
118 }
119
120 return !rc;
121} 89}
122 90
123void Database::abortTransaction() 91void Database::abortTransaction()
124{ 92{
125 if (!d->env || !d->transaction) { 93 if (!d->dbOpen || !d->inTransaction) {
126 return; 94 return;
127 } 95 }
128 96
129 mdb_txn_abort(d->transaction); 97 d->db.end_transaction(false);
130 d->transaction = 0; 98 d->inTransaction = false;
131} 99}
132 100
133bool Database::write(const std::string &sKey, const std::string &sValue) 101bool Database::write(const char *key, size_t keySize, const char *value, size_t valueSize)
134{ 102{
135 if (!d->env) { 103 if (!d->dbOpen) {
136 return false; 104 return false;
137 } 105 }
138 106
139 const bool implicitTransaction = !d->transaction || d->readTransaction; 107 bool success = d->db.set(key, keySize, value, valueSize);
140 if (implicitTransaction) { 108 return success;
141 // TODO: if this fails, still try the write below? 109}
142 if (!startTransaction()) {
143 return false;
144 }
145 }
146
147 int rc;
148 MDB_val key, data;
149 key.mv_size = sKey.size();
150 key.mv_data = (void*)sKey.data();
151 data.mv_size = sValue.size();
152 data.mv_data = (void*)sValue.data();
153 rc = mdb_put(d->transaction, d->dbi, &key, &data, 0);
154
155 if (rc) {
156 std::cerr << "mdb_put: " << rc << " " << mdb_strerror(rc) << std::endl;
157 }
158 110
159 if (implicitTransaction) { 111bool Database::write(const std::string &sKey, const std::string &sValue)
160 if (rc) { 112{
161 abortTransaction(); 113 if (!d->dbOpen) {
162 } else { 114 return false;
163 rc = commitTransaction();
164 }
165 } 115 }
166 116
167 return !rc; 117 bool success = d->db.set(sKey, sValue);
118 return success;
168} 119}
169 120
170void Database::read(const std::string &sKey, const std::function<void(const std::string &value)> &resultHandler) 121void Database::read(const std::string &sKey, const std::function<void(const std::string &value)> &resultHandler)
171{ 122{
172 read(sKey, 123 if (!d->dbOpen) {
173 [&](void *ptr, int size) {
174 const std::string resultValue(static_cast<char*>(ptr), size);
175 resultHandler(resultValue);
176 });
177// std::cout << "key: " << resultKey << " data: " << resultValue << std::endl;
178}
179
180void Database::read(const std::string &sKey, const std::function<void(void *ptr, int size)> &resultHandler)
181{
182 if (!d->env) {
183 return; 124 return;
184 } 125 }
185 126
186 int rc; 127 std::string value;
187 MDB_val key; 128 if (d->db.get(sKey, &value)) {
188 MDB_val data; 129 resultHandler(value);
189 MDB_cursor *cursor;
190
191 key.mv_size = sKey.size();
192 key.mv_data = (void*)sKey.data();
193
194 /*
195TODO: do we need a write transaction before a read transaction? only relevant for implicitTransactions in any case
196 {
197 //A write transaction is at least required the first time
198 rc = mdb_txn_begin(env, nullptr, 0, &txn);
199 //Open the database
200 //With this we could open multiple named databases if we wanted to
201 rc = mdb_dbi_open(txn, nullptr, 0, &dbi);
202 mdb_txn_abort(txn);
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 } 130 }
131}
212 132
213 rc = mdb_cursor_open(d->transaction, d->dbi, &cursor); 133void Database::read(const std::string &sKey, const std::function<void(void *ptr, int size)> &resultHandler)
214 if (rc) { 134{
215 std::cerr << "mdb_cursor_get: " << rc << " " << mdb_strerror(rc) << std::endl; 135 if (!d->dbOpen) {
216 return; 136 return;
217 } 137 }
218 138
219 if (sKey.empty()) { 139 size_t valueSize;
220 std::cout << "Iterating over all values of store!" << std::endl; 140 char *valueBuffer = d->db.get(sKey.data(), sKey.size(), &valueSize);
221 while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 141 resultHandler(valueBuffer, valueSize);
222 resultHandler(key.mv_data, data.mv_size); 142 delete[] valueBuffer;
223 } 143}
224
225 //We never find the last value
226 if (rc == MDB_NOTFOUND) {
227 rc = 0;
228 }
229 } else {
230 if ((rc = mdb_cursor_get(cursor, &key, &data, MDB_SET)) == 0) {
231 resultHandler(data.mv_data, data.mv_size);
232 } else {
233 std::cout << "couldn't find value " << sKey << " " << std::endl;
234 }
235 }
236 144
237 if (rc) { 145qint64 Database::diskUsage() const
238 std::cerr << "mdb_cursor_get: " << rc << " " << mdb_strerror(rc) << std::endl; 146{
147 if (!d->dbOpen) {
148 return 0;
239 } 149 }
240 150
241 mdb_cursor_close(cursor); 151 QFileInfo info(QString::fromStdString(d->db.path()));
152 return info.size();
153}
242 154
243 /** 155void Database::removeFromDisk() const
244 we don't abort the transaction since we need it for reading the values 156{
245 if (implicitTransaction) { 157 if (!d->dbOpen) {
246 abortTransaction(); 158 return;
247 } 159 }
248 */
249}
250 160
161 QFileInfo info(QString::fromStdString(d->db.path()));
162 QDir dir = info.dir();
163 dir.remove(info.fileName());
164}
diff --git a/store/database.h b/store/database.h
index 2bf0556..e752ff5 100644
--- a/store/database.h
+++ b/store/database.h
@@ -7,17 +7,20 @@ class Database {
7public: 7public:
8 enum TransactionType { ReadOnly, ReadWrite }; 8 enum TransactionType { ReadOnly, ReadWrite };
9 9
10 Database(const QString &path); 10 Database(const QString &storageRoot, const QString &name);
11 ~Database(); 11 ~Database();
12 bool isInTransaction() const; 12 bool isInTransaction() const;
13 bool startTransaction(TransactionType type = ReadWrite); 13 bool startTransaction(TransactionType type = ReadWrite);
14 bool commitTransaction(); 14 bool commitTransaction();
15 void abortTransaction(); 15 void abortTransaction();
16 bool write(const char *key, size_t keySize, const char *value, size_t valueSize);
16 bool write(const std::string &sKey, const std::string &sValue); 17 bool write(const std::string &sKey, const std::string &sValue);
17 //Perhaps prefer iterators (assuming we need to be able to match multiple values 18 //Perhaps prefer iterators (assuming we need to be able to match multiple values
18 void read(const std::string &sKey, const std::function<void(const std::string &value)> &); 19 void read(const std::string &sKey, const std::function<void(const std::string &value)> &);
19 void read(const std::string &sKey, const std::function<void(void *ptr, int size)> &); 20 void read(const std::string &sKey, const std::function<void(void *ptr, int size)> &);
20 21
22 qint64 diskUsage() const;
23 void removeFromDisk() const;
21private: 24private:
22 class Private; 25 class Private;
23 Private * const d; 26 Private * const d;
diff --git a/store/test/CMakeLists.txt b/store/test/CMakeLists.txt
index 4743cfb..1b9dc9e 100644
--- a/store/test/CMakeLists.txt
+++ b/store/test/CMakeLists.txt
@@ -12,7 +12,7 @@ macro(manual_tests)
12 foreach(_testname ${ARGN}) 12 foreach(_testname ${ARGN})
13 add_executable(${_testname} ${_testname}.cpp ${store_SRCS}) 13 add_executable(${_testname} ${_testname}.cpp ${store_SRCS})
14 qt5_use_modules(${_testname} Core Test) 14 qt5_use_modules(${_testname} Core Test)
15 target_link_libraries(${_testname} lmdb) 15 target_link_libraries(${_testname} kyotocabinet)
16 endforeach(_testname) 16 endforeach(_testname)
17endmacro(auto_tests) 17endmacro(auto_tests)
18 18
diff --git a/store/test/storagebenchmark.cpp b/store/test/storagebenchmark.cpp
index 3be90e9..d42dea8 100644
--- a/store/test/storagebenchmark.cpp
+++ b/store/test/storagebenchmark.cpp
@@ -46,21 +46,16 @@ class StorageBenchmark : public QObject
46private: 46private:
47 //This should point to a directory on disk and not a ramdisk (since we're measuring performance) 47 //This should point to a directory on disk and not a ramdisk (since we're measuring performance)
48 QString testDataPath; 48 QString testDataPath;
49 QString dbPath; 49 QString dbName;
50 QString filePath; 50 QString filePath;
51 const int count = 50000; 51 const int count = 50000;
52 52
53private Q_SLOTS: 53private Q_SLOTS:
54 void initTestCase() 54 void initTestCase()
55 { 55 {
56 testDataPath = "./"; 56 testDataPath = "./testdb";
57 dbPath = testDataPath + "testdb"; 57 dbName = "test";
58 filePath = testDataPath + "buffer.fb"; 58 filePath = testDataPath + "buffer.fb";
59
60 QDir dir(testDataPath);
61 dir.remove("testdb/data.mdb");
62 dir.remove("testdb/lock.mdb");
63 dir.remove(filePath);
64 } 59 }
65 60
66 void testWriteRead_data() 61 void testWriteRead_data()
@@ -79,7 +74,7 @@ private Q_SLOTS:
79 74
80 Database *db = 0; 75 Database *db = 0;
81 if (useDb) { 76 if (useDb) {
82 db = new Database(dbPath); 77 db = new Database(testDataPath, dbName);
83 } 78 }
84 79
85 std::ofstream myfile; 80 std::ofstream myfile;
@@ -118,7 +113,7 @@ private Q_SLOTS:
118 { 113 {
119 for (int i = 0; i < count; i++) { 114 for (int i = 0; i < count; i++) {
120 if (db) { 115 if (db) {
121 db->read(keyPrefix + std::to_string(i), [](void *ptr, int size){}); 116 db->read(keyPrefix + std::to_string(i), [](std::string value){});
122 } 117 }
123 } 118 }
124 } 119 }
@@ -149,9 +144,11 @@ private Q_SLOTS:
149 144
150 void testSizes() 145 void testSizes()
151 { 146 {
152 QFileInfo dbInfo(dbPath, "data.mdb"); 147 Database db(testDataPath, dbName);
148 qDebug() << "Database size [kb]: " << db.diskUsage()/1024;
149 db.removeFromDisk();
150
153 QFileInfo fileInfo(filePath); 151 QFileInfo fileInfo(filePath);
154 qDebug() << "Database size [kb]: " << dbInfo.size()/1024;
155 qDebug() << "File size [kb]: " << fileInfo.size()/1024; 152 qDebug() << "File size [kb]: " << fileInfo.size()/1024;
156 } 153 }
157}; 154};