summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-05-11 16:36:39 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-05-11 17:47:16 +0200
commit1291ccf13a0b8a1eddb6aaed24b45ceb31a2e01e (patch)
tree661e33c6f3e1abc3054d52bd9cc2d378a0054ded
parent330f9418cac14664b2f80e0c2a5a89295d8ec51f (diff)
downloadsink-1291ccf13a0b8a1eddb6aaed24b45ceb31a2e01e.tar.gz
sink-1291ccf13a0b8a1eddb6aaed24b45ceb31a2e01e.zip
initEnvironment with double-checked locking
-rw-r--r--common/storage_lmdb.cpp124
1 files changed, 67 insertions, 57 deletions
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index c3240f6..f7999d1 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -716,75 +716,85 @@ public:
716 716
717 MDB_env *env; 717 MDB_env *env;
718 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
719}; 783};
720 784
721DataStore::Private::Private(const QString &s, const QString &n, AccessMode m, const DbLayout &layout) : 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())
722{ 786{
787
723 const QString fullPath(storageRoot + '/' + name); 788 const QString fullPath(storageRoot + '/' + name);
724 QFileInfo dirInfo(fullPath); 789 QFileInfo dirInfo(fullPath);
725 if (!dirInfo.exists() && mode == ReadWrite) { 790 if (!dirInfo.exists() && mode == ReadWrite) {
726 QDir().mkpath(fullPath); 791 QDir().mkpath(fullPath);
727 dirInfo.refresh(); 792 dirInfo.refresh();
728 } 793 }
729 Sink::Log::Context logCtx{n.toLatin1()};
730 if (mode == ReadWrite && !dirInfo.permission(QFile::WriteOwner)) { 794 if (mode == ReadWrite && !dirInfo.permission(QFile::WriteOwner)) {
731 qCritical() << fullPath << "does not have write permissions. Aborting"; 795 qCritical() << fullPath << "does not have write permissions. Aborting";
732 } else if (dirInfo.exists()) { 796 } else if (dirInfo.exists()) {
733 // Ensure the environment is only created once 797 initEnvironment(fullPath, layout);
734 QMutexLocker locker(&sMutex);
735
736 /*
737 * It seems we can only ever have one environment open in the process.
738 * Otherwise multi-threading breaks.
739 */
740 env = sEnvironments.value(fullPath);
741 if (!env) {
742 int rc = 0;
743 if ((rc = mdb_env_create(&env))) {
744 // TODO: handle error
745 SinkWarningCtx(logCtx) << "mdb_env_create: " << rc << " " << mdb_strerror(rc);
746 } else {
747 mdb_env_set_maxdbs(env, 50);
748 unsigned int flags = MDB_NOTLS;
749 if (mode == ReadOnly) {
750 flags |= MDB_RDONLY;
751 }
752 if ((rc = mdb_env_open(env, fullPath.toStdString().data(), flags, 0664))) {
753 SinkWarningCtx(logCtx) << "mdb_env_open: " << rc << ":" << mdb_strerror(rc);
754 mdb_env_close(env);
755 env = 0;
756 } else {
757 if (RUNNING_ON_VALGRIND) {
758 // In order to run valgrind this size must be smaller than half your available RAM
759 // https://github.com/BVLC/caffe/issues/2404
760 const size_t dbSize = (size_t)10485760 * (size_t)1000; // 1MB * 1000
761 mdb_env_set_mapsize(env, dbSize);
762 } else {
763 // FIXME: dynamic resize
764 const size_t dbSize = (size_t)10485760 * (size_t)8000; // 1MB * 8000
765 mdb_env_set_mapsize(env, dbSize);
766 }
767 sEnvironments.insert(fullPath, env);
768 //Open all available dbi's
769 bool noLock = true;
770 bool requestedRead = m == ReadOnly;
771 auto t = Transaction(new Transaction::Private(requestedRead, nullptr, name, env, noLock));
772 if (!layout.tables.isEmpty()) {
773 for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) {
774 bool allowDuplicates = it.value();
775 t.openDatabase(it.key(), {}, allowDuplicates);
776 }
777 } else {
778 for (const auto &db : t.getDatabaseNames()) {
779 //Get dbi to store for future use.
780 t.openDatabase(db);
781 }
782 }
783 //To persist the dbis (this is also necessary for read-only transactions)
784 t.commit();
785 }
786 }
787 }
788 } 798 }
789} 799}
790 800