summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenrik Edin <henrik.edin@mongodb.com>2019-04-11 14:32:36 -0400
committerHenrik Edin <henrik.edin@mongodb.com>2019-04-24 11:15:31 -0400
commit9e4f12393b4a468175f9f0a6cc74dd669bf1fd5e (patch)
treeaa76481db392f7bcd266074a6a29ffa3f0d60097
parentf202c4c1ba24b9f561e8b11dac5b04fa0eeb4919 (diff)
downloadmongo-9e4f12393b4a468175f9f0a6cc74dd669bf1fd5e.tar.gz
SERVER-40125 Add a periodic job that runs incremental vacuum on the mobile storage engine.
Also fix options and parameter handling for the mobile storage engine options.
-rw-r--r--src/mongo/db/storage/mobile/SConscript19
-rw-r--r--src/mongo/db/storage/mobile/mobile_init.cpp9
-rw-r--r--src/mongo/db/storage/mobile/mobile_kv_engine.cpp168
-rw-r--r--src/mongo/db/storage/mobile/mobile_kv_engine.h15
-rw-r--r--src/mongo/db/storage/mobile/mobile_kv_engine_test.cpp39
-rw-r--r--src/mongo/db/storage/mobile/mobile_options.cpp (renamed from src/mongo/db/storage/mobile/mobile_global_options.h)21
-rw-r--r--src/mongo/db/storage/mobile/mobile_options.h (renamed from src/mongo/db/storage/mobile/mobile_global_options.cpp)47
-rw-r--r--src/mongo/db/storage/mobile/mobile_options.idl (renamed from src/mongo/db/storage/mobile/mobile_global_options.idl)43
-rw-r--r--src/mongo/db/storage/mobile/mobile_options_mongod.cpp (renamed from src/mongo/db/storage/mobile/mobile_options_init.cpp)28
-rw-r--r--src/mongo/db/storage/mobile/mobile_record_store_test.cpp18
-rw-r--r--src/mongo/db/storage/mobile/mobile_recovery_unit.cpp4
-rw-r--r--src/mongo/db/storage/mobile/mobile_session_pool.cpp8
-rw-r--r--src/mongo/db/storage/mobile/mobile_session_pool.h15
-rw-r--r--src/mongo/db/storage/mobile/mobile_sqlite_statement.cpp4
-rw-r--r--src/mongo/db/storage/mobile/mobile_sqlite_statement.h2
-rw-r--r--src/mongo/db/storage/mobile/mobile_util.cpp16
-rw-r--r--src/mongo/db/storage/mobile/mobile_util.h3
-rw-r--r--src/mongo/embedded/embedded_options.cpp5
-rw-r--r--src/mongo/embedded/mongo_embedded/SConscript1
-rw-r--r--src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp10
-rw-r--r--src/mongo/util/options_parser/options_parser.cpp8
21 files changed, 279 insertions, 204 deletions
diff --git a/src/mongo/db/storage/mobile/SConscript b/src/mongo/db/storage/mobile/SConscript
index 72d0e6bbf65..294dd3deb37 100644
--- a/src/mongo/db/storage/mobile/SConscript
+++ b/src/mongo/db/storage/mobile/SConscript
@@ -16,9 +16,8 @@ env.Library(
'mobile_session_pool.cpp',
'mobile_sqlite_statement.cpp',
'mobile_util.cpp',
- 'mobile_global_options.cpp',
- 'mobile_options_init.cpp',
- env.Idlc('mobile_global_options.idl')[0],
+ 'mobile_options.cpp',
+ env.Idlc('mobile_options.idl')[0],
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
@@ -46,6 +45,19 @@ if mobile_se:
'$BUILD_DIR/mongo/mongod',
'$BUILD_DIR/mongo/dbtests/dbtest',
]
+
+ env.Library(
+ target='mobile_options_mongod',
+ source=[
+ 'mobile_options_mongod.cpp',
+ ],
+ PROGDEPS_DEPENDENTS=serveronlyProgDepsDependents,
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/util/options_parser/options_parser',
+ ],
+ LIBDEPS_PRIVATE=[
+ ],
+ )
env.Library(
target='storage_mobile',
@@ -84,6 +96,7 @@ env.CppUnitTest(
'storage_mobile_core',
'$BUILD_DIR/mongo/db/storage/record_store_test_harness',
'$BUILD_DIR/mongo/db/concurrency/write_conflict_exception',
+ '$BUILD_DIR/mongo/util/options_parser/options_parser',
]
)
diff --git a/src/mongo/db/storage/mobile/mobile_init.cpp b/src/mongo/db/storage/mobile/mobile_init.cpp
index 54620ab048c..e504d40cb7e 100644
--- a/src/mongo/db/storage/mobile/mobile_init.cpp
+++ b/src/mongo/db/storage/mobile/mobile_init.cpp
@@ -34,8 +34,8 @@
#include "mongo/base/init.h"
#include "mongo/db/service_context.h"
#include "mongo/db/storage/kv/kv_storage_engine.h"
-#include "mongo/db/storage/mobile/mobile_global_options.h"
#include "mongo/db/storage/mobile/mobile_kv_engine.h"
+#include "mongo/db/storage/mobile/mobile_options.h"
#include "mongo/db/storage/storage_engine_init.h"
#include "mongo/db/storage/storage_options.h"
@@ -54,11 +54,8 @@ public:
options.directoryPerDB = params.directoryperdb;
options.forRepair = params.repair;
- MobileKVEngine* kvEngine = new MobileKVEngine(params.dbpath,
- mobileGlobalOptions.mobileDurabilityLevel,
- mobileGlobalOptions.mobileCacheSizeKB,
- mobileGlobalOptions.mobileMmapSizeKB,
- mobileGlobalOptions.mobileJournalSizeLimitKB);
+ MobileKVEngine* kvEngine = new MobileKVEngine(
+ params.dbpath, embedded::mobileGlobalOptions, getGlobalServiceContext());
return new KVStorageEngine(kvEngine, options);
}
diff --git a/src/mongo/db/storage/mobile/mobile_kv_engine.cpp b/src/mongo/db/storage/mobile/mobile_kv_engine.cpp
index 14842514d28..4bf076e5b99 100644
--- a/src/mongo/db/storage/mobile/mobile_kv_engine.cpp
+++ b/src/mongo/db/storage/mobile/mobile_kv_engine.cpp
@@ -37,6 +37,8 @@
#include <memory>
#include <vector>
+#include "mongo/db/concurrency/d_concurrency.h"
+#include "mongo/db/concurrency/lock_state.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/storage/mobile/mobile_index.h"
@@ -51,89 +53,105 @@
#include "mongo/util/scopeguard.h"
namespace mongo {
+namespace {
+int64_t queryPragmaInt(const MobileSession& session, StringData pragma) {
+ SqliteStatement stmt(session, "PRAGMA ", pragma, ";");
+ stmt.step(SQLITE_ROW);
+ return stmt.getColInt(0);
+}
-class MobileSession;
-class SqliteStatement;
+std::string queryPragmaStr(const MobileSession& session, StringData pragma) {
+ SqliteStatement stmt(session, "PRAGMA ", pragma, ";");
+ stmt.step(SQLITE_ROW);
+ return stmt.getColText(0);
+}
+} // namespace
MobileKVEngine::MobileKVEngine(const std::string& path,
- std::uint32_t durabilityLevel,
- std::uint32_t cacheSizeKB,
- std::uint32_t mmapSizeKB,
- std::uint32_t journalSizeLimitKB) {
+ const embedded::MobileOptions& options,
+ ServiceContext* serviceContext)
+ : _options(options) {
_initDBPath(path);
- // Initialize the database to be in WAL mode.
- sqlite3* initSession;
- int status = sqlite3_open(_path.c_str(), &initSession);
- embedded::checkStatus(status, SQLITE_OK, "sqlite3_open");
-
- // Guarantees that sqlite3_close() will be called when the function returns.
- ON_BLOCK_EXIT([&initSession] { sqlite3_close(initSession); });
-
- embedded::configureSession(initSession);
-
- // Check and ensure that WAL mode is working as expected
- // This is not something that we want to be configurable
- {
- sqlite3_stmt* stmt;
- status = sqlite3_prepare_v2(initSession, "PRAGMA journal_mode;", -1, &stmt, NULL);
- embedded::checkStatus(status, SQLITE_OK, "sqlite3_prepare_v2");
-
- status = sqlite3_step(stmt);
- embedded::checkStatus(status, SQLITE_ROW, "sqlite3_step");
-
- // Pragma returns current mode in SQLite, ensure it is "wal" mode.
- const void* colText = sqlite3_column_text(stmt, 0);
- const char* mode = reinterpret_cast<const char*>(colText);
- fassert(37001, !strcmp(mode, "wal"));
- status = sqlite3_finalize(stmt);
- embedded::checkStatus(status, SQLITE_OK, "sqlite3_finalize");
-
- LOG(MOBILE_LOG_LEVEL_LOW) << "MobileSE: Confirmed SQLite database opened in WAL mode";
+ _sessionPool.reset(new MobileSessionPool(_path, _options));
+
+ // getSession only needs a valid opCtx if the pool has no available sessions and we need to wait
+ // for one. But as this is init code and we've just initialized the pool that should not happen.
+ // Passing nullptr as opCtx so we avoid creating one here.
+ auto session = _sessionPool->getSession(nullptr);
+
+ fassert(37001, queryPragmaStr(*session, "journal_mode"_sd) == "wal");
+ LOG(MOBILE_LOG_LEVEL_LOW) << "MobileSE: Confirmed SQLite database opened in WAL mode";
+
+ fassert(50869, queryPragmaInt(*session, "synchronous"_sd) == _options.durabilityLevel);
+ LOG(MOBILE_LOG_LEVEL_LOW) << "MobileSE: Confirmed SQLite database has synchronous set to: "
+ << _options.durabilityLevel;
+
+ fassert(50868, queryPragmaInt(*session, "fullfsync"_sd) == 1);
+ LOG(MOBILE_LOG_LEVEL_LOW) << "MobileSE: Confirmed SQLite database is set to fsync with "
+ "F_FULLFSYNC if the platform supports it (currently only darwin "
+ "kernels). Value: 1";
+
+ if (!_options.disableVacuumJob) {
+ _vacuumJob = serviceContext->getPeriodicRunner()->makeJob(
+ PeriodicRunner::PeriodicJob("SQLiteVacuumJob",
+ [this](Client* client) {
+ if (!client->getServiceContext()->getStorageEngine())
+ return;
+ maybeVacuum(client, Date_t::max());
+ },
+ Minutes(options.vacuumCheckIntervalMinutes)));
+ _vacuumJob->start();
}
+}
- // Check and ensure that synchronous mode is working as expected
- {
- sqlite3_stmt* stmt;
- status = sqlite3_prepare_v2(initSession, "PRAGMA synchronous;", -1, &stmt, NULL);
- embedded::checkStatus(status, SQLITE_OK, "sqlite3_prepare_v2");
-
- status = sqlite3_step(stmt);
- embedded::checkStatus(status, SQLITE_ROW, "sqlite3_step");
-
- // Pragma returns current "synchronous" setting
- std::uint32_t sync_val = sqlite3_column_int(stmt, 0);
- fassert(50869, sync_val == durabilityLevel);
- status = sqlite3_finalize(stmt);
- embedded::checkStatus(status, SQLITE_OK, "sqlite3_finalize");
+void MobileKVEngine::cleanShutdown() {
+ try {
+ if (!_options.disableVacuumJob)
+ maybeVacuum(Client::getCurrent(), Date_t());
+ } catch (const std::exception& e) {
+ LOG(MOBILE_LOG_LEVEL_LOW)
+ << "MobileSE: Exception while doing vacuum at shutdown, surpressing. " << e.what();
+ }
+}
- LOG(MOBILE_LOG_LEVEL_LOW) << "MobileSE: Confirmed SQLite database has synchronous "
- << "set to: " << durabilityLevel;
+void MobileKVEngine::maybeVacuum(Client* client, Date_t deadline) {
+ ServiceContext::UniqueOperationContext opCtxUPtr;
+ OperationContext* opCtx = client->getOperationContext();
+ if (!opCtx) {
+ opCtxUPtr = client->makeOperationContext();
+ opCtx = opCtxUPtr.get();
}
- // Check and ensure that we were able to set the F_FULLFSYNC fcntl on darwin kernels
- // This prevents data corruption as fsync doesn't work as expected
- // This is not something that we want to be configurable
+ std::unique_ptr<MobileSession> session;
+ int64_t pageCount;
+ int64_t freelistCount;
{
- sqlite3_stmt* stmt;
- status = sqlite3_prepare_v2(initSession, "PRAGMA fullfsync;", -1, &stmt, NULL);
- embedded::checkStatus(status, SQLITE_OK, "sqlite3_prepare_v2");
-
- status = sqlite3_step(stmt);
- embedded::checkStatus(status, SQLITE_ROW, "sqlite3_step");
-
- // Pragma returns current fullsync setting, ensure it is enabled.
- int fullfsync_val = sqlite3_column_int(stmt, 0);
- fassert(50868, fullfsync_val == 1);
- status = sqlite3_finalize(stmt);
- embedded::checkStatus(status, SQLITE_OK, "sqlite3_finalize");
-
- LOG(MOBILE_LOG_LEVEL_LOW) << "MobileSE: Confirmed SQLite database is set to fsync "
- << "with F_FULLFSYNC if the platform supports it (currently"
- << " only darwin kernels). Value: " << fullfsync_val;
+ // There may be other threads doing write operations that has locked the database in
+ // exclusive mode. Grab an S lock here to ensure that isn't the case while we get the
+ // session and query the pragmas.
+ Lock::GlobalLock lk(opCtx, MODE_S, deadline, Lock::InterruptBehavior::kThrow);
+ if (!lk.isLocked())
+ return;
+
+ session = _sessionPool->getSession(opCtx);
+ pageCount = queryPragmaInt(*session, "page_count"_sd);
+ freelistCount = queryPragmaInt(*session, "freelist_count"_sd);
}
- _sessionPool.reset(new MobileSessionPool(_path));
+ constexpr int kPageSize = 4096; // SQLite default
+ LOG(MOBILE_LOG_LEVEL_LOW) << "MobileSE: Evaluating if we need to vacuum. page_count = "
+ << pageCount << ", freelist_count = " << freelistCount;
+ if ((pageCount > 0 && (float)freelistCount / pageCount >= _options.vacuumFreePageRatio) ||
+ (freelistCount * kPageSize >= _options.vacuumFreeSizeMB * 1024 * 1024)) {
+ LOG(MOBILE_LOG_LEVEL_LOW) << "MobileSE: Performing incremental vacuum";
+ // Data will we moved on the file system, take an exclusive lock
+ Lock::GlobalLock lk(opCtx, MODE_X, deadline, Lock::InterruptBehavior::kThrow);
+ if (!lk.isLocked())
+ return;
+
+ SqliteStatement::execQuery(session.get(), "PRAGMA incremental_vacuum;");
+ }
}
void MobileKVEngine::_initDBPath(const std::string& path) {
@@ -245,8 +263,8 @@ Status MobileKVEngine::dropIdent(OperationContext* opCtx, StringData ident) {
}
/**
- * Note: this counts the total number of bytes in the key and value columns, not the actual number
- * of bytes on disk used by this ident.
+ * Note: this counts the total number of bytes in the key and value columns, not the actual
+ * number of bytes on disk used by this ident.
*/
int64_t MobileKVEngine::getIdentSize(OperationContext* opCtx, StringData ident) {
MobileSession* session = MobileRecoveryUnit::get(opCtx)->getSession(opCtx);
@@ -255,9 +273,9 @@ int64_t MobileKVEngine::getIdentSize(OperationContext* opCtx, StringData ident)
SqliteStatement colNameStmt(*session, "PRAGMA table_info(\"", ident, "\")");
colNameStmt.step(SQLITE_ROW);
- std::string keyColName(static_cast<const char*>(colNameStmt.getColText(1)));
+ std::string keyColName(colNameStmt.getColText(1));
colNameStmt.step(SQLITE_ROW);
- std::string valueColName(static_cast<const char*>(colNameStmt.getColText(1)));
+ std::string valueColName(colNameStmt.getColText(1));
colNameStmt.step(SQLITE_DONE);
// Get total data size of key-value columns.
@@ -299,7 +317,7 @@ std::vector<std::string> MobileKVEngine::getAllIdents(OperationContext* opCtx) c
int status;
while ((status = getTablesStmt.step()) == SQLITE_ROW) {
- std::string tableName(reinterpret_cast<const char*>(getTablesStmt.getColText(0)));
+ std::string tableName(getTablesStmt.getColText(0));
idents.push_back(tableName);
}
embedded::checkStatus(status, SQLITE_DONE, "sqlite3_step");
diff --git a/src/mongo/db/storage/mobile/mobile_kv_engine.h b/src/mongo/db/storage/mobile/mobile_kv_engine.h
index 87145591398..75fa8140cf8 100644
--- a/src/mongo/db/storage/mobile/mobile_kv_engine.h
+++ b/src/mongo/db/storage/mobile/mobile_kv_engine.h
@@ -33,8 +33,10 @@
#include "mongo/db/storage/journal_listener.h"
#include "mongo/db/storage/kv/kv_engine.h"
+#include "mongo/db/storage/mobile/mobile_options.h"
#include "mongo/db/storage/mobile/mobile_session_pool.h"
#include "mongo/stdx/mutex.h"
+#include "mongo/util/periodic_runner.h"
#include "mongo/util/string_map.h"
namespace mongo {
@@ -44,10 +46,8 @@ class JournalListener;
class MobileKVEngine : public KVEngine {
public:
MobileKVEngine(const std::string& path,
- std::uint32_t durabilityLevel,
- std::uint32_t cacheSizeKB,
- std::uint32_t mmapSizeKB,
- std::uint32_t journalSizeLimitKB);
+ const embedded::MobileOptions& options,
+ ServiceContext* serviceContext);
RecoveryUnit* newRecoveryUnit() override;
@@ -117,7 +117,7 @@ public:
return Status::OK();
}
- void cleanShutdown() override{};
+ void cleanShutdown() override;
bool hasIdent(OperationContext* opCtx, StringData ident) const override;
@@ -137,6 +137,8 @@ public:
}
private:
+ void maybeVacuum(Client* client, Date_t deadline);
+
mutable stdx::mutex _mutex;
void _initDBPath(const std::string& path);
std::int32_t _setSQLitePragma(const std::string& pragma, sqlite3* session);
@@ -147,6 +149,9 @@ private:
JournalListener* _journalListener = &NoOpJournalListener::instance;
std::string _path;
+ embedded::MobileOptions _options;
+
+ std::unique_ptr<PeriodicRunner::PeriodicJobHandle> _vacuumJob;
};
} // namespace mongo
diff --git a/src/mongo/db/storage/mobile/mobile_kv_engine_test.cpp b/src/mongo/db/storage/mobile/mobile_kv_engine_test.cpp
index 64011b02733..3cde73486e7 100644
--- a/src/mongo/db/storage/mobile/mobile_kv_engine_test.cpp
+++ b/src/mongo/db/storage/mobile/mobile_kv_engine_test.cpp
@@ -31,30 +31,38 @@
#include "mongo/db/storage/kv/kv_engine_test_harness.h"
#include "mongo/db/storage/mobile/mobile_kv_engine.h"
+#include "mongo/db/storage/mobile/mobile_options_gen.h"
#include "mongo/stdx/memory.h"
#include "mongo/unittest/temp_dir.h"
+#include "mongo/util/options_parser/options_parser.h"
+#include "mongo/util/options_parser/startup_options.h"
namespace mongo {
namespace {
class MobileKVHarnessHelper : public KVHarnessHelper {
public:
- MobileKVHarnessHelper()
- : _dbPath("mobile_kv_engine_harness"),
- _mobileDurabilityLevel(1),
- _cacheSizeKB(10240),
- _mmapSizeKB(51200),
- _journalSizeLimitKB(5120) {
+ MobileKVHarnessHelper() : _dbPath("mobile_kv_engine_harness") {
+ addMobileStorageOptionDefinitions(&optionenvironment::startupOptions).ignore();
+ optionenvironment::OptionsParser parser;
+ std::vector<std::string> args;
+ std::map<std::string, std::string> env;
+ parser
+ .run(optionenvironment::startupOptions,
+ args,
+ env,
+ &optionenvironment::startupOptionsParsed)
+ .ignore();
+ storeMobileStorageOptionDefinitions(optionenvironment::startupOptionsParsed).ignore();
+
+ embedded::mobileGlobalOptions.disableVacuumJob = true;
_engine = stdx::make_unique<MobileKVEngine>(
- _dbPath.path(), _mobileDurabilityLevel, _cacheSizeKB, _mmapSizeKB, _journalSizeLimitKB);
+ _dbPath.path(), embedded::mobileGlobalOptions, nullptr);
}
virtual KVEngine* restartEngine() {
- _engine.reset(new MobileKVEngine(_dbPath.path(),
- _mobileDurabilityLevel,
- _cacheSizeKB,
- _mmapSizeKB,
- _journalSizeLimitKB));
+ _engine.reset(new MobileKVEngine(
+ _dbPath.path(), embedded::mobileGlobalOptions, _serviceContext.get()));
return _engine.get();
}
@@ -63,12 +71,9 @@ public:
}
private:
- std::unique_ptr<MobileKVEngine> _engine;
unittest::TempDir _dbPath;
- std::uint32_t _mobileDurabilityLevel;
- std::uint32_t _cacheSizeKB;
- std::uint32_t _mmapSizeKB;
- std::uint32_t _journalSizeLimitKB;
+ std::unique_ptr<MobileKVEngine> _engine;
+ ServiceContext::UniqueServiceContext _serviceContext;
};
std::unique_ptr<KVHarnessHelper> makeHelper() {
diff --git a/src/mongo/db/storage/mobile/mobile_global_options.h b/src/mongo/db/storage/mobile/mobile_options.cpp
index e1e822e4c0b..ac5ff02aed9 100644
--- a/src/mongo/db/storage/mobile/mobile_global_options.h
+++ b/src/mongo/db/storage/mobile/mobile_options.cpp
@@ -27,25 +27,14 @@
* it in the license file.
*/
-#pragma once
+#include "mongo/platform/basic.h"
-#include <string>
-
-#include "mongo/base/status.h"
-#include "mongo/util/options_parser/environment.h"
+#include "mongo/db/storage/mobile/mobile_options.h"
namespace mongo {
+namespace embedded {
-class MobileGlobalOptions {
-public:
- std::uint32_t mobileDurabilityLevel = 1;
- std::uint32_t mobileCacheSizeKB = 10240;
- std::uint32_t mobileMmapSizeKB = 51200;
- std::uint32_t mobileJournalSizeLimitKB = 5120;
-
- Status store(const optionenvironment::Environment& params);
-};
-
-extern MobileGlobalOptions mobileGlobalOptions;
+MobileOptions mobileGlobalOptions;
+} // namespace embedded
} // namespace mongo
diff --git a/src/mongo/db/storage/mobile/mobile_global_options.cpp b/src/mongo/db/storage/mobile/mobile_options.h
index e93dfdbeab1..f5dd4ec71ac 100644
--- a/src/mongo/db/storage/mobile/mobile_global_options.cpp
+++ b/src/mongo/db/storage/mobile/mobile_options.h
@@ -27,38 +27,33 @@
* it in the license file.
*/
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
+#pragma once
-#include "mongo/platform/basic.h"
+#include <string>
-#include "mongo/db/storage/mobile/mobile_global_options.h"
-
-#include "mongo/util/log.h"
-
-namespace moe = mongo::optionenvironment;
+#include "mongo/base/status.h"
+#include "mongo/util/options_parser/environment.h"
namespace mongo {
+namespace embedded {
+
+struct MobileOptions {
+ // Initialize to broken nonsense defaults, the real ones are in IDL
+ uint32_t durabilityLevel = 0;
+ uint32_t cacheSizeKB = 0;
+ uint32_t mmapSizeKB = 0;
+ uint32_t journalSizeLimitKB = 0;
-MobileGlobalOptions mobileGlobalOptions;
+ double vacuumFreePageRatio = 0.0;
+ uint32_t vacuumFreeSizeMB = 0;
+ uint32_t vacuumCheckIntervalMinutes = 0;
-Status MobileGlobalOptions::store(const moe::Environment& params) {
- // Mobile storage engine options
- if (params.count("storage.mobile.durabilityLevel")) {
- mobileGlobalOptions.mobileDurabilityLevel =
- params["storage.mobile.durabilityLevel"].as<int>();
- }
- if (params.count("storage.mobile.cacheSizeKB")) {
- mobileGlobalOptions.mobileCacheSizeKB = params["storage.mobile.cacheSizeKB"].as<int>();
- }
- if (params.count("storage.mobile.mmapSizeKB")) {
- mobileGlobalOptions.mobileMmapSizeKB = params["storage.mobile.mmapSizeKB"].as<int>();
- }
- if (params.count("storage.mobile.journalSizeLimitKB")) {
- mobileGlobalOptions.mobileJournalSizeLimitKB =
- params["storage.mobile.journalSizeLimitKB"].as<int>();
- }
+ // This setting is not available for users to configure. Just meant to be able to disable this
+ // feature in certain unit tests.
+ bool disableVacuumJob = false;
+};
- return Status::OK();
-}
+extern MobileOptions mobileGlobalOptions;
+} // namespace embedded
} // namespace mongo
diff --git a/src/mongo/db/storage/mobile/mobile_global_options.idl b/src/mongo/db/storage/mobile/mobile_options.idl
index 92529c308c3..c4ed0292208 100644
--- a/src/mongo/db/storage/mobile/mobile_global_options.idl
+++ b/src/mongo/db/storage/mobile/mobile_options.idl
@@ -29,12 +29,13 @@
global:
cpp_namespace: "mongo"
cpp_includes:
- - "mongo/db/storage/mobile/mobile_global_options.h"
+ - "mongo/db/storage/mobile/mobile_options.h"
configs:
section: 'Mobile Storage Engine options'
source: [ cli, ini, yaml ]
initializer:
register: addMobileStorageOptionDefinitions
+ store: storeMobileStorageOptionDefinitions
configs:
# Mobile storage engine options
@@ -49,32 +50,56 @@ configs:
performance, set this to 2 or 3. Conversely, if you are fine with
somewhat ephemeral local data then you can set this to 0.
arg_vartype: Int
- cpp_varname: 'mobileGlobalOptions.mobileDurabilityLevel'
+ cpp_varname: 'embedded::mobileGlobalOptions.durabilityLevel'
short_name: mobileDurabilityLevel
default: 1
validator: {gte: 0, lte: 3}
"storage.mobile.cacheSizeKB":
- description: 'The size of the cache in kilobytes.'
+ description: 'Maximum size in kilobytes of the database to be cached in memory.'
arg_vartype: Int
- cpp_varname: 'mobileGlobalOptions.mobileCacheSizeKB'
+ cpp_varname: 'embedded::mobileGlobalOptions.cacheSizeKB'
short_name: mobileCacheSizeKB
default: 10240
- validator: {gte: 0}
+ validator: {gte: 0, lte: {expr: 'std::numeric_limits<int>::max()'}}
"storage.mobile.mmapSizeKB":
- description: 'How much of the data can we memory map in kilobytes.'
+ description: 'Maximum size in kilobytes that can be used for memory mapped I/O.'
arg_vartype: Int
- cpp_varname: 'mobileGlobalOptions.mobileMmapSizeKB'
+ cpp_varname: 'embedded::mobileGlobalOptions.mmapSizeKB'
short_name: mobileMmapSizeKB
default: 51200
validator: {gte: 0}
"storage.mobile.journalSizeLimitKB":
- description: 'Limit the size of the WAL to this amount in kilobytes.'
+ description: 'Maximum size of the rollback journal in kilobytes that is stored on the file system.'
arg_vartype: Int
- cpp_varname: 'mobileGlobalOptions.mobileJournalSizeLimitKB'
+ cpp_varname: 'embedded::mobileGlobalOptions.journalSizeLimitKB'
short_name: mobileJournalSizeLimitKB
default: 5120
validator: {gte: 0}
+ "storage.mobile.vacuumFreePageRatio":
+ description: 'Ratio of free pages to total pages that triggers vacuuming, if above, of the database files on the file system.'
+ arg_vartype: Double
+ cpp_varname: 'embedded::mobileGlobalOptions.vacuumFreePageRatio'
+ short_name: mobileVacuumFreePageRatio
+ default: 0.25
+ validator: {gt: 0.0, lte: 1.0}
+
+ "storage.mobile.vacuumFreeSizeMB":
+ description: 'Number of megabytes of free space in the database files that trigger vaccuming.'
+ arg_vartype: Int
+ cpp_varname: 'embedded::mobileGlobalOptions.vacuumFreeSizeMB'
+ short_name: mobileVacuumFreeSizeMB
+ default: 50
+ validator: {gt: 0}
+
+ "storage.mobile.vacuumCheckIntervalMinutes":
+ description: 'Interval in minutes to check if vacuum needs to be triggered.'
+ arg_vartype: Int
+ cpp_varname: 'embedded::mobileGlobalOptions.vacuumCheckIntervalMinutes'
+ short_name: mobileVacuumCheckIntervalMinutes
+ default: 10
+ validator: {gt: 0}
+
diff --git a/src/mongo/db/storage/mobile/mobile_options_init.cpp b/src/mongo/db/storage/mobile/mobile_options_mongod.cpp
index e004088f12d..58a1d95485e 100644
--- a/src/mongo/db/storage/mobile/mobile_options_init.cpp
+++ b/src/mongo/db/storage/mobile/mobile_options_mongod.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018-present MongoDB, Inc.
+ * Copyright (C) 2019-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
@@ -27,27 +27,17 @@
* it in the license file.
*/
-#include "mongo/platform/basic.h"
-
+#include "mongo/db/storage/mobile/mobile_options_gen.h"
#include "mongo/util/options_parser/startup_option_init.h"
-
-#include <iostream>
-
-#include "mongo/db/storage/mobile/mobile_global_options.h"
-#include "mongo/util/exit_code.h"
#include "mongo/util/options_parser/startup_options.h"
-namespace moe = mongo::optionenvironment;
-
+// When the mobile storage engine is used in embedded we don't need to do this. But when mongod we
+// need to inject the mobile specific options.
namespace mongo {
-
-MONGO_STARTUP_OPTIONS_STORE(MobileOptions)(InitializerContext* context) {
- Status ret = mobileGlobalOptions.store(moe::startupOptionsParsed);
- if (!ret.isOK()) {
- std::cerr << ret.toString() << std::endl;
- std::cerr << "try '" << context->args()[0] << " --help' for more information" << std::endl;
- ::_exit(EXIT_BADOPTIONS);
- }
- return Status::OK();
+MONGO_MODULE_STARTUP_OPTIONS_REGISTER(mobile_options_mongod_register)(InitializerContext*) {
+ return addMobileStorageOptionDefinitions(&optionenvironment::startupOptions);
+}
+MONGO_STARTUP_OPTIONS_STORE(mobile_options_mongod_store)(InitializerContext*) {
+ return storeMobileStorageOptionDefinitions(optionenvironment::startupOptionsParsed);
}
} // namespace mongo
diff --git a/src/mongo/db/storage/mobile/mobile_record_store_test.cpp b/src/mongo/db/storage/mobile/mobile_record_store_test.cpp
index 4ec6f0b78cb..8fb268d64f4 100644
--- a/src/mongo/db/storage/mobile/mobile_record_store_test.cpp
+++ b/src/mongo/db/storage/mobile/mobile_record_store_test.cpp
@@ -35,6 +35,7 @@
#include "mongo/base/init.h"
#include "mongo/db/catalog/collection_options.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
+#include "mongo/db/storage/mobile/mobile_options_gen.h"
#include "mongo/db/storage/mobile/mobile_record_store.h"
#include "mongo/db/storage/mobile/mobile_recovery_unit.h"
#include "mongo/db/storage/mobile/mobile_session.h"
@@ -45,6 +46,8 @@
#include "mongo/stdx/memory.h"
#include "mongo/unittest/temp_dir.h"
#include "mongo/unittest/unittest.h"
+#include "mongo/util/options_parser/options_parser.h"
+#include "mongo/util/options_parser/startup_options.h"
namespace mongo {
@@ -85,7 +88,20 @@ public:
}
_fullPath = fullPath.string();
- _sessionPool.reset(new MobileSessionPool(_fullPath));
+
+ addMobileStorageOptionDefinitions(&optionenvironment::startupOptions).ignore();
+ optionenvironment::OptionsParser parser;
+ std::vector<std::string> args;
+ std::map<std::string, std::string> env;
+ parser
+ .run(optionenvironment::startupOptions,
+ args,
+ env,
+ &optionenvironment::startupOptionsParsed)
+ .ignore();
+ storeMobileStorageOptionDefinitions(optionenvironment::startupOptionsParsed).ignore();
+
+ _sessionPool.reset(new MobileSessionPool(_fullPath, embedded::mobileGlobalOptions));
}
std::unique_ptr<RecordStore> newNonCappedRecordStore() override {
diff --git a/src/mongo/db/storage/mobile/mobile_recovery_unit.cpp b/src/mongo/db/storage/mobile/mobile_recovery_unit.cpp
index 8ebd40e3f3f..2d194396feb 100644
--- a/src/mongo/db/storage/mobile/mobile_recovery_unit.cpp
+++ b/src/mongo/db/storage/mobile/mobile_recovery_unit.cpp
@@ -36,7 +36,7 @@
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/operation_context.h"
-#include "mongo/db/storage/mobile/mobile_global_options.h"
+#include "mongo/db/storage/mobile/mobile_options.h"
#include "mongo/db/storage/mobile/mobile_recovery_unit.h"
#include "mongo/db/storage/mobile/mobile_sqlite_statement.h"
#include "mongo/db/storage/mobile/mobile_util.h"
@@ -135,7 +135,7 @@ bool MobileRecoveryUnit::waitUntilDurable() {
// before going down but our powercycle test bench require it. Therefore make sure embedded does
// not call this (by disabling writeConcern j:true) but allow it when this is used inside
// mongod.
- if (mobileGlobalOptions.mobileDurabilityLevel < 2) {
+ if (_sessionPool->getOptions().durabilityLevel < 2) {
OperationContext* opCtx = Client::getCurrent()->getOperationContext();
_ensureSession(opCtx);
RECOVERY_UNIT_TRACE() << "waitUntilDurable called, attempting to perform a checkpoint";
diff --git a/src/mongo/db/storage/mobile/mobile_session_pool.cpp b/src/mongo/db/storage/mobile/mobile_session_pool.cpp
index 38f7b1b0575..1e73885d8b0 100644
--- a/src/mongo/db/storage/mobile/mobile_session_pool.cpp
+++ b/src/mongo/db/storage/mobile/mobile_session_pool.cpp
@@ -95,8 +95,10 @@ bool MobileDelayedOpQueue::isEmpty() {
return (_isEmpty.load());
}
-MobileSessionPool::MobileSessionPool(const std::string& path, std::uint64_t maxPoolSize)
- : _path(path), _maxPoolSize(maxPoolSize) {}
+MobileSessionPool::MobileSessionPool(const std::string& path,
+ const embedded::MobileOptions& options,
+ std::uint64_t maxPoolSize)
+ : _path(path), _options(options), _maxPoolSize(maxPoolSize) {}
MobileSessionPool::~MobileSessionPool() {
shutDown();
@@ -120,7 +122,7 @@ std::unique_ptr<MobileSession> MobileSessionPool::getSession(OperationContext* o
sqlite3* session;
int status = sqlite3_open(_path.c_str(), &session);
embedded::checkStatus(status, SQLITE_OK, "sqlite3_open");
- embedded::configureSession(session);
+ embedded::configureSession(session, _options);
_curPoolSize++;
return stdx::make_unique<MobileSession>(session, this);
}
diff --git a/src/mongo/db/storage/mobile/mobile_session_pool.h b/src/mongo/db/storage/mobile/mobile_session_pool.h
index 90535091fd8..605117e6983 100644
--- a/src/mongo/db/storage/mobile/mobile_session_pool.h
+++ b/src/mongo/db/storage/mobile/mobile_session_pool.h
@@ -35,6 +35,7 @@
#include <vector>
#include "mongo/db/operation_context.h"
+#include "mongo/db/storage/mobile/mobile_options.h"
#include "mongo/db/storage/mobile/mobile_session.h"
#include "mongo/stdx/mutex.h"
@@ -69,7 +70,9 @@ class MobileSessionPool final {
MobileSessionPool& operator=(const MobileSessionPool&) = delete;
public:
- MobileSessionPool(const std::string& path, std::uint64_t maxPoolSize = 80);
+ MobileSessionPool(const std::string& path,
+ const embedded::MobileOptions& options,
+ std::uint64_t maxPoolSize = 80);
~MobileSessionPool();
@@ -92,10 +95,15 @@ public:
// Failed drops get queued here and get re-tried periodically
MobileDelayedOpQueue failedDropsQueue;
+ // Returns the mobile options associated with this storage engine instance
+ const embedded::MobileOptions& getOptions() const {
+ return _options;
+ }
+
private:
/**
- * Gets the front element from _sessions and then pops it off the queue.
- */
+ * Gets the front element from _sessions and then pops it off the queue.
+ */
sqlite3* _popSession_inlock();
// This is used to lock the _sessions vector.
@@ -103,6 +111,7 @@ private:
stdx::condition_variable _releasedSessionNotifier;
std::string _path;
+ const embedded::MobileOptions& _options;
/**
* PoolSize is the number of open sessions associated with the session pool.
diff --git a/src/mongo/db/storage/mobile/mobile_sqlite_statement.cpp b/src/mongo/db/storage/mobile/mobile_sqlite_statement.cpp
index cebd769dc2f..151ad0d68d7 100644
--- a/src/mongo/db/storage/mobile/mobile_sqlite_statement.cpp
+++ b/src/mongo/db/storage/mobile/mobile_sqlite_statement.cpp
@@ -143,8 +143,8 @@ int64_t SqliteStatement::getColBytes(int colIndex) {
return sqlite3_column_bytes(_stmt, colIndex);
}
-const void* SqliteStatement::getColText(int colIndex) {
- return sqlite3_column_text(_stmt, colIndex);
+const char* SqliteStatement::getColText(int colIndex) {
+ return reinterpret_cast<const char*>(sqlite3_column_text(_stmt, colIndex));
}
void SqliteStatement::_execQuery(sqlite3* session, const char* query) {
diff --git a/src/mongo/db/storage/mobile/mobile_sqlite_statement.h b/src/mongo/db/storage/mobile/mobile_sqlite_statement.h
index 51a6c8a61e2..31a61f7ffa8 100644
--- a/src/mongo/db/storage/mobile/mobile_sqlite_statement.h
+++ b/src/mongo/db/storage/mobile/mobile_sqlite_statement.h
@@ -98,7 +98,7 @@ public:
*
* @param colIndex - zero-based index of a column retrieved from a query row.
*/
- const void* getColText(int colIndex);
+ const char* getColText(int colIndex);
/**
* Resets the statement to the first of the query result rows.
diff --git a/src/mongo/db/storage/mobile/mobile_util.cpp b/src/mongo/db/storage/mobile/mobile_util.cpp
index 3af03c7ca65..141cff1a070 100644
--- a/src/mongo/db/storage/mobile/mobile_util.cpp
+++ b/src/mongo/db/storage/mobile/mobile_util.cpp
@@ -34,7 +34,7 @@
#include <sqlite3.h>
-#include "mongo/db/storage/mobile/mobile_global_options.h"
+#include "mongo/db/storage/mobile/mobile_options.h"
#include "mongo/db/storage/mobile/mobile_recovery_unit.h"
#include "mongo/db/storage/mobile/mobile_sqlite_statement.h"
#include "mongo/db/storage/mobile/mobile_util.h"
@@ -159,7 +159,7 @@ void doValidate(OperationContext* opCtx, ValidateResults* results) {
int status;
// By default, the integrity check returns the first 100 errors found.
while ((status = validateStmt.step()) == SQLITE_ROW) {
- std::string errMsg(reinterpret_cast<const char*>(validateStmt.getColText(0)));
+ std::string errMsg(validateStmt.getColText(0));
if (errMsg == "ok") {
// If the first message returned is "ok", the integrity check passed without
@@ -182,7 +182,7 @@ void doValidate(OperationContext* opCtx, ValidateResults* results) {
}
}
-void configureSession(sqlite3* session) {
+void configureSession(sqlite3* session, const MobileOptions& options) {
auto executePragma = [session](auto pragma, auto value) {
SqliteStatement::execQuery(session, "PRAGMA ", pragma, " = ", value, ";");
LOG(MOBILE_LOG_LEVEL_LOW) << "MobileSE session configuration: " << pragma << " = " << value;
@@ -195,7 +195,7 @@ void configureSession(sqlite3* session) {
executePragma("journal_mode"_sd, "WAL"_sd);
// synchronous = NORMAL(1) is recommended with WAL, but we allow it to be overriden
- executePragma("synchronous"_sd, std::to_string(mobileGlobalOptions.mobileDurabilityLevel));
+ executePragma("synchronous"_sd, std::to_string(options.durabilityLevel));
// Set full fsync on OSX (only supported there) to ensure durability
executePragma("fullfsync"_sd, "1"_sd);
@@ -206,11 +206,9 @@ void configureSession(sqlite3* session) {
// Set some additional internal sizes for this session
// Cache size described as KB should be set as negative number
// https://sqlite.org/pragma.html#pragma_cache_size
- executePragma("cache_size"_sd,
- std::to_string(-static_cast<int32_t>(mobileGlobalOptions.mobileCacheSizeKB)));
- executePragma("mmap_size"_sd, std::to_string(mobileGlobalOptions.mobileMmapSizeKB * 1024));
- executePragma("journal_size_limit"_sd,
- std::to_string(mobileGlobalOptions.mobileJournalSizeLimitKB * 1024));
+ executePragma("cache_size"_sd, std::to_string(-static_cast<int32_t>(options.cacheSizeKB)));
+ executePragma("mmap_size"_sd, std::to_string(options.mmapSizeKB * 1024));
+ executePragma("journal_size_limit"_sd, std::to_string(options.journalSizeLimitKB * 1024));
}
} // namespace embedded
diff --git a/src/mongo/db/storage/mobile/mobile_util.h b/src/mongo/db/storage/mobile/mobile_util.h
index d96e32a076c..93ca3fdd51d 100644
--- a/src/mongo/db/storage/mobile/mobile_util.h
+++ b/src/mongo/db/storage/mobile/mobile_util.h
@@ -31,6 +31,7 @@
#include "mongo/base/status.h"
#include "mongo/db/operation_context.h"
+#include "mongo/db/storage/mobile/mobile_options.h"
#include "mongo/db/storage/record_store.h"
#define MOBILE_LOG_LEVEL_LOW 2
@@ -69,7 +70,7 @@ void doValidate(OperationContext* opCtx, ValidateResults* results);
* Sets the SQLite Pragmas that we want (https://www.sqlite.org/pragma.html)
* These should generally improve behavior, performance, and resource usage
*/
-void configureSession(sqlite3* session);
+void configureSession(sqlite3* session, const MobileOptions& options);
} // namespace embedded
} // namespace mongo
diff --git a/src/mongo/embedded/embedded_options.cpp b/src/mongo/embedded/embedded_options.cpp
index 934cb7c076e..8ffe6f93ce6 100644
--- a/src/mongo/embedded/embedded_options.cpp
+++ b/src/mongo/embedded/embedded_options.cpp
@@ -33,7 +33,7 @@
#include "mongo/db/server_options_base.h"
#include "mongo/db/server_options_helpers.h"
-#include "mongo/db/storage/mobile/mobile_global_options_gen.h"
+#include "mongo/db/storage/mobile/mobile_options_gen.h"
#include "mongo/db/storage/storage_options.h"
#include "mongo/embedded/embedded_options_gen.h"
@@ -84,7 +84,7 @@ Status storeOptions(const moe::Environment& params) {
storageGlobalParams.dbpath = params["storage.dbPath"].as<string>();
}
- ret = mobileGlobalOptions.store(params);
+ ret = storeMobileStorageOptionDefinitions(params);
if (!ret.isOK()) {
return ret;
}
@@ -113,6 +113,7 @@ Status storeOptions(const moe::Environment& params) {
void resetOptions() {
storageGlobalParams.reset();
+ mobileGlobalOptions = MobileOptions();
}
std::string storageDBPathDescription() {
diff --git a/src/mongo/embedded/mongo_embedded/SConscript b/src/mongo/embedded/mongo_embedded/SConscript
index fe143671726..42b533801c8 100644
--- a/src/mongo/embedded/mongo_embedded/SConscript
+++ b/src/mongo/embedded/mongo_embedded/SConscript
@@ -101,6 +101,7 @@ if get_option('link-model') != 'dynamic-sdk':
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/db/commands/test_commands_enabled',
'$BUILD_DIR/mongo/db/server_options_core',
+ '$BUILD_DIR/mongo/db/storage/mobile/storage_mobile_core',
'$BUILD_DIR/mongo/rpc/protocol',
'$BUILD_DIR/mongo/unittest/unittest',
'$BUILD_DIR/mongo/util/net/network',
diff --git a/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp b/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp
index 510b467733d..1cde189eb41 100644
--- a/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp
+++ b/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp
@@ -39,6 +39,7 @@
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/json.h"
#include "mongo/db/server_options.h"
+#include "mongo/db/storage/mobile/mobile_options.h"
#include "mongo/embedded/mongo_embedded/mongo_embedded_test_gen.h"
#include "mongo/rpc/message.h"
#include "mongo/rpc/op_msg.h"
@@ -108,6 +109,9 @@ protected:
params.log_callback = nullptr;
params.log_user_data = nullptr;
+ // Set a parameter that lives in mobileGlobalOptions to verify it can be set using YAML.
+ uint32_t vacuumCheckIntervalMinutes = 1;
+
YAML::Emitter yaml;
yaml << YAML::BeginMap;
@@ -115,6 +119,10 @@ protected:
yaml << YAML::Value << YAML::BeginMap;
yaml << YAML::Key << "dbPath";
yaml << YAML::Value << globalTempDir->path();
+ yaml << YAML::Key << "mobile" << YAML::BeginMap;
+ yaml << YAML::Key << "vacuumCheckIntervalMinutes" << YAML::Value
+ << vacuumCheckIntervalMinutes;
+ yaml << YAML::EndMap; // mobile
yaml << YAML::EndMap; // storage
yaml << YAML::EndMap;
@@ -126,6 +134,8 @@ protected:
db = mongo_embedded_v1_instance_create(lib, yaml.c_str(), status);
ASSERT(db != nullptr) << mongo_embedded_v1_status_get_explanation(status);
+ ASSERT(mongo::embedded::mobileGlobalOptions.vacuumCheckIntervalMinutes ==
+ vacuumCheckIntervalMinutes);
}
void tearDown() {
diff --git a/src/mongo/util/options_parser/options_parser.cpp b/src/mongo/util/options_parser/options_parser.cpp
index 70163ff279d..81f0626557b 100644
--- a/src/mongo/util/options_parser/options_parser.cpp
+++ b/src/mongo/util/options_parser/options_parser.cpp
@@ -1674,14 +1674,14 @@ Status OptionsParser::runConfigFile(
const std::string& config,
const std::map<std::string, std::string>& env, // Unused, interface consistent with run()
Environment* configEnvironment) {
- // Add the default values to our resulting environment
- Status ret = addDefaultValues(options, configEnvironment);
+ // Add values from the provided config file
+ Status ret = parseConfigFile(options, config, configEnvironment, ConfigExpand());
if (!ret.isOK()) {
return ret;
}
- // Add values from the provided config file
- ret = parseConfigFile(options, config, configEnvironment, ConfigExpand());
+ // Add the default values to our resulting environment
+ ret = addDefaultValues(options, configEnvironment);
if (!ret.isOK()) {
return ret;
}