summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/storage_lmdb.cpp54
-rw-r--r--tests/storagetest.cpp54
2 files changed, 63 insertions, 45 deletions
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index 13a6853..f0a5bc0 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -52,9 +52,11 @@ public:
52 bool readTransaction; 52 bool readTransaction;
53 bool firstOpen; 53 bool firstOpen;
54 static QMutex sMutex; 54 static QMutex sMutex;
55 static QHash<QString, MDB_env*> sEnvironments;
55}; 56};
56 57
57QMutex Storage::Private::sMutex; 58QMutex Storage::Private::sMutex;
59QHash<QString, MDB_env*> Storage::Private::sEnvironments;
58 60
59Storage::Private::Private(const QString &s, const QString &n, AccessMode m) 61Storage::Private::Private(const QString &s, const QString &n, AccessMode m)
60 : storageRoot(s), 62 : storageRoot(s),
@@ -70,23 +72,30 @@ Storage::Private::Private(const QString &s, const QString &n, AccessMode m)
70 dir.mkpath(storageRoot); 72 dir.mkpath(storageRoot);
71 dir.mkdir(fullPath); 73 dir.mkdir(fullPath);
72 74
73 //This seems to resolve threading related issues, not sure why though 75 //Ensure the environment is only created once
74 QMutexLocker locker(&sMutex); 76 QMutexLocker locker(&sMutex);
75 77
76 //create file 78 int rc = 0;
77 if (mdb_env_create(&env)) { 79 /*
78 // TODO: handle error 80 * It seems we can only ever have one environment open in the process.
79 } else { 81 * Otherwise multi-threading breaks.
80 int rc = mdb_env_open(env, fullPath.toStdString().data(), 0, 0664); 82 */
81 83 env = sEnvironments.value(fullPath);
82 if (rc) { 84 if (!env) {
83 std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl; 85 if ((rc = mdb_env_create(&env))) {
84 mdb_env_close(env); 86 // TODO: handle error
85 env = 0; 87 std::cerr << "mdb_env_create: " << rc << " " << mdb_strerror(rc) << std::endl;
86 } else { 88 } else {
87 //FIXME: dynamic resize 89 if ((rc = mdb_env_open(env, fullPath.toStdString().data(), 0, 0664))) {
88 const size_t dbSize = (size_t)10485760 * (size_t)100 * (size_t)80; //10MB * 800 90 std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl;
89 mdb_env_set_mapsize(env, dbSize); 91 mdb_env_close(env);
92 env = 0;
93 } else {
94 //FIXME: dynamic resize
95 const size_t dbSize = (size_t)10485760 * (size_t)100 * (size_t)80; //10MB * 800
96 mdb_env_set_mapsize(env, dbSize);
97 sEnvironments.insert(fullPath, env);
98 }
90 } 99 }
91 } 100 }
92} 101}
@@ -97,11 +106,12 @@ Storage::Private::~Private()
97 mdb_txn_abort(transaction); 106 mdb_txn_abort(transaction);
98 } 107 }
99 108
100 // it is still there and still unused, so we can shut it down 109 //Since we can have only one environment open per process, we currently leak the environments.
101 if (env) { 110 // if (env) {
102 mdb_dbi_close(env, dbi); 111 // //mdb_dbi_close should not be necessary and is potentially dangerous (see docs)
103 mdb_env_close(env); 112 // mdb_dbi_close(env, dbi);
104 } 113 // mdb_env_close(env);
114 // }
105} 115}
106 116
107Storage::Storage(const QString &storageRoot, const QString &name, AccessMode mode) 117Storage::Storage(const QString &storageRoot, const QString &name, AccessMode mode)
@@ -292,7 +302,7 @@ void Storage::scan(const char *keyData, uint keySize,
292 302
293 rc = mdb_cursor_open(d->transaction, d->dbi, &cursor); 303 rc = mdb_cursor_open(d->transaction, d->dbi, &cursor);
294 if (rc) { 304 if (rc) {
295 Error error(d->name.toStdString(), rc, mdb_strerror(rc)); 305 Error error(d->name.toStdString(), rc, std::string("Error during mdb_cursor open: ") + mdb_strerror(rc));
296 errorHandler(error); 306 errorHandler(error);
297 return; 307 return;
298 } 308 }
@@ -314,15 +324,13 @@ void Storage::scan(const char *keyData, uint keySize,
314 } else { 324 } else {
315 if ((rc = mdb_cursor_get(cursor, &key, &data, MDB_SET)) == 0) { 325 if ((rc = mdb_cursor_get(cursor, &key, &data, MDB_SET)) == 0) {
316 resultHandler(key.mv_data, key.mv_size, data.mv_data, data.mv_size); 326 resultHandler(key.mv_data, key.mv_size, data.mv_data, data.mv_size);
317 } else {
318 std::cout << "couldn't find value " << std::string(keyData, keySize) << std::endl;
319 } 327 }
320 } 328 }
321 329
322 mdb_cursor_close(cursor); 330 mdb_cursor_close(cursor);
323 331
324 if (rc) { 332 if (rc) {
325 Error error(d->name.toStdString(), rc, mdb_strerror(rc)); 333 Error error(d->name.toStdString(), rc, std::string("Key: ") + std::string(keyData, keySize) + " : " + mdb_strerror(rc));
326 errorHandler(error); 334 errorHandler(error);
327 } 335 }
328 336
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp
index a405de0..6c1d02b 100644
--- a/tests/storagetest.cpp
+++ b/tests/storagetest.cpp
@@ -46,8 +46,11 @@ private:
46 } 46 }
47 return keyMatch; 47 return keyMatch;
48 }, 48 },
49 [&success](const Akonadi2::Storage::Error &) { success = false; } 49 [&success](const Akonadi2::Storage::Error &error) {
50 ); 50 qDebug() << QString::fromStdString(error.message);
51 success = false;
52 }
53 );
51 return success && keyMatch; 54 return success && keyMatch;
52 } 55 }
53 56
@@ -138,29 +141,36 @@ private Q_SLOTS:
138 const int count = 10000; 141 const int count = 10000;
139 142
140 populate(count); 143 populate(count);
141 144 // QTest::qWait(500);
142 bool error = false; 145
143 //Try to concurrently read 146 //We repeat the test a bunch of times since failing is relatively random
144 QList<QFuture<void> > futures; 147 for (int tries = 0; tries < 10; tries++) {
145 const int concurrencyLevel = 10; 148 bool error = false;
146 for (int num = 0; num < concurrencyLevel; num++) { 149 //Try to concurrently read
147 futures << QtConcurrent::run([this, count, &error](){ 150 QList<QFuture<void> > futures;
148 Akonadi2::Storage storage(testDataPath, dbName); 151 const int concurrencyLevel = 20;
149 for (int i = 0; i < count; i++) { 152 for (int num = 0; num < concurrencyLevel; num++) {
150 if (!verify(storage, i)) { 153 futures << QtConcurrent::run([this, count, &error](){
151 error = true; 154 Akonadi2::Storage storage(testDataPath, dbName, Akonadi2::Storage::ReadOnly);
152 break; 155 Akonadi2::Storage storage2(testDataPath, dbName + "2", Akonadi2::Storage::ReadOnly);
156 for (int i = 0; i < count; i++) {
157 if (!verify(storage, i)) {
158 error = true;
159 break;
160 }
153 } 161 }
154 } 162 });
155 }); 163 }
156 } 164 for(auto future : futures) {
157 for(auto future : futures) { 165 future.waitForFinished();
158 future.waitForFinished(); 166 }
167 QVERIFY(!error);
159 } 168 }
160 QVERIFY(!error);
161 169
162 Akonadi2::Storage storage(testDataPath, dbName); 170 {
163 storage.removeFromDisk(); 171 Akonadi2::Storage storage(testDataPath, dbName);
172 storage.removeFromDisk();
173 }
164 } 174 }
165}; 175};
166 176