summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2018-05-21 19:48:09 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2018-05-23 13:55:15 +0200
commit4cb0d1561cf41551d4ddc418f8666388b90318b9 (patch)
tree2efb1dbc7a8627893b3b0ddd9f95d08dda6b7e8f /tests
parentfa9e0e2cbbcb0733e86a47f489296f58fbcf34af (diff)
downloadsink-4cb0d1561cf41551d4ddc418f8666388b90318b9.tar.gz
sink-4cb0d1561cf41551d4ddc418f8666388b90318b9.zip
Fixed use of mdb_dbi_open
There can only ever be one transaction using mdb_dbi_open running, and that transaction must commit or abort before any other transaction attempts to use mdb_dbi_open. Use delayed dbi merging with write transactions and a temporary transaction for read transactions. We now protect dbi initialization with a mutex and immediately update the sDbis hash. This assumes that the created dbis are indeed We can still violate the only one transaction may use mdb_dbi_open rule if we start a read-only transaction after the write transaction, before the write transaction commits. It does not seem to be something we actually do though. Opening dbis on environment init is further separated out, so we don't end up in the regular openDatabase codepath at all.
Diffstat (limited to 'tests')
-rw-r--r--tests/dbwriter.cpp6
-rw-r--r--tests/storagetest.cpp129
2 files changed, 110 insertions, 25 deletions
diff --git a/tests/dbwriter.cpp b/tests/dbwriter.cpp
index 902a607..3045eac 100644
--- a/tests/dbwriter.cpp
+++ b/tests/dbwriter.cpp
@@ -18,7 +18,11 @@ int main(int argc, char *argv[])
18 } 18 }
19 19
20 qWarning() << "Creating db: " << testDataPath << dbName << count; 20 qWarning() << "Creating db: " << testDataPath << dbName << count;
21 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 21 QMap<QByteArray, int> dbs = {{"a", 0}, {"b", 0}, {"c", 0}, {"p", 0}, {"q", 0}, {"db", 0}};
22 for (int d = 0; d < 40; d++) {
23 dbs.insert("db" + QByteArray::number(d), 0);
24 }
25 Sink::Storage::DataStore store(testDataPath, {dbName, dbs}, Sink::Storage::DataStore::ReadWrite);
22 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 26 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
23 for (int i = 0; i < count; i++) { 27 for (int i = 0; i < count; i++) {
24 if (!transaction) { 28 if (!transaction) {
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp
index 802947f..618f9d0 100644
--- a/tests/storagetest.cpp
+++ b/tests/storagetest.cpp
@@ -16,12 +16,12 @@ class StorageTest : public QObject
16 Q_OBJECT 16 Q_OBJECT
17private: 17private:
18 QString testDataPath; 18 QString testDataPath;
19 QString dbName; 19 QByteArray dbName;
20 const char *keyPrefix = "key"; 20 const char *keyPrefix = "key";
21 21
22 void populate(int count) 22 void populate(int count)
23 { 23 {
24 Sink::Storage::DataStore storage(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 24 Sink::Storage::DataStore storage(testDataPath, {dbName, {{"default", 0}}}, Sink::Storage::DataStore::ReadWrite);
25 auto transaction = storage.createTransaction(Sink::Storage::DataStore::ReadWrite); 25 auto transaction = storage.createTransaction(Sink::Storage::DataStore::ReadWrite);
26 for (int i = 0; i < count; i++) { 26 for (int i = 0; i < count; i++) {
27 // This should perhaps become an implementation detail of the db? 27 // This should perhaps become an implementation detail of the db?
@@ -63,20 +63,20 @@ private slots:
63 { 63 {
64 testDataPath = "./testdb"; 64 testDataPath = "./testdb";
65 dbName = "test"; 65 dbName = "test";
66 Sink::Storage::DataStore storage(testDataPath, dbName); 66 Sink::Storage::DataStore storage(testDataPath, {dbName, {{"default", 0}}});
67 storage.removeFromDisk(); 67 storage.removeFromDisk();
68 } 68 }
69 69
70 void cleanup() 70 void cleanup()
71 { 71 {
72 Sink::Storage::DataStore storage(testDataPath, dbName); 72 Sink::Storage::DataStore storage(testDataPath, {dbName, {{"default", 0}}});
73 storage.removeFromDisk(); 73 storage.removeFromDisk();
74 } 74 }
75 75
76 void testCleanup() 76 void testCleanup()
77 { 77 {
78 populate(1); 78 populate(1);
79 Sink::Storage::DataStore storage(testDataPath, dbName); 79 Sink::Storage::DataStore storage(testDataPath, {dbName, {{"default", 0}}});
80 storage.removeFromDisk(); 80 storage.removeFromDisk();
81 QFileInfo info(testDataPath + "/" + dbName); 81 QFileInfo info(testDataPath + "/" + dbName);
82 QVERIFY(!info.exists()); 82 QVERIFY(!info.exists());
@@ -163,7 +163,7 @@ private slots:
163 { 163 {
164 bool gotResult = false; 164 bool gotResult = false;
165 bool gotError = false; 165 bool gotError = false;
166 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 166 Sink::Storage::DataStore store(testDataPath, {dbName, {{"default", 0}}}, Sink::Storage::DataStore::ReadWrite);
167 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); 167 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly);
168 auto db = transaction.openDatabase("default", [&](const Sink::Storage::DataStore::Error &error) { 168 auto db = transaction.openDatabase("default", [&](const Sink::Storage::DataStore::Error &error) {
169 qDebug() << error.message; 169 qDebug() << error.message;
@@ -227,7 +227,7 @@ private slots:
227 { 227 {
228 bool gotResult = false; 228 bool gotResult = false;
229 bool gotError = false; 229 bool gotError = false;
230 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 230 Sink::Storage::DataStore store(testDataPath, {dbName, {{"default", 0}}}, Sink::Storage::DataStore::ReadWrite);
231 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 231 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
232 auto db = transaction.openDatabase("default", nullptr, false); 232 auto db = transaction.openDatabase("default", nullptr, false);
233 db.write("key", "value"); 233 db.write("key", "value");
@@ -252,7 +252,7 @@ private slots:
252 { 252 {
253 bool gotResult = false; 253 bool gotResult = false;
254 bool gotError = false; 254 bool gotError = false;
255 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 255 Sink::Storage::DataStore store(testDataPath, {dbName, {{"default", 0x04}}}, Sink::Storage::DataStore::ReadWrite);
256 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 256 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
257 auto db = transaction.openDatabase("default", nullptr, true); 257 auto db = transaction.openDatabase("default", nullptr, true);
258 db.write("key", "value1"); 258 db.write("key", "value1");
@@ -295,7 +295,7 @@ private slots:
295 void testWriteToNamedDb() 295 void testWriteToNamedDb()
296 { 296 {
297 bool gotError = false; 297 bool gotError = false;
298 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 298 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
299 store.createTransaction(Sink::Storage::DataStore::ReadWrite) 299 store.createTransaction(Sink::Storage::DataStore::ReadWrite)
300 .openDatabase("test") 300 .openDatabase("test")
301 .write("key1", "value1", [&](const Sink::Storage::DataStore::Error &error) { 301 .write("key1", "value1", [&](const Sink::Storage::DataStore::Error &error) {
@@ -308,7 +308,8 @@ private slots:
308 void testWriteDuplicatesToNamedDb() 308 void testWriteDuplicatesToNamedDb()
309 { 309 {
310 bool gotError = false; 310 bool gotError = false;
311 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 311
312 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
312 store.createTransaction(Sink::Storage::DataStore::ReadWrite) 313 store.createTransaction(Sink::Storage::DataStore::ReadWrite)
313 .openDatabase("test", nullptr, true) 314 .openDatabase("test", nullptr, true)
314 .write("key1", "value1", [&](const Sink::Storage::DataStore::Error &error) { 315 .write("key1", "value1", [&](const Sink::Storage::DataStore::Error &error) {
@@ -321,7 +322,7 @@ private slots:
321 // By default we want only exact matches 322 // By default we want only exact matches
322 void testSubstringKeys() 323 void testSubstringKeys()
323 { 324 {
324 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 325 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0x04}}}, Sink::Storage::DataStore::ReadWrite);
325 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 326 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
326 auto db = transaction.openDatabase("test", nullptr, true); 327 auto db = transaction.openDatabase("test", nullptr, true);
327 db.write("sub", "value1"); 328 db.write("sub", "value1");
@@ -333,7 +334,7 @@ private slots:
333 334
334 void testFindSubstringKeys() 335 void testFindSubstringKeys()
335 { 336 {
336 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 337 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
337 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 338 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
338 auto db = transaction.openDatabase("test", nullptr, false); 339 auto db = transaction.openDatabase("test", nullptr, false);
339 db.write("sub", "value1"); 340 db.write("sub", "value1");
@@ -346,7 +347,7 @@ private slots:
346 347
347 void testFindSubstringKeysWithDuplicatesEnabled() 348 void testFindSubstringKeysWithDuplicatesEnabled()
348 { 349 {
349 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 350 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
350 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 351 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
351 auto db = transaction.openDatabase("test", nullptr, true); 352 auto db = transaction.openDatabase("test", nullptr, true);
352 db.write("sub", "value1"); 353 db.write("sub", "value1");
@@ -359,7 +360,7 @@ private slots:
359 360
360 void testKeySorting() 361 void testKeySorting()
361 { 362 {
362 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 363 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
363 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 364 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
364 auto db = transaction.openDatabase("test", nullptr, false); 365 auto db = transaction.openDatabase("test", nullptr, false);
365 db.write("sub_2", "value2"); 366 db.write("sub_2", "value2");
@@ -380,7 +381,7 @@ private slots:
380 // Ensure we don't retrieve a key that is greater than the current key. We only want equal keys. 381 // Ensure we don't retrieve a key that is greater than the current key. We only want equal keys.
381 void testKeyRange() 382 void testKeyRange()
382 { 383 {
383 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 384 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
384 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 385 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
385 auto db = transaction.openDatabase("test", nullptr, true); 386 auto db = transaction.openDatabase("test", nullptr, true);
386 db.write("sub1", "value1"); 387 db.write("sub1", "value1");
@@ -391,7 +392,7 @@ private slots:
391 392
392 void testFindLatest() 393 void testFindLatest()
393 { 394 {
394 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 395 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
395 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 396 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
396 auto db = transaction.openDatabase("test", nullptr, false); 397 auto db = transaction.openDatabase("test", nullptr, false);
397 db.write("sub1", "value1"); 398 db.write("sub1", "value1");
@@ -406,7 +407,7 @@ private slots:
406 407
407 void testFindLatestInSingle() 408 void testFindLatestInSingle()
408 { 409 {
409 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 410 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
410 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 411 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
411 auto db = transaction.openDatabase("test", nullptr, false); 412 auto db = transaction.openDatabase("test", nullptr, false);
412 db.write("sub2", "value2"); 413 db.write("sub2", "value2");
@@ -418,7 +419,7 @@ private slots:
418 419
419 void testFindLast() 420 void testFindLast()
420 { 421 {
421 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 422 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
422 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 423 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
423 auto db = transaction.openDatabase("test", nullptr, false); 424 auto db = transaction.openDatabase("test", nullptr, false);
424 db.write("sub2", "value2"); 425 db.write("sub2", "value2");
@@ -429,9 +430,18 @@ private slots:
429 QCOMPARE(result, QByteArray("value3")); 430 QCOMPARE(result, QByteArray("value3"));
430 } 431 }
431 432
433 static QMap<QByteArray, int> baseDbs()
434 {
435 return {{"revisionType", 0},
436 {"revisions", 0},
437 {"uids", 0},
438 {"default", 0},
439 {"__flagtable", 0}};
440 }
441
432 void testRecordRevision() 442 void testRecordRevision()
433 { 443 {
434 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 444 Sink::Storage::DataStore store(testDataPath, {dbName, baseDbs()}, Sink::Storage::DataStore::ReadWrite);
435 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 445 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
436 Sink::Storage::DataStore::recordRevision(transaction, 1, "uid", "type"); 446 Sink::Storage::DataStore::recordRevision(transaction, 1, "uid", "type");
437 QCOMPARE(Sink::Storage::DataStore::getTypeFromRevision(transaction, 1), QByteArray("type")); 447 QCOMPARE(Sink::Storage::DataStore::getTypeFromRevision(transaction, 1), QByteArray("type"));
@@ -440,7 +450,7 @@ private slots:
440 450
441 void testRecordRevisionSorting() 451 void testRecordRevisionSorting()
442 { 452 {
443 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 453 Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite);
444 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 454 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
445 QByteArray result; 455 QByteArray result;
446 auto db = transaction.openDatabase("test", nullptr, false); 456 auto db = transaction.openDatabase("test", nullptr, false);
@@ -463,7 +473,7 @@ private slots:
463 return result; 473 return result;
464 }; 474 };
465 { 475 {
466 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 476 Sink::Storage::DataStore store(testDataPath, {dbName, {{"testTransactionVisibility", 0}}}, Sink::Storage::DataStore::ReadWrite);
467 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 477 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
468 478
469 auto db = transaction.openDatabase("testTransactionVisibility", nullptr, false); 479 auto db = transaction.openDatabase("testTransactionVisibility", nullptr, false);
@@ -489,7 +499,7 @@ private slots:
489 499
490 void testCopyTransaction() 500 void testCopyTransaction()
491 { 501 {
492 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 502 Sink::Storage::DataStore store(testDataPath, {dbName, {{"a", 0}, {"b", 0}, {"c", 0}}}, Sink::Storage::DataStore::ReadWrite);
493 { 503 {
494 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 504 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
495 transaction.openDatabase("a", nullptr, false); 505 transaction.openDatabase("a", nullptr, false);
@@ -516,7 +526,6 @@ private slots:
516 */ 526 */
517 void testReadDuringExternalProcessWrite() 527 void testReadDuringExternalProcessWrite()
518 { 528 {
519 QSKIP("Not running multiprocess test");
520 529
521 QList<QFuture<void>> futures; 530 QList<QFuture<void>> futures;
522 for (int i = 0; i < 5; i++) { 531 for (int i = 0; i < 5; i++) {
@@ -545,7 +554,17 @@ private slots:
545 554
546 void testRecordUid() 555 void testRecordUid()
547 { 556 {
548 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); 557
558 QMap<QByteArray, int> dbs = {{"revisionType", 0},
559 {"revisions", 0},
560 {"uids", 0},
561 {"default", 0},
562 {"__flagtable", 0},
563 {"typeuids", 0},
564 {"type2uids", 0}
565 };
566
567 Sink::Storage::DataStore store(testDataPath, {dbName, dbs}, Sink::Storage::DataStore::ReadWrite);
549 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); 568 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
550 Sink::Storage::DataStore::recordUid(transaction, "uid1", "type"); 569 Sink::Storage::DataStore::recordUid(transaction, "uid1", "type");
551 Sink::Storage::DataStore::recordUid(transaction, "uid2", "type"); 570 Sink::Storage::DataStore::recordUid(transaction, "uid2", "type");
@@ -571,6 +590,68 @@ private slots:
571 QCOMPARE(uids, expected); 590 QCOMPARE(uids, expected);
572 } 591 }
573 } 592 }
593
594 void testDbiVisibility()
595 {
596 auto readValue = [](const Sink::Storage::DataStore::NamedDatabase &db, const QByteArray) {
597 QByteArray result;
598 db.scan("key1", [&](const QByteArray &, const QByteArray &value) {
599 result = value;
600 return true;
601 });
602 return result;
603 };
604 {
605 Sink::Storage::DataStore store(testDataPath, {dbName, {{"testTransactionVisibility", 0}}}, Sink::Storage::DataStore::ReadWrite);
606 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
607
608 auto db = transaction.openDatabase("testTransactionVisibility", nullptr, false);
609 db.write("key1", "foo");
610 QCOMPARE(readValue(db, "key1"), QByteArray("foo"));
611 transaction.commit();
612 }
613 Sink::Storage::DataStore::clearEnv();
614
615 //Try to read-only dynamic opening of the db.
616 //This is the case if we don't have all databases available upon initializatoin and we don't (e.g. because the db hasn't been created yet)
617 {
618 // Trick the db into not loading all dbs by passing in a bogus layout.
619 Sink::Storage::DataStore store(testDataPath, {dbName, {{"bogus", 0}}}, Sink::Storage::DataStore::ReadOnly);
620
621 //This transaction should open the dbi
622 auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadOnly);
623 auto db2 = transaction2.openDatabase("testTransactionVisibility", nullptr, false);
624 QCOMPARE(readValue(db2, "key1"), QByteArray("foo"));
625
626 //This transaction should have the dbi available
627 auto transaction3 = store.createTransaction(Sink::Storage::DataStore::ReadOnly);
628 auto db3 = transaction3.openDatabase("testTransactionVisibility", nullptr, false);
629 QCOMPARE(readValue(db3, "key1"), QByteArray("foo"));
630 }
631
632 Sink::Storage::DataStore::clearEnv();
633 //Try to read-write dynamic opening of the db.
634 //This is the case if we don't have all databases available upon initializatoin and we don't (e.g. because the db hasn't been created yet)
635 {
636 // Trick the db into not loading all dbs by passing in a bogus layout.
637 Sink::Storage::DataStore store(testDataPath, {dbName, {{"bogus", 0}}}, Sink::Storage::DataStore::ReadWrite);
638
639 //This transaction should open the dbi
640 auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
641 auto db2 = transaction2.openDatabase("testTransactionVisibility", nullptr, false);
642 QCOMPARE(readValue(db2, "key1"), QByteArray("foo"));
643
644 //This transaction should have the dbi available (creating two write transactions obviously doesn't work)
645 //NOTE: we don't support this scenario. A write transaction must commit or abort before a read transaction opens the same database.
646 // auto transaction3 = store.createTransaction(Sink::Storage::DataStore::ReadOnly);
647 // auto db3 = transaction3.openDatabase("testTransactionVisibility", nullptr, false);
648 // QCOMPARE(readValue(db3, "key1"), QByteArray("foo"));
649
650 //Ensure we can still open further dbis in the write transaction
651 auto db4 = transaction2.openDatabase("anotherDb", nullptr, false);
652 }
653
654 }
574}; 655};
575 656
576QTEST_MAIN(StorageTest) 657QTEST_MAIN(StorageTest)