diff options
author | Henrik Edin <henrik.edin@mongodb.com> | 2019-04-11 14:32:36 -0400 |
---|---|---|
committer | Henrik Edin <henrik.edin@mongodb.com> | 2019-04-24 11:15:31 -0400 |
commit | 9e4f12393b4a468175f9f0a6cc74dd669bf1fd5e (patch) | |
tree | aa76481db392f7bcd266074a6a29ffa3f0d60097 | |
parent | f202c4c1ba24b9f561e8b11dac5b04fa0eeb4919 (diff) | |
download | mongo-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.
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; } |