summaryrefslogtreecommitdiffstats
path: root/common/storage_lmdb.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-07-03 14:02:27 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-07-03 14:02:27 +0200
commit55fe06979ceebe67553135b43aa47e70d931304b (patch)
tree16b10a744879cc1872d6c07624b59ae64469ddbf /common/storage_lmdb.cpp
parent56fae95f49a1ca8ca614bd9f89b0ea5f872765e9 (diff)
parent288946f1694c2abe1d2c5800c87339d1e8780e4b (diff)
downloadsink-55fe06979ceebe67553135b43aa47e70d931304b.tar.gz
sink-55fe06979ceebe67553135b43aa47e70d931304b.zip
Merge branch 'develop'
Diffstat (limited to 'common/storage_lmdb.cpp')
-rw-r--r--common/storage_lmdb.cpp153
1 files changed, 94 insertions, 59 deletions
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index 08eea37..f7999d1 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -35,9 +35,6 @@
35#include <lmdb.h> 35#include <lmdb.h>
36#include "log.h" 36#include "log.h"
37 37
38SINK_DEBUG_AREA("storage")
39// SINK_DEBUG_COMPONENT(d->storageRoot.toLatin1() + '/' + d->name.toLatin1())
40
41namespace Sink { 38namespace Sink {
42namespace Storage { 39namespace Storage {
43 40
@@ -169,6 +166,27 @@ public:
169 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags, &dbi)) { 166 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags, &dbi)) {
170 //Create the db if it is not existing already 167 //Create the db if it is not existing already
171 if (rc == MDB_NOTFOUND && !readOnly) { 168 if (rc == MDB_NOTFOUND && !readOnly) {
169 //Sanity check db name
170 {
171 auto parts = db.split('.');
172 for (const auto &p : parts) {
173 auto containsSpecialCharacter = [] (const QByteArray &p) {
174 for (int i = 0; i < p.size(); i++) {
175 const auto c = p.at(i);
176 //Between 0 and z in the ascii table. Essentially ensures that the name is printable and doesn't contain special chars
177 if (c < 0x30 || c > 0x7A) {
178 return true;
179 }
180 }
181 return false;
182 };
183 if (p.isEmpty() || containsSpecialCharacter(p)) {
184 SinkError() << "Tried to create a db with an invalid name. Hex:" << db.toHex() << " ASCII:" << db;
185 Q_ASSERT(false);
186 throw std::runtime_error("Fatal error while creating db.");
187 }
188 }
189 }
172 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags | MDB_CREATE, &dbi)) { 190 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags | MDB_CREATE, &dbi)) {
173 SinkWarning() << "Failed to create db " << QByteArray(mdb_strerror(rc)); 191 SinkWarning() << "Failed to create db " << QByteArray(mdb_strerror(rc));
174 Error error(name.toLatin1(), ErrorCodes::GenericError, "Error while creating database: " + QByteArray(mdb_strerror(rc))); 192 Error error(name.toLatin1(), ErrorCodes::GenericError, "Error while creating database: " + QByteArray(mdb_strerror(rc)));
@@ -542,6 +560,7 @@ DataStore::Transaction::Transaction(Transaction &&other) : d(nullptr)
542DataStore::Transaction &DataStore::Transaction::operator=(DataStore::Transaction &&other) 560DataStore::Transaction &DataStore::Transaction::operator=(DataStore::Transaction &&other)
543{ 561{
544 if (&other != this) { 562 if (&other != this) {
563 abort();
545 delete d; 564 delete d;
546 d = other.d; 565 d = other.d;
547 other.d = nullptr; 566 other.d = nullptr;
@@ -639,11 +658,6 @@ static bool ensureCorrectDb(DataStore::NamedDatabase &database, const QByteArray
639 return !openedTheWrongDatabase; 658 return !openedTheWrongDatabase;
640} 659}
641 660
642bool DataStore::Transaction::validateNamedDatabases()
643{
644 return true;
645}
646
647DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const 661DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const
648{ 662{
649 if (!d) { 663 if (!d) {
@@ -694,7 +708,7 @@ QList<QByteArray> DataStore::Transaction::getDatabaseNames() const
694class DataStore::Private 708class DataStore::Private
695{ 709{
696public: 710public:
697 Private(const QString &s, const QString &n, AccessMode m); 711 Private(const QString &s, const QString &n, AccessMode m, const DbLayout &layout = {});
698 ~Private(); 712 ~Private();
699 713
700 QString storageRoot; 714 QString storageRoot;
@@ -702,68 +716,85 @@ public:
702 716
703 MDB_env *env; 717 MDB_env *env;
704 AccessMode mode; 718 AccessMode mode;
719 Sink::Log::Context logCtx;
720
721 void initEnvironment(const QString &fullPath, const DbLayout &layout)
722 {
723 // Ensure the environment is only created once, and that we only have one environment per process
724 if (!(env = sEnvironments.value(fullPath))) {
725 QMutexLocker locker(&sMutex);
726 if (!(env = sEnvironments.value(fullPath))) {
727 int rc = 0;
728 if ((rc = mdb_env_create(&env))) {
729 SinkWarningCtx(logCtx) << "mdb_env_create: " << rc << " " << mdb_strerror(rc);
730 qCritical() << "mdb_env_create: " << rc << " " << mdb_strerror(rc);
731 } else {
732 //Limit large enough to accomodate all our named dbs. This only starts to matter if the number gets large, otherwise it's just a bunch of extra entries in the main table.
733 mdb_env_set_maxdbs(env, 50);
734 const bool readOnly = (mode == ReadOnly);
735 unsigned int flags = MDB_NOTLS;
736 if (readOnly) {
737 flags |= MDB_RDONLY;
738 }
739 if ((rc = mdb_env_open(env, fullPath.toStdString().data(), flags, 0664))) {
740 SinkWarningCtx(logCtx) << "mdb_env_open: " << rc << ":" << mdb_strerror(rc);
741 mdb_env_close(env);
742 env = 0;
743 } else {
744 if (RUNNING_ON_VALGRIND) {
745 // In order to run valgrind this size must be smaller than half your available RAM
746 // https://github.com/BVLC/caffe/issues/2404
747 mdb_env_set_mapsize(env, (size_t)10485760 * (size_t)1000); // 1MB * 1000
748 } else {
749 //This is the maximum size of the db (but will not be used directly), so we make it large enough that we hopefully never run into the limit.
750 mdb_env_set_mapsize(env, (size_t)10485760 * (size_t)100000); // 1MB * 1000
751 }
752 Q_ASSERT(env);
753 sEnvironments.insert(fullPath, env);
754 //Open all available dbi's
755 bool noLock = true;
756 auto t = Transaction(new Transaction::Private(readOnly, nullptr, name, env, noLock));
757 if (!layout.tables.isEmpty()) {
758
759 //TODO upgrade db if the layout has changed:
760 //* read existing layout
761 //* if layout is not the same create new layout
762 //If the db is read only, abort if the db is not yet existing.
763 //If the db is not read-only but is not existing, ensure we have a layout and create all tables.
764
765 for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) {
766 bool allowDuplicates = it.value();
767 t.openDatabase(it.key(), {}, allowDuplicates);
768 }
769 } else {
770 for (const auto &db : t.getDatabaseNames()) {
771 //Get dbi to store for future use.
772 t.openDatabase(db);
773 }
774 }
775 //To persist the dbis (this is also necessary for read-only transactions)
776 t.commit();
777 }
778 }
779 }
780 }
781 }
782
705}; 783};
706 784
707DataStore::Private::Private(const QString &s, const QString &n, AccessMode m) : storageRoot(s), name(n), env(0), mode(m) 785DataStore::Private::Private(const QString &s, const QString &n, AccessMode m, const DbLayout &layout) : storageRoot(s), name(n), env(0), mode(m), logCtx(n.toLatin1())
708{ 786{
787
709 const QString fullPath(storageRoot + '/' + name); 788 const QString fullPath(storageRoot + '/' + name);
710 QFileInfo dirInfo(fullPath); 789 QFileInfo dirInfo(fullPath);
711 if (!dirInfo.exists() && mode == ReadWrite) { 790 if (!dirInfo.exists() && mode == ReadWrite) {
712 QDir().mkpath(fullPath); 791 QDir().mkpath(fullPath);
713 dirInfo.refresh(); 792 dirInfo.refresh();
714 } 793 }
715 Sink::Log::Context logCtx{n.toLatin1()};
716 if (mode == ReadWrite && !dirInfo.permission(QFile::WriteOwner)) { 794 if (mode == ReadWrite && !dirInfo.permission(QFile::WriteOwner)) {
717 qCritical() << fullPath << "does not have write permissions. Aborting"; 795 qCritical() << fullPath << "does not have write permissions. Aborting";
718 } else if (dirInfo.exists()) { 796 } else if (dirInfo.exists()) {
719 // Ensure the environment is only created once 797 initEnvironment(fullPath, layout);
720 QMutexLocker locker(&sMutex);
721
722 /*
723 * It seems we can only ever have one environment open in the process.
724 * Otherwise multi-threading breaks.
725 */
726 env = sEnvironments.value(fullPath);
727 if (!env) {
728 int rc = 0;
729 if ((rc = mdb_env_create(&env))) {
730 // TODO: handle error
731 SinkWarningCtx(logCtx) << "mdb_env_create: " << rc << " " << mdb_strerror(rc);
732 } else {
733 mdb_env_set_maxdbs(env, 50);
734 unsigned int flags = MDB_NOTLS;
735 if (mode == ReadOnly) {
736 flags |= MDB_RDONLY;
737 }
738 if ((rc = mdb_env_open(env, fullPath.toStdString().data(), flags, 0664))) {
739 SinkWarningCtx(logCtx) << "mdb_env_open: " << rc << ":" << mdb_strerror(rc);
740 mdb_env_close(env);
741 env = 0;
742 } else {
743 if (RUNNING_ON_VALGRIND) {
744 // In order to run valgrind this size must be smaller than half your available RAM
745 // https://github.com/BVLC/caffe/issues/2404
746 const size_t dbSize = (size_t)10485760 * (size_t)1000; // 1MB * 1000
747 mdb_env_set_mapsize(env, dbSize);
748 } else {
749 // FIXME: dynamic resize
750 const size_t dbSize = (size_t)10485760 * (size_t)8000; // 1MB * 8000
751 mdb_env_set_mapsize(env, dbSize);
752 }
753 sEnvironments.insert(fullPath, env);
754 //Open all available dbi's
755 bool noLock = true;
756 bool requestedRead = m == ReadOnly;
757 auto t = Transaction(new Transaction::Private(requestedRead, nullptr, name, env, noLock));
758 for (const auto &db : t.getDatabaseNames()) {
759 //Get dbi to store for future use.
760 t.openDatabase(db);
761 }
762 //To persist the dbis (this is also necessary for read-only transactions)
763 t.commit();
764 }
765 }
766 }
767 } 798 }
768} 799}
769 800
@@ -777,6 +808,10 @@ DataStore::DataStore(const QString &storageRoot, const QString &name, AccessMode
777{ 808{
778} 809}
779 810
811DataStore::DataStore(const QString &storageRoot, const DbLayout &dbLayout, AccessMode mode) : d(new Private(storageRoot, dbLayout.name, mode, dbLayout))
812{
813}
814
780DataStore::~DataStore() 815DataStore::~DataStore()
781{ 816{
782 delete d; 817 delete d;