diff options
author | Aaron Seigo <aseigo@kde.org> | 2014-12-04 19:23:09 +0100 |
---|---|---|
committer | Aaron Seigo <aseigo@kde.org> | 2014-12-04 19:23:09 +0100 |
commit | f57dca821e2a33783ec0dd0af2f5c31daef4fa64 (patch) | |
tree | c96de4486a3278cad2e13724e0c33d04d005f00c | |
parent | 3ff320c9f11a4c51164adeb8a2b098aec22742c3 (diff) | |
download | sink-f57dca821e2a33783ec0dd0af2f5c31daef4fa64.tar.gz sink-f57dca821e2a33783ec0dd0af2f5c31daef4fa64.zip |
kyoto!
-rw-r--r-- | store/database.cpp | 220 | ||||
-rw-r--r-- | store/database.h | 5 | ||||
-rw-r--r-- | store/test/CMakeLists.txt | 2 | ||||
-rw-r--r-- | store/test/storagebenchmark.cpp | 21 |
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 | ||
14 | class Database::Private | 15 | class Database::Private |
15 | { | 16 | { |
16 | public: | 17 | public: |
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 | ||
26 | Database::Private::Private(const QString &path) | 26 | Database::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 | ||
50 | Database::Private::~Private() | 39 | Database::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 | ||
61 | Database::Database(const QString &path) | 46 | Database::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 | ||
71 | bool Database::isInTransaction() const | 56 | bool Database::isInTransaction() const |
72 | { | 57 | { |
73 | return d->transaction; | 58 | return d->inTransaction; |
74 | } | 59 | } |
75 | 60 | ||
76 | bool Database::startTransaction(TransactionType type) | 61 | bool 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 | ||
102 | bool Database::commitTransaction() | 76 | bool 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 | ||
123 | void Database::abortTransaction() | 91 | void 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 | ||
133 | bool Database::write(const std::string &sKey, const std::string &sValue) | 101 | bool 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) { | 111 | bool 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 | ||
170 | void Database::read(const std::string &sKey, const std::function<void(const std::string &value)> &resultHandler) | 121 | void 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 | |||
180 | void 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 | /* | ||
195 | TODO: 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); | 133 | void 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) { | 145 | qint64 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 | /** | 155 | void 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 { | |||
7 | public: | 7 | public: |
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; | ||
21 | private: | 24 | private: |
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) |
17 | endmacro(auto_tests) | 17 | endmacro(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 | |||
46 | private: | 46 | private: |
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 | ||
53 | private Q_SLOTS: | 53 | private 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 | }; |