summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2015-11-25 00:37:42 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2015-11-25 00:37:42 +0100
commite4a4d72fd206fc2d5c1095b39b2839e53cd114bb (patch)
treebf1edb0a483c12cda89973b3f073cd44b00286b4
parent9ad96df6cd1526de32bff2b4f98491dd8318f760 (diff)
downloadsink-e4a4d72fd206fc2d5c1095b39b2839e53cd114bb.tar.gz
sink-e4a4d72fd206fc2d5c1095b39b2839e53cd114bb.zip
Optimize findLast
This just gave a 700% boost to query performance from ~2k to 14k reads per second...
-rw-r--r--common/storage_lmdb.cpp71
-rw-r--r--tests/storagetest.cpp30
2 files changed, 89 insertions, 12 deletions
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index 1516e69..a247e38 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -253,22 +253,69 @@ int Storage::NamedDatabase::scan(const QByteArray &k,
253 253
254 return numberOfRetrievedValues; 254 return numberOfRetrievedValues;
255} 255}
256void Storage::NamedDatabase::findLatest(const QByteArray &uid, 256
257void Storage::NamedDatabase::findLatest(const QByteArray &k,
257 const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, 258 const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler,
258 const std::function<void(const Storage::Error &error)> &errorHandler) const 259 const std::function<void(const Storage::Error &error)> &errorHandler) const
259{ 260{
260 QByteArray latestKey; 261 if (!d || !d->transaction) {
261 scan(uid, [&](const QByteArray &key, const QByteArray &value) -> bool { 262 //Not an error. We rely on this to read nothing from non-existing databases.
262 latestKey = key; 263 return;
263 return true; 264 }
264 },
265 errorHandler, true);
266 265
267 scan(latestKey, [=](const QByteArray &key, const QByteArray &value) -> bool { 266 int rc;
268 resultHandler(key, value); 267 MDB_val key;
269 return false; 268 MDB_val data;
270 }, 269 MDB_cursor *cursor;
271 errorHandler); 270
271 key.mv_data = (void*)k.constData();
272 key.mv_size = k.size();
273
274 rc = mdb_cursor_open(d->transaction, d->dbi, &cursor);
275 if (rc) {
276 Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Error during mdb_cursor open: ") + QByteArray(mdb_strerror(rc)));
277 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
278 return;
279 }
280
281 MDB_cursor_op op = MDB_SET_RANGE;
282 if ((rc = mdb_cursor_get(cursor, &key, &data, op)) == 0) {
283 //The first lookup will find a key that is equal or greather than our key
284 if (QByteArray::fromRawData((char*)key.mv_data, key.mv_size).startsWith(k)) {
285 bool advanced = false;
286 while (QByteArray::fromRawData((char*)key.mv_data, key.mv_size).startsWith(k)) {
287 advanced = true;
288 MDB_cursor_op nextOp = MDB_NEXT;
289 rc = mdb_cursor_get(cursor, &key, &data, nextOp);
290 if (rc) {
291 break;
292 }
293 }
294 if (advanced) {
295 MDB_cursor_op prefOp = MDB_PREV;
296 //We read past the end above, just take the last value
297 if (rc == MDB_NOTFOUND) {
298 prefOp = MDB_LAST;
299 }
300 rc = mdb_cursor_get(cursor, &key, &data, prefOp);
301 resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size));
302 }
303 }
304 }
305
306 //We never find the last value
307 if (rc == MDB_NOTFOUND) {
308 rc = 0;
309 }
310
311 mdb_cursor_close(cursor);
312
313 if (rc) {
314 Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Key: ") + k + " : " + QByteArray(mdb_strerror(rc)));
315 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
316 }
317
318 return;
272} 319}
273 320
274 321
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp
index bef8755..d950961 100644
--- a/tests/storagetest.cpp
+++ b/tests/storagetest.cpp
@@ -376,6 +376,7 @@ private Q_SLOTS:
376 db.write("sub1","value1"); 376 db.write("sub1","value1");
377 db.write("sub2","value2"); 377 db.write("sub2","value2");
378 db.write("wub3","value3"); 378 db.write("wub3","value3");
379 db.write("wub4","value4");
379 QByteArray result; 380 QByteArray result;
380 db.findLatest("sub", [&](const QByteArray &key, const QByteArray &value) { 381 db.findLatest("sub", [&](const QByteArray &key, const QByteArray &value) {
381 result = value; 382 result = value;
@@ -384,6 +385,35 @@ private Q_SLOTS:
384 QCOMPARE(result, QByteArray("value2")); 385 QCOMPARE(result, QByteArray("value2"));
385 } 386 }
386 387
388 void testFindLatestInSingle()
389 {
390 Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite);
391 auto transaction = store.createTransaction(Akonadi2::Storage::ReadWrite);
392 auto db = transaction.openDatabase("test", nullptr, false);
393 db.write("sub2","value2");
394 QByteArray result;
395 db.findLatest("sub", [&](const QByteArray &key, const QByteArray &value) {
396 result = value;
397 });
398
399 QCOMPARE(result, QByteArray("value2"));
400 }
401
402 void testFindLast()
403 {
404 Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite);
405 auto transaction = store.createTransaction(Akonadi2::Storage::ReadWrite);
406 auto db = transaction.openDatabase("test", nullptr, false);
407 db.write("sub2","value2");
408 db.write("wub3","value3");
409 QByteArray result;
410 db.findLatest("wub", [&](const QByteArray &key, const QByteArray &value) {
411 result = value;
412 });
413
414 QCOMPARE(result, QByteArray("value3"));
415 }
416
387 void testRecordRevision() 417 void testRecordRevision()
388 { 418 {
389 Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite); 419 Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite);