diff options
author | Andy Schwerin <schwerin@mongodb.com> | 2018-05-05 10:20:46 -0400 |
---|---|---|
committer | Andy Schwerin <schwerin@mongodb.com> | 2018-05-05 10:20:46 -0400 |
commit | 30994f3bacb6e814ae015d83693c549a3b924ccc (patch) | |
tree | bb959682199469dc6039406721a2f8000cefc10e /src/mongo/db | |
parent | 757c434668e4936d6b2ac62131be7f12aeac25be (diff) | |
download | mongo-30994f3bacb6e814ae015d83693c549a3b924ccc.tar.gz |
SERVER-34798 Move storage initialization behaviors out of ServiceContext subclasses to new library.
Diffstat (limited to 'src/mongo/db')
32 files changed, 566 insertions, 507 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index fdb33e83840..a77d3d7de2e 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -699,6 +699,7 @@ env.Library( '$BUILD_DIR/mongo/db/concurrency/lock_manager', '$BUILD_DIR/mongo/db/ops/write_ops_parsers', '$BUILD_DIR/mongo/db/s/sharding', + '$BUILD_DIR/mongo/db/storage/storage_engine_common', '$BUILD_DIR/mongo/db/storage/storage_options', '$BUILD_DIR/mongo/db/storage/storage_engine_lock_file', '$BUILD_DIR/mongo/db/storage/storage_engine_metadata', diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript index 5ed9e0ad896..47c0b0f662e 100644 --- a/src/mongo/db/catalog/SConscript +++ b/src/mongo/db/catalog/SConscript @@ -289,6 +289,7 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/db/storage/mmap_v1/mmap_v1_options', + '$BUILD_DIR/mongo/db/storage/storage_engine_common', ], ) diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp index 7ee8c22b7ba..b2df4e237b0 100644 --- a/src/mongo/db/catalog/database_impl.cpp +++ b/src/mongo/db/catalog/database_impl.cpp @@ -67,6 +67,7 @@ #include "mongo/db/stats/top.h" #include "mongo/db/storage/recovery_unit.h" #include "mongo/db/storage/storage_engine.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_options.h" #include "mongo/db/system_index.h" #include "mongo/db/views/view_catalog.h" @@ -92,12 +93,12 @@ namespace { MONGO_FP_DECLARE(hangBeforeLoggingCreateCollection); } // namespace -using std::unique_ptr; using std::endl; using std::list; using std::set; using std::string; using std::stringstream; +using std::unique_ptr; using std::vector; void uassertNamespaceNotIndex(StringData ns, StringData caller) { @@ -1019,18 +1020,19 @@ MONGO_REGISTER_SHIM(Database::userCreateNS) } } - status = - validateStorageOptions(collectionOptions.storageEngine, [](const auto& x, const auto& y) { - return x->validateCollectionStorageOptions(y); - }); + status = validateStorageOptions( + opCtx->getServiceContext(), + collectionOptions.storageEngine, + [](const auto& x, const auto& y) { return x->validateCollectionStorageOptions(y); }); if (!status.isOK()) return status; if (auto indexOptions = collectionOptions.indexOptionDefaults["storageEngine"]) { - status = validateStorageOptions(indexOptions.Obj(), [](const auto& x, const auto& y) { - return x->validateIndexStorageOptions(y); - }); + status = validateStorageOptions( + opCtx->getServiceContext(), indexOptions.Obj(), [](const auto& x, const auto& y) { + return x->validateIndexStorageOptions(y); + }); if (!status.isOK()) { return status; diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp index 583721aba4b..ac93349e71e 100644 --- a/src/mongo/db/catalog/index_catalog_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_impl.cpp @@ -64,6 +64,7 @@ #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/server_options.h" #include "mongo/db/service_context.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/util/assert_util.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" @@ -97,9 +98,9 @@ MONGO_REGISTER_SHIM(IndexCatalog::prepareInsertDeleteOptions) return IndexCatalogImpl::prepareInsertDeleteOptions(opCtx, desc, options); } -using std::unique_ptr; using std::endl; using std::string; +using std::unique_ptr; using std::vector; using IndexVersion = IndexDescriptor::IndexVersion; @@ -535,7 +536,7 @@ Status _checkValidFilterExpressions(MatchExpression* expression, int level = 0) << expression->toString()); } } -} +} // namespace Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, const BSONObj& spec) const { const NamespaceString& nss = _collection->ns(); @@ -764,8 +765,8 @@ Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, const BSONObj& spec) "Empty \"storageEngine\" options are invalid. " "Please remove the field or include valid options."); } - Status storageEngineStatus = - validateStorageOptions(storageEngineOptions, [](const auto& x, const auto& y) { + Status storageEngineStatus = validateStorageOptions( + opCtx->getServiceContext(), storageEngineOptions, [](const auto& x, const auto& y) { return x->validateIndexStorageOptions(y); }); if (!storageEngineStatus.isOK()) { diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index 03dd3919180..b95489f622c 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -221,6 +221,7 @@ env.Library( '$BUILD_DIR/mongo/db/query_exec', '$BUILD_DIR/mongo/db/repair_database', '$BUILD_DIR/mongo/db/rw_concern_d', + '$BUILD_DIR/mongo/db/storage/storage_engine_common', '$BUILD_DIR/mongo/db/views/views_mongod', 'core', 'current_op_common', diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp index 0499c35f683..02a279e4653 100644 --- a/src/mongo/db/commands/dbcommands.cpp +++ b/src/mongo/db/commands/dbcommands.cpp @@ -87,6 +87,7 @@ #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/server_parameters.h" #include "mongo/db/stats/storage_stats.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/write_concern.h" #include "mongo/s/stale_exception.h" #include "mongo/scripting/engine.h" @@ -94,6 +95,7 @@ #include "mongo/util/log.h" #include "mongo/util/md5.hpp" #include "mongo/util/scopeguard.h" +#include "mongo/util/version.h" namespace mongo { @@ -764,5 +766,38 @@ public: } cmdDBStats; +class CmdBuildInfo : public BasicCommand { +public: + CmdBuildInfo() : BasicCommand("buildInfo", "buildinfo") {} + + AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { + return AllowedOnSecondary::kAlways; + } + + virtual bool adminOnly() const { + return false; + } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) const {} // No auth required + std::string help() const override { + return "get version #, etc.\n" + "{ buildinfo:1 }"; + } + + bool run(OperationContext* opCtx, + const std::string& dbname, + const BSONObj& jsobj, + BSONObjBuilder& result) { + VersionInfoInterface::instance().appendBuildInfo(&result); + appendStorageEngineList(opCtx->getServiceContext(), &result); + return true; + } + +} cmdBuildInfo; + } // namespace } // namespace mongo diff --git a/src/mongo/db/commands/generic.cpp b/src/mongo/db/commands/generic.cpp index e157ff5a64a..9a4bf94a78b 100644 --- a/src/mongo/db/commands/generic.cpp +++ b/src/mongo/db/commands/generic.cpp @@ -38,7 +38,6 @@ #include "mongo/util/log.h" #include "mongo/util/processinfo.h" #include "mongo/util/ramlog.h" -#include "mongo/util/version.h" #include <sstream> #include <string> @@ -51,39 +50,6 @@ using std::string; using std::stringstream; using std::vector; -class CmdBuildInfo : public BasicCommand { -public: - CmdBuildInfo() : BasicCommand("buildInfo", "buildinfo") {} - - AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { - return AllowedOnSecondary::kAlways; - } - - virtual bool adminOnly() const { - return false; - } - virtual bool supportsWriteConcern(const BSONObj& cmd) const override { - return false; - } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) const {} // No auth required - std::string help() const override { - return "get version #, etc.\n" - "{ buildinfo:1 }"; - } - - bool run(OperationContext* opCtx, - const std::string& dbname, - const BSONObj& jsobj, - BSONObjBuilder& result) { - VersionInfoInterface::instance().appendBuildInfo(&result); - appendStorageEngineList(&result); - return true; - } - -} cmdBuildInfo; - class PingCommand : public BasicCommand { public: PingCommand() : BasicCommand("ping") {} diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 957b55b633b..f9496fe49cf 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -40,7 +40,6 @@ #include <signal.h> #include <string> -#include "mongo/base/checked_cast.h" #include "mongo/base/init.h" #include "mongo/base/initializer.h" #include "mongo/base/status.h" @@ -129,6 +128,7 @@ #include "mongo/db/storage/encryption_hooks.h" #include "mongo/db/storage/mmap_v1/mmap_v1_options.h" #include "mongo/db/storage/storage_engine.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_options.h" #include "mongo/db/system_index.h" #include "mongo/db/ttl.h" @@ -209,7 +209,7 @@ void logStartup(OperationContext* opCtx) { BSONObjBuilder buildinfo(toLog.subobjStart("buildinfo")); VersionInfoInterface::instance().appendBuildInfo(&buildinfo); - appendStorageEngineList(&buildinfo); + appendStorageEngineList(opCtx->getServiceContext(), &buildinfo); buildinfo.doneFast(); BSONObj o = toLog.obj(); @@ -270,7 +270,7 @@ ExitCode _initAndListen(int listenPort) { Client::initThread("initandlisten"); initWireSpec(); - auto serviceContext = checked_cast<ServiceContextMongoD*>(getGlobalServiceContext()); + auto serviceContext = getGlobalServiceContext(); serviceContext->setFastClockSource(FastClockSourceFactory::create(Milliseconds(10))); auto opObserverRegistry = stdx::make_unique<OpObserverRegistry>(); @@ -312,7 +312,7 @@ ExitCode _initAndListen(int listenPort) { logProcessDetails(); - serviceContext->createLockFile(); + createLockFile(serviceContext); serviceContext->setServiceEntryPoint( stdx::make_unique<ServiceEntryPointMongod>(serviceContext)); @@ -328,7 +328,7 @@ ExitCode _initAndListen(int listenPort) { serviceContext->setTransportLayer(std::move(tl)); } - serviceContext->initializeGlobalStorageEngine(); + initializeStorageEngine(serviceContext); #ifdef MONGO_CONFIG_WIREDTIGER_ENABLED if (EncryptionHooks::get(serviceContext)->restartRequired()) { @@ -348,7 +348,7 @@ ExitCode _initAndListen(int listenPort) { } // Warn if field name matches non-active registered storage engine. - if (serviceContext->isRegisteredStorageEngine(e.fieldName())) { + if (isRegisteredStorageEngine(serviceContext, e.fieldName())) { warning() << "Detected configuration for non-active storage engine " << e.fieldName() << " when current storage engine is " << storageGlobalParams.engine; @@ -931,7 +931,7 @@ void shutdownTask() { // Global storage engine may not be started in all cases before we exit if (serviceContext->getStorageEngine()) { - serviceContext->shutdownGlobalStorageEngineCleanly(); + shutdownGlobalStorageEngineCleanly(serviceContext); } // We drop the scope cache because leak sanitizer can't see across the diff --git a/src/mongo/db/service_context.cpp b/src/mongo/db/service_context.cpp index 17193d9462a..92389a0f6a1 100644 --- a/src/mongo/db/service_context.cpp +++ b/src/mongo/db/service_context.cpp @@ -99,43 +99,6 @@ bool isMMAPV1() { return globalStorageEngine->isMmapV1(); } -Status validateStorageOptions( - const BSONObj& storageEngineOptions, - stdx::function<Status(const StorageEngine::Factory* const, const BSONObj&)> validateFunc) { - BSONObjIterator storageIt(storageEngineOptions); - while (storageIt.more()) { - BSONElement storageElement = storageIt.next(); - StringData storageEngineName = storageElement.fieldNameStringData(); - if (storageElement.type() != mongo::Object) { - return Status(ErrorCodes::BadValue, - str::stream() << "'storageEngine." << storageElement.fieldNameStringData() - << "' has to be an embedded document."); - } - - std::unique_ptr<StorageFactoriesIterator> sfi( - getGlobalServiceContext()->makeStorageFactoriesIterator()); - invariant(sfi); - bool found = false; - while (sfi->more()) { - const StorageEngine::Factory* const& factory = sfi->next(); - if (storageEngineName != factory->getCanonicalName()) { - continue; - } - Status status = validateFunc(factory, storageElement.Obj()); - if (!status.isOK()) { - return status; - } - found = true; - } - if (!found) { - return Status(ErrorCodes::InvalidOptions, - str::stream() << storageEngineName - << " is not a registered storage engine for this server"); - } - } - return Status::OK(); -} - ServiceContext::ServiceContext() : _tickSource(stdx::make_unique<SystemTickSource>()), _fastClockSource(stdx::make_unique<SystemClockSource>()), @@ -300,29 +263,6 @@ Client* ServiceContext::LockedClientsCursor::next() { return result; } -BSONArray storageEngineList() { - if (!hasGlobalServiceContext()) - return BSONArray(); - - std::unique_ptr<StorageFactoriesIterator> sfi( - getGlobalServiceContext()->makeStorageFactoriesIterator()); - - if (!sfi) - return BSONArray(); - - BSONArrayBuilder engineArrayBuilder; - - while (sfi->more()) { - engineArrayBuilder.append(sfi->next()->getCanonicalName()); - } - - return engineArrayBuilder.arr(); -} - -void appendStorageEngineList(BSONObjBuilder* result) { - result->append("storageEngines", storageEngineList()); -} - void ServiceContext::setKillAllOperations() { stdx::lock_guard<stdx::mutex> clientLock(_mutex); diff --git a/src/mongo/db/service_context.h b/src/mongo/db/service_context.h index 1caa0fd0f05..73b272470e6 100644 --- a/src/mongo/db/service_context.h +++ b/src/mongo/db/service_context.h @@ -82,19 +82,6 @@ protected: virtual ~KillOpListenerInterface() = default; }; -class StorageFactoriesIterator { - MONGO_DISALLOW_COPYING(StorageFactoriesIterator); - -public: - virtual ~StorageFactoriesIterator() = default; - - virtual bool more() const = 0; - virtual const StorageEngine::Factory* next() = 0; - -protected: - StorageFactoriesIterator() = default; -}; - /** * Class representing the context of a service, such as a MongoD database service or * a MongoS routing service. @@ -239,34 +226,6 @@ public: // /** - * Register a storage engine. Called from a MONGO_INIT that depends on initializiation of - * the global environment. - * Ownership of 'factory' is transferred to global environment upon registration. - */ - virtual void registerStorageEngine(const std::string& name, - const StorageEngine::Factory* factory) = 0; - - /** - * Returns true if "name" refers to a registered storage engine. - */ - virtual bool isRegisteredStorageEngine(const std::string& name) = 0; - - /** - * Produce an iterator over all registered storage engine factories. - * Caller owns the returned object and is responsible for deleting when finished. - * - * Never returns nullptr. - */ - virtual StorageFactoriesIterator* makeStorageFactoriesIterator() = 0; - - virtual void initializeGlobalStorageEngine() = 0; - - /** - * Shuts down storage engine cleanly and releases any locks on mongod.lock. - */ - virtual void shutdownGlobalStorageEngineCleanly() = 0; - - /** * Sets the storage engine for this instance. May be called up to once per instance. */ void setStorageEngine(std::unique_ptr<StorageEngine> engine); @@ -574,29 +533,4 @@ bool supportsDocLocking(); */ bool isMMAPV1(); -/* - * Extracts the storageEngine bson from the CollectionOptions provided. Loops through each - * provided storageEngine and asks the matching registered storage engine if the - * collection/index options are valid. Returns an error if the collection/index options are - * invalid. - * If no matching registered storage engine is found, return an error. - * Validation function 'func' must be either: - * - &StorageEngine::Factory::validateCollectionStorageOptions; or - * - &StorageEngine::Factory::validateIndexStorageOptions - */ -Status validateStorageOptions( - const BSONObj& storageEngineOptions, - stdx::function<Status(const StorageEngine::Factory* const, const BSONObj&)> validateFunc); - -/* - * Returns a BSONArray containing the names of available storage engines, or an empty - * array if there is no global ServiceContext - */ -BSONArray storageEngineList(); - -/* - * Appends a the list of available storage engines to a BSONObjBuilder for reporting purposes. - */ -void appendStorageEngineList(BSONObjBuilder* result); - } // namespace mongo diff --git a/src/mongo/db/service_context_d.cpp b/src/mongo/db/service_context_d.cpp index ca500ce6abe..b4595b97c98 100644 --- a/src/mongo/db/service_context_d.cpp +++ b/src/mongo/db/service_context_d.cpp @@ -43,7 +43,6 @@ #include "mongo/db/storage/storage_engine_lock_file.h" #include "mongo/db/storage/storage_engine_metadata.h" #include "mongo/db/storage/storage_options.h" -#include "mongo/db/unclean_shutdown.h" #include "mongo/stdx/memory.h" #include "mongo/util/log.h" #include "mongo/util/map_util.h" @@ -70,213 +69,6 @@ ServiceContextMongoD::ServiceContextMongoD() = default; ServiceContextMongoD::~ServiceContextMongoD() = default; -void ServiceContextMongoD::createLockFile() { - try { - _lockFile.reset(new StorageEngineLockFile(storageGlobalParams.dbpath)); - } catch (const std::exception& ex) { - uassert(28596, - str::stream() << "Unable to determine status of lock file in the data directory " - << storageGlobalParams.dbpath - << ": " - << ex.what(), - false); - } - bool wasUnclean = _lockFile->createdByUncleanShutdown(); - auto openStatus = _lockFile->open(); - if (storageGlobalParams.readOnly && openStatus == ErrorCodes::IllegalOperation) { - _lockFile.reset(); - } else { - uassertStatusOK(openStatus); - } - - if (wasUnclean) { - if (storageGlobalParams.readOnly) { - severe() << "Attempted to open dbpath in readOnly mode, but the server was " - "previously not shut down cleanly."; - fassertFailedNoTrace(34416); - } - warning() << "Detected unclean shutdown - " << _lockFile->getFilespec() << " is not empty."; - startingAfterUncleanShutdown(this) = true; - } -} - -void ServiceContextMongoD::initializeGlobalStorageEngine() { - // This should be set once. - invariant(!getStorageEngine()); - - // We should have a _lockFile or be in read-only mode. Confusingly, we can still have a lockFile - // if we are in read-only mode. This can happen if the server is started in read-only mode on a - // writable dbpath. - invariant(_lockFile || storageGlobalParams.readOnly); - - const std::string dbpath = storageGlobalParams.dbpath; - if (auto existingStorageEngine = StorageEngineMetadata::getStorageEngineForPath(dbpath)) { - if (*existingStorageEngine == "mmapv1" || - (storageGlobalParams.engineSetByUser && storageGlobalParams.engine == "mmapv1")) { - log() << startupWarningsLog; - log() << "** WARNING: Support for MMAPV1 storage engine has been deprecated and will be" - << startupWarningsLog; - log() << "** removed in version 4.2. Please plan to migrate to the wiredTiger" - << startupWarningsLog; - log() << "** storage engine." << startupWarningsLog; - log() << "** See http://dochub.mongodb.org/core/deprecated-mmapv1"; - log() << startupWarningsLog; - } - - if (storageGlobalParams.engineSetByUser) { - // Verify that the name of the user-supplied storage engine matches the contents of - // the metadata file. - const StorageEngine::Factory* factory = - mapFindWithDefault(_storageFactories, - storageGlobalParams.engine, - static_cast<const StorageEngine::Factory*>(nullptr)); - - if (factory) { - uassert(28662, - str::stream() << "Cannot start server. Detected data files in " << dbpath - << " created by" - << " the '" - << *existingStorageEngine - << "' storage engine, but the" - << " specified storage engine was '" - << factory->getCanonicalName() - << "'.", - factory->getCanonicalName() == *existingStorageEngine); - } - } else { - // Otherwise set the active storage engine as the contents of the metadata file. - log() << "Detected data files in " << dbpath << " created by the '" - << *existingStorageEngine << "' storage engine, so setting the active" - << " storage engine to '" << *existingStorageEngine << "'."; - storageGlobalParams.engine = *existingStorageEngine; - } - } else if (!storageGlobalParams.engineSetByUser) { - // Ensure the default storage engine is available with this build of mongod. - uassert(28663, - str::stream() - << "Cannot start server. The default storage engine '" - << storageGlobalParams.engine - << "' is not available with this build of mongod. Please specify a different" - << " storage engine explicitly, e.g. --storageEngine=mmapv1.", - isRegisteredStorageEngine(storageGlobalParams.engine)); - } else if (storageGlobalParams.engineSetByUser && storageGlobalParams.engine == "mmapv1") { - log() << startupWarningsLog; - log() << "** WARNING: You have explicitly specified 'MMAPV1' storage engine in your" - << startupWarningsLog; - log() << "** config file or as a command line option. Support for the MMAPV1" - << startupWarningsLog; - log() << "** storage engine has been deprecated and will be removed in" - << startupWarningsLog; - log() << "** version 4.2. See http://dochub.mongodb.org/core/deprecated-mmapv1"; - log() << startupWarningsLog; - } - - const std::string repairpath = storageGlobalParams.repairpath; - uassert(40311, - str::stream() << "Cannot start server. The command line option '--repairpath'" - << " is only supported by the mmapv1 storage engine", - repairpath.empty() || repairpath == dbpath || storageGlobalParams.engine == "mmapv1"); - - const StorageEngine::Factory* factory = _storageFactories[storageGlobalParams.engine]; - - uassert(18656, - str::stream() << "Cannot start server with an unknown storage engine: " - << storageGlobalParams.engine, - factory); - - if (storageGlobalParams.readOnly) { - uassert(34368, - str::stream() - << "Server was started in read-only mode, but the configured storage engine, " - << storageGlobalParams.engine - << ", does not support read-only operation", - factory->supportsReadOnly()); - } - - std::unique_ptr<StorageEngineMetadata> metadata = StorageEngineMetadata::forPath(dbpath); - - if (storageGlobalParams.readOnly) { - uassert(34415, - "Server was started in read-only mode, but the storage metadata file was not" - " found.", - metadata.get()); - } - - // Validate options in metadata against current startup options. - if (metadata.get()) { - uassertStatusOK(factory->validateMetadata(*metadata, storageGlobalParams)); - } - - ScopeGuard guard = MakeGuard([&] { - if (_lockFile) { - _lockFile->close(); - } - }); - - setStorageEngine( - std::unique_ptr<StorageEngine>(factory->create(storageGlobalParams, _lockFile.get()))); - getStorageEngine()->finishInit(); - - if (_lockFile) { - uassertStatusOK(_lockFile->writePid()); - } - - // Write a new metadata file if it is not present. - if (!metadata.get()) { - invariant(!storageGlobalParams.readOnly); - metadata.reset(new StorageEngineMetadata(storageGlobalParams.dbpath)); - metadata->setStorageEngine(factory->getCanonicalName().toString()); - metadata->setStorageEngineOptions(factory->createMetadataOptions(storageGlobalParams)); - uassertStatusOK(metadata->write()); - } - - guard.Dismiss(); - - _supportsDocLocking = getStorageEngine()->supportsDocLocking(); -} - -void ServiceContextMongoD::shutdownGlobalStorageEngineCleanly() { - invariant(getStorageEngine()); - getStorageEngine()->cleanShutdown(); - if (_lockFile) { - _lockFile->clearPidAndUnlock(); - } -} - -void ServiceContextMongoD::registerStorageEngine(const std::string& name, - const StorageEngine::Factory* factory) { - // No double-registering. - invariant(0 == _storageFactories.count(name)); - - // Some sanity checks: the factory must exist, - invariant(factory); - - // and all factories should be added before we pick a storage engine. - invariant(!getStorageEngine()); - - _storageFactories[name] = factory; -} - -bool ServiceContextMongoD::isRegisteredStorageEngine(const std::string& name) { - return _storageFactories.count(name); -} - -StorageFactoriesIterator* ServiceContextMongoD::makeStorageFactoriesIterator() { - return new StorageFactoriesIteratorMongoD(_storageFactories.begin(), _storageFactories.end()); -} - -StorageFactoriesIteratorMongoD::StorageFactoriesIteratorMongoD(const FactoryMapIterator& begin, - const FactoryMapIterator& end) - : _curr(begin), _end(end) {} - -bool StorageFactoriesIteratorMongoD::more() const { - return _curr != _end; -} - -const StorageEngine::Factory* StorageFactoriesIteratorMongoD::next() { - return _curr++->second; -} - std::unique_ptr<OperationContext> ServiceContextMongoD::_newOpCtx(Client* client, unsigned opId) { invariant(&cc() == client); auto opCtx = stdx::make_unique<OperationContext>(client, opId); diff --git a/src/mongo/db/service_context_d.h b/src/mongo/db/service_context_d.h index 9074cb07e51..dfab1926390 100644 --- a/src/mongo/db/service_context_d.h +++ b/src/mongo/db/service_context_d.h @@ -28,8 +28,6 @@ #pragma once -#include <map> - #include "mongo/db/service_context.h" namespace mongo { @@ -39,46 +37,12 @@ class StorageEngineLockFile; class ServiceContextMongoD final : public ServiceContext { public: - using FactoryMap = std::map<std::string, const StorageEngine::Factory*>; - ServiceContextMongoD(); ~ServiceContextMongoD(); - void createLockFile(); - - void initializeGlobalStorageEngine() override; - - void shutdownGlobalStorageEngineCleanly() override; - - void registerStorageEngine(const std::string& name, - const StorageEngine::Factory* factory) override; - - bool isRegisteredStorageEngine(const std::string& name) override; - - StorageFactoriesIterator* makeStorageFactoriesIterator() override; - private: std::unique_ptr<OperationContext> _newOpCtx(Client* client, unsigned opId) override; - - std::unique_ptr<StorageEngineLockFile> _lockFile; - - // All possible storage engines are registered here through MONGO_INIT. - FactoryMap _storageFactories; -}; - -class StorageFactoriesIteratorMongoD final : public StorageFactoriesIterator { -public: - typedef ServiceContextMongoD::FactoryMap::const_iterator FactoryMapIterator; - - StorageFactoriesIteratorMongoD(const FactoryMapIterator& begin, const FactoryMapIterator& end); - - bool more() const override; - const StorageEngine::Factory* next() override; - -private: - FactoryMapIterator _curr; - FactoryMapIterator _end; }; } // namespace mongo diff --git a/src/mongo/db/service_context_d_test_fixture.cpp b/src/mongo/db/service_context_d_test_fixture.cpp index 1b51ac9e966..db0b6bbee7b 100644 --- a/src/mongo/db/service_context_d_test_fixture.cpp +++ b/src/mongo/db/service_context_d_test_fixture.cpp @@ -42,6 +42,7 @@ #include "mongo/db/op_observer_noop.h" #include "mongo/db/op_observer_registry.h" #include "mongo/db/service_context_d.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_options.h" #include "mongo/stdx/memory.h" #include "mongo/unittest/temp_dir.h" @@ -67,8 +68,8 @@ void ServiceContextMongoDTest::setUp() { storageGlobalParams.engine = "ephemeralForTest"; storageGlobalParams.engineSetByUser = true; - checked_cast<ServiceContextMongoD*>(serviceContext)->createLockFile(); - serviceContext->initializeGlobalStorageEngine(); + createLockFile(serviceContext); + initializeStorageEngine(serviceContext); serviceContext->setOpObserver(stdx::make_unique<OpObserverNoop>()); } diff --git a/src/mongo/db/service_context_noop.cpp b/src/mongo/db/service_context_noop.cpp index 59d0e696fb6..e2cc4b7d41e 100644 --- a/src/mongo/db/service_context_noop.cpp +++ b/src/mongo/db/service_context_noop.cpp @@ -35,33 +35,6 @@ namespace mongo { -void ServiceContextNoop::initializeGlobalStorageEngine() {} - -void ServiceContextNoop::shutdownGlobalStorageEngineCleanly() {} - -void ServiceContextNoop::registerStorageEngine(const std::string& name, - const StorageEngine::Factory* factory) { - // Takes ownership of 'factory' and deletes it because we don't need it. - delete factory; -} - -bool ServiceContextNoop::isRegisteredStorageEngine(const std::string& name) { - return false; -} - -StorageFactoriesIterator* ServiceContextNoop::makeStorageFactoriesIterator() { - class EmptySFI : public StorageFactoriesIterator { - public: - virtual bool more() const { - return false; - } - virtual const StorageEngine::Factory* next() { - MONGO_UNREACHABLE; - } - }; - return new EmptySFI(); -} - std::unique_ptr<OperationContext> ServiceContextNoop::_newOpCtx(Client* client, unsigned opId) { return stdx::make_unique<OperationContextNoop>(client, opId); } diff --git a/src/mongo/db/service_context_noop.h b/src/mongo/db/service_context_noop.h index 3bf4829cffe..77e8119accd 100644 --- a/src/mongo/db/service_context_noop.h +++ b/src/mongo/db/service_context_noop.h @@ -33,18 +33,6 @@ namespace mongo { class ServiceContextNoop : public ServiceContext { -public: - void initializeGlobalStorageEngine() override; - - void shutdownGlobalStorageEngineCleanly() override; - - void registerStorageEngine(const std::string& name, - const StorageEngine::Factory* factory) override; - - bool isRegisteredStorageEngine(const std::string& name) override; - - StorageFactoriesIterator* makeStorageFactoriesIterator() override; - private: std::unique_ptr<OperationContext> _newOpCtx(Client* client, unsigned opId) override; }; diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript index 95a8a6bd74e..398007c45fa 100644 --- a/src/mongo/db/storage/SConscript +++ b/src/mongo/db/storage/SConscript @@ -181,14 +181,30 @@ env.Library( env.Library( target='storage_engine_lock_file', source=[ + 'storage_engine_lock_file.cpp', 'storage_engine_lock_file_${TARGET_OS_FAMILY}.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/base', - ] + '$BUILD_DIR/mongo/db/service_context', + ], ) env.Library( + target='storage_engine_common', + source=[ + 'storage_engine_init.cpp' + ], + LIBDEPS=[ + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/service_context', + 'storage_engine_lock_file', + 'storage_engine_metadata', + 'storage_options', + ], +) +env.Library( target="storage_init_d", source=[ "storage_init.cpp", diff --git a/src/mongo/db/storage/devnull/SConscript b/src/mongo/db/storage/devnull/SConscript index 564ec403783..1d960936a60 100644 --- a/src/mongo/db/storage/devnull/SConscript +++ b/src/mongo/db/storage/devnull/SConscript @@ -24,4 +24,7 @@ env.Library( 'storage_devnull_core', '$BUILD_DIR/mongo/db/storage/kv/kv_engine', ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/storage/storage_engine_common', + ], ) diff --git a/src/mongo/db/storage/devnull/devnull_init.cpp b/src/mongo/db/storage/devnull/devnull_init.cpp index c3df87cda3e..68e80902d29 100644 --- a/src/mongo/db/storage/devnull/devnull_init.cpp +++ b/src/mongo/db/storage/devnull/devnull_init.cpp @@ -34,6 +34,7 @@ #include "mongo/db/service_context_d.h" #include "mongo/db/storage/devnull/devnull_kv_engine.h" #include "mongo/db/storage/kv/kv_storage_engine.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_options.h" namespace mongo { @@ -66,7 +67,8 @@ public: MONGO_INITIALIZER_WITH_PREREQUISITES(DevNullEngineInit, ("ServiceContext")) (InitializerContext* context) { - getGlobalServiceContext()->registerStorageEngine("devnull", new DevNullStorageEngineFactory()); + registerStorageEngine(getGlobalServiceContext(), + std::make_unique<DevNullStorageEngineFactory>()); return Status::OK(); } -} +} // namespace mongo diff --git a/src/mongo/db/storage/ephemeral_for_test/SConscript b/src/mongo/db/storage/ephemeral_for_test/SConscript index fed440462a4..13e16746684 100644 --- a/src/mongo/db/storage/ephemeral_for_test/SConscript +++ b/src/mongo/db/storage/ephemeral_for_test/SConscript @@ -44,6 +44,9 @@ env.Library( 'storage_ephemeral_for_test_core', '$BUILD_DIR/mongo/db/storage/kv/kv_engine' ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/storage/storage_engine_common', + ], ) env.CppUnitTest( diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_init.cpp b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_init.cpp index 8922776255f..a4de16caf63 100644 --- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_init.cpp +++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_init.cpp @@ -33,6 +33,7 @@ #include "mongo/db/service_context.h" #include "mongo/db/storage/ephemeral_for_test/ephemeral_for_test_engine.h" #include "mongo/db/storage/kv/kv_storage_engine.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_options.h" namespace mongo { @@ -72,8 +73,7 @@ public: MONGO_INITIALIZER_WITH_PREREQUISITES(EphemeralForTestEngineInit, ("ServiceContext")) (InitializerContext* context) { - getGlobalServiceContext()->registerStorageEngine("ephemeralForTest", - new EphemeralForTestFactory()); + registerStorageEngine(getGlobalServiceContext(), std::make_unique<EphemeralForTestFactory>()); return Status::OK(); } diff --git a/src/mongo/db/storage/mmap_v1/SConscript b/src/mongo/db/storage/mmap_v1/SConscript index 4d7bf2b5f87..c879be79a24 100644 --- a/src/mongo/db/storage/mmap_v1/SConscript +++ b/src/mongo/db/storage/mmap_v1/SConscript @@ -80,6 +80,7 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/commands/server_status', '$BUILD_DIR/mongo/db/commands/test_commands_enabled', + '$BUILD_DIR/mongo/db/storage/storage_engine_common', ], ) diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_init.cpp b/src/mongo/db/storage/mmap_v1/mmap_v1_init.cpp index 3bce0e22fba..42ba6cb864c 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_v1_init.cpp +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_init.cpp @@ -33,6 +33,7 @@ #include "mongo/bson/bsonobjbuilder.h" #include "mongo/db/service_context.h" #include "mongo/db/storage/mmap_v1/mmap_v1_engine.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_engine_metadata.h" #include "mongo/db/storage/storage_options.h" @@ -78,7 +79,7 @@ public: MONGO_INITIALIZER_WITH_PREREQUISITES(MMAPV1EngineInit, ("ServiceContext")) (InitializerContext* context) { - getGlobalServiceContext()->registerStorageEngine("mmapv1", new MMAPV1Factory()); + registerStorageEngine(getGlobalServiceContext(), std::make_unique<MMAPV1Factory>()); return Status::OK(); } diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_init_test.cpp b/src/mongo/db/storage/mmap_v1/mmap_v1_init_test.cpp index 19ec450e1ac..dff7166e77a 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_v1_init_test.cpp +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_init_test.cpp @@ -31,6 +31,7 @@ #include "mongo/db/json.h" #include "mongo/db/service_context.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_engine_metadata.h" #include "mongo/db/storage/storage_options.h" #include "mongo/unittest/unittest.h" @@ -45,24 +46,13 @@ private: virtual void setUp() { ServiceContext* globalEnv = getGlobalServiceContext(); ASSERT_TRUE(globalEnv); - ASSERT_TRUE(getGlobalServiceContext()->isRegisteredStorageEngine("mmapv1")); - std::unique_ptr<StorageFactoriesIterator> sfi( - getGlobalServiceContext()->makeStorageFactoriesIterator()); - ASSERT_TRUE(sfi); - bool found = false; - while (sfi->more()) { - const StorageEngine::Factory* currentFactory = sfi->next(); - if (currentFactory->getCanonicalName() == "mmapv1") { - found = true; - factory = currentFactory; - break; - } - } - ASSERT_TRUE(found); + ASSERT_TRUE(isRegisteredStorageEngine(globalEnv, "mmapv1")); + factory = getFactoryForStorageEngine(globalEnv, "mmapv1"); + ASSERT_TRUE(factory); } virtual void tearDown() { - factory = NULL; + factory = nullptr; } protected: diff --git a/src/mongo/db/storage/mobile/SConscript b/src/mongo/db/storage/mobile/SConscript index 98ed0435228..1f481c23d0a 100644 --- a/src/mongo/db/storage/mobile/SConscript +++ b/src/mongo/db/storage/mobile/SConscript @@ -50,8 +50,11 @@ env.Library( LIBDEPS=[ 'storage_mobile_core', '$BUILD_DIR/mongo/db/storage/kv/kv_engine' - ] - ) + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/storage/storage_engine_common', + ], +) ''' env.CppUnitTest( target='storage_mobile_index_test', diff --git a/src/mongo/db/storage/mobile/mobile_init.cpp b/src/mongo/db/storage/mobile/mobile_init.cpp index edadad22887..41a5dc83159 100644 --- a/src/mongo/db/storage/mobile/mobile_init.cpp +++ b/src/mongo/db/storage/mobile/mobile_init.cpp @@ -34,6 +34,7 @@ #include "mongo/db/service_context.h" #include "mongo/db/storage/kv/kv_storage_engine.h" #include "mongo/db/storage/mobile/mobile_kv_engine.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_options.h" namespace mongo { @@ -74,7 +75,7 @@ GlobalInitializerRegisterer mobileKVEngineInitializer( "MobileKVEngineInit", {"ServiceContext"}, [](InitializerContext* context) { - getGlobalServiceContext()->registerStorageEngine("mobile", new MobileFactory()); + registerStorageEngine(getGlobalServiceContext(), std::make_unique<MobileFactory>()); return Status::OK(); }, [](DeinitializerContext* const) { return Status::OK(); }); diff --git a/src/mongo/db/storage/storage_engine_init.cpp b/src/mongo/db/storage/storage_engine_init.cpp new file mode 100644 index 00000000000..24a0b790972 --- /dev/null +++ b/src/mongo/db/storage/storage_engine_init.cpp @@ -0,0 +1,308 @@ +/** + * Copyright (C) 2018 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage + +#include "mongo/platform/basic.h" + +#include "mongo/db/storage/storage_engine_init.h" + +#include <map> + +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/db/storage/storage_engine_lock_file.h" +#include "mongo/db/storage/storage_engine_metadata.h" +#include "mongo/db/storage/storage_options.h" +#include "mongo/db/unclean_shutdown.h" +#include "mongo/util/assert_util.h" +#include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" + +namespace mongo { + +extern bool _supportsDocLocking; + +void initializeStorageEngine(ServiceContext* service) { + // This should be set once. + invariant(!service->getStorageEngine()); + + // We should have a lock file or be in read-only mode. Confusingly, we can still have a lockFile + // if we are in read-only mode. This can happen if the server is started in read-only mode on a + // writable dbpath. + invariant(StorageEngineLockFile::get(service) || storageGlobalParams.readOnly); + + const std::string dbpath = storageGlobalParams.dbpath; + if (auto existingStorageEngine = StorageEngineMetadata::getStorageEngineForPath(dbpath)) { + if (*existingStorageEngine == "mmapv1" || + (storageGlobalParams.engineSetByUser && storageGlobalParams.engine == "mmapv1")) { + log() << startupWarningsLog; + log() << "** WARNING: Support for MMAPV1 storage engine has been deprecated and will be" + << startupWarningsLog; + log() << "** removed in version 4.2. Please plan to migrate to the wiredTiger" + << startupWarningsLog; + log() << "** storage engine." << startupWarningsLog; + log() << "** See http://dochub.mongodb.org/core/deprecated-mmapv1"; + log() << startupWarningsLog; + } + + if (storageGlobalParams.engineSetByUser) { + // Verify that the name of the user-supplied storage engine matches the contents of + // the metadata file. + const StorageEngine::Factory* factory = + getFactoryForStorageEngine(service, storageGlobalParams.engine); + if (factory) { + uassert(28662, + str::stream() << "Cannot start server. Detected data files in " << dbpath + << " created by" + << " the '" + << *existingStorageEngine + << "' storage engine, but the" + << " specified storage engine was '" + << factory->getCanonicalName() + << "'.", + factory->getCanonicalName() == *existingStorageEngine); + } + } else { + // Otherwise set the active storage engine as the contents of the metadata file. + log() << "Detected data files in " << dbpath << " created by the '" + << *existingStorageEngine << "' storage engine, so setting the active" + << " storage engine to '" << *existingStorageEngine << "'."; + storageGlobalParams.engine = *existingStorageEngine; + } + } else if (!storageGlobalParams.engineSetByUser) { + // Ensure the default storage engine is available with this build of mongod. + uassert(28663, + str::stream() + << "Cannot start server. The default storage engine '" + << storageGlobalParams.engine + << "' is not available with this build of mongod. Please specify a different" + << " storage engine explicitly, e.g. --storageEngine=mmapv1.", + isRegisteredStorageEngine(service, storageGlobalParams.engine)); + } else if (storageGlobalParams.engineSetByUser && storageGlobalParams.engine == "mmapv1") { + log() << startupWarningsLog; + log() << "** WARNING: You have explicitly specified 'MMAPV1' storage engine in your" + << startupWarningsLog; + log() << "** config file or as a command line option. Support for the MMAPV1" + << startupWarningsLog; + log() << "** storage engine has been deprecated and will be removed in" + << startupWarningsLog; + log() << "** version 4.2. See http://dochub.mongodb.org/core/deprecated-mmapv1"; + log() << startupWarningsLog; + } + + const std::string repairpath = storageGlobalParams.repairpath; + uassert(40311, + str::stream() << "Cannot start server. The command line option '--repairpath'" + << " is only supported by the mmapv1 storage engine", + repairpath.empty() || repairpath == dbpath || storageGlobalParams.engine == "mmapv1"); + + const StorageEngine::Factory* factory = + getFactoryForStorageEngine(service, storageGlobalParams.engine); + + uassert(18656, + str::stream() << "Cannot start server with an unknown storage engine: " + << storageGlobalParams.engine, + factory); + + if (storageGlobalParams.readOnly) { + uassert(34368, + str::stream() + << "Server was started in read-only mode, but the configured storage engine, " + << storageGlobalParams.engine + << ", does not support read-only operation", + factory->supportsReadOnly()); + } + + std::unique_ptr<StorageEngineMetadata> metadata = StorageEngineMetadata::forPath(dbpath); + + if (storageGlobalParams.readOnly) { + uassert(34415, + "Server was started in read-only mode, but the storage metadata file was not" + " found.", + metadata.get()); + } + + // Validate options in metadata against current startup options. + if (metadata.get()) { + uassertStatusOK(factory->validateMetadata(*metadata, storageGlobalParams)); + } + + ScopeGuard guard = MakeGuard([&] { + auto& lockFile = StorageEngineLockFile::get(service); + if (lockFile) { + lockFile->close(); + } + }); + + auto& lockFile = StorageEngineLockFile::get(service); + service->setStorageEngine(std::unique_ptr<StorageEngine>( + factory->create(storageGlobalParams, lockFile ? &*lockFile : nullptr))); + service->getStorageEngine()->finishInit(); + + if (lockFile) { + uassertStatusOK(lockFile->writePid()); + } + + // Write a new metadata file if it is not present. + if (!metadata.get()) { + invariant(!storageGlobalParams.readOnly); + metadata.reset(new StorageEngineMetadata(storageGlobalParams.dbpath)); + metadata->setStorageEngine(factory->getCanonicalName().toString()); + metadata->setStorageEngineOptions(factory->createMetadataOptions(storageGlobalParams)); + uassertStatusOK(metadata->write()); + } + + guard.Dismiss(); + + _supportsDocLocking = service->getStorageEngine()->supportsDocLocking(); +} + +void shutdownGlobalStorageEngineCleanly(ServiceContext* service) { + invariant(service->getStorageEngine()); + service->getStorageEngine()->cleanShutdown(); + auto& lockFile = StorageEngineLockFile::get(service); + if (lockFile) { + lockFile->clearPidAndUnlock(); + } +} + +void createLockFile(ServiceContext* service) { + auto& lockFile = StorageEngineLockFile::get(service); + try { + lockFile.emplace(storageGlobalParams.dbpath); + } catch (const std::exception& ex) { + uassert(28596, + str::stream() << "Unable to determine status of lock file in the data directory " + << storageGlobalParams.dbpath + << ": " + << ex.what(), + false); + } + const bool wasUnclean = lockFile->createdByUncleanShutdown(); + const auto openStatus = lockFile->open(); + if (storageGlobalParams.readOnly && openStatus == ErrorCodes::IllegalOperation) { + lockFile = boost::none; + } else { + uassertStatusOK(openStatus); + } + + if (wasUnclean) { + if (storageGlobalParams.readOnly) { + severe() << "Attempted to open dbpath in readOnly mode, but the server was " + "previously not shut down cleanly."; + fassertFailedNoTrace(34416); + } + warning() << "Detected unclean shutdown - " << lockFile->getFilespec() << " is not empty."; + startingAfterUncleanShutdown(service) = true; + } +} + +namespace { + +using FactoryMap = std::map<std::string, std::unique_ptr<StorageEngine::Factory>>; + +auto storageFactories = ServiceContext::declareDecoration<FactoryMap>(); + +} // namespace + +void registerStorageEngine(ServiceContext* service, + std::unique_ptr<StorageEngine::Factory> factory) { + // No double-registering. + invariant(!getFactoryForStorageEngine(service, factory->getCanonicalName())); + + // Some sanity checks: the factory must exist, + invariant(factory); + + // and all factories should be added before we pick a storage engine. + invariant(!service->getStorageEngine()); + + auto name = factory->getCanonicalName().toString(); + storageFactories(service).emplace(name, std::move(factory)); +} + +bool isRegisteredStorageEngine(ServiceContext* service, StringData name) { + return getFactoryForStorageEngine(service, name); +} + +StorageEngine::Factory* getFactoryForStorageEngine(ServiceContext* service, StringData name) { + const auto result = storageFactories(service).find(name.toString()); + if (result == storageFactories(service).end()) { + return nullptr; + } + return result->second.get(); +} + +Status validateStorageOptions( + ServiceContext* service, + const BSONObj& storageEngineOptions, + stdx::function<Status(const StorageEngine::Factory* const, const BSONObj&)> validateFunc) { + + BSONObjIterator storageIt(storageEngineOptions); + while (storageIt.more()) { + BSONElement storageElement = storageIt.next(); + StringData storageEngineName = storageElement.fieldNameStringData(); + if (storageElement.type() != mongo::Object) { + return Status(ErrorCodes::BadValue, + str::stream() << "'storageEngine." << storageElement.fieldNameStringData() + << "' has to be an embedded document."); + } + + if (auto factory = getFactoryForStorageEngine(service, storageEngineName)) { + Status status = validateFunc(factory, storageElement.Obj()); + if (!status.isOK()) { + return status; + } + } else { + return Status(ErrorCodes::InvalidOptions, + str::stream() << storageEngineName + << " is not a registered storage engine for this server"); + } + } + return Status::OK(); +} + +namespace { +BSONArray storageEngineList(ServiceContext* service) { + if (!service) + return BSONArray(); + + BSONArrayBuilder engineArrayBuilder; + + for (const auto& nameAndFactory : storageFactories(service)) { + engineArrayBuilder.append(nameAndFactory.first); + } + + return engineArrayBuilder.arr(); +} +} // namespace + +void appendStorageEngineList(ServiceContext* service, BSONObjBuilder* result) { + result->append("storageEngines", storageEngineList(service)); +} + +} // namespace mongo diff --git a/src/mongo/db/storage/storage_engine_init.h b/src/mongo/db/storage/storage_engine_init.h new file mode 100644 index 00000000000..161cc4558b9 --- /dev/null +++ b/src/mongo/db/storage/storage_engine_init.h @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2018 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include <memory> + +#include "mongo/base/string_data.h" +#include "mongo/db/service_context.h" +#include "mongo/db/storage/storage_engine.h" + +namespace mongo { + +/** + * Initializes the storage engine on "service". + */ +void initializeStorageEngine(ServiceContext* service); + +/** + * Shuts down storage engine cleanly and releases any locks on mongod.lock. + */ +void shutdownGlobalStorageEngineCleanly(ServiceContext* service); + +/** + * Creates the lock file used to prevent concurrent processes from accessing the data files, + * as appropriate. + */ +void createLockFile(ServiceContext* service); + +/** + * Registers a storage engine onto the given "service". + */ +void registerStorageEngine(ServiceContext* service, + std::unique_ptr<StorageEngine::Factory> factory); + +/** + * Returns true if "name" refers to a registered storage engine. + */ +bool isRegisteredStorageEngine(ServiceContext* service, StringData name); + +/** + * Returns an unowned pointer to the factory for the named storage engine, or nullptr. + * + * NOTE: Exposed only for use in legacy testing scenarios. + */ +StorageEngine::Factory* getFactoryForStorageEngine(ServiceContext* context, StringData name); + +/* + * Extracts the storageEngine bson from the CollectionOptions provided. Loops through each + * provided storageEngine and asks the matching registered storage engine if the + * collection/index options are valid. Returns an error if the collection/index options are + * invalid. + * If no matching registered storage engine is found, return an error. + * Validation function 'func' must be either: + * - &StorageEngine::Factory::validateCollectionStorageOptions; or + * - &StorageEngine::Factory::validateIndexStorageOptions + */ +Status validateStorageOptions( + ServiceContext* service, + const BSONObj& storageEngineOptions, + stdx::function<Status(const StorageEngine::Factory* const, const BSONObj&)> validateFunc); + +/* + * Appends a the list of available storage engines to a BSONObjBuilder for reporting purposes. + */ +void appendStorageEngineList(ServiceContext* service, BSONObjBuilder* result); + +} // namespace mongo diff --git a/src/mongo/db/storage/storage_engine_lock_file.cpp b/src/mongo/db/storage/storage_engine_lock_file.cpp new file mode 100644 index 00000000000..56b0bde2442 --- /dev/null +++ b/src/mongo/db/storage/storage_engine_lock_file.cpp @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2018 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage + +#include "mongo/platform/basic.h" + +#include "mongo/db/storage/storage_engine_lock_file.h" + +namespace mongo { +namespace { + +auto getLockFile = ServiceContext::declareDecoration<boost::optional<StorageEngineLockFile>>(); + +} // namespace + +boost::optional<StorageEngineLockFile>& StorageEngineLockFile::get(ServiceContext* service) { + return getLockFile(service); +} + +} // namespace mongo diff --git a/src/mongo/db/storage/storage_engine_lock_file.h b/src/mongo/db/storage/storage_engine_lock_file.h index 24e2359396e..b65364bc65e 100644 --- a/src/mongo/db/storage/storage_engine_lock_file.h +++ b/src/mongo/db/storage/storage_engine_lock_file.h @@ -28,11 +28,13 @@ #pragma once +#include <boost/optional.hpp> #include <memory> #include <string> #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" +#include "mongo/db/service_context.h" namespace mongo { @@ -40,6 +42,8 @@ class StorageEngineLockFile { MONGO_DISALLOW_COPYING(StorageEngineLockFile); public: + static boost::optional<StorageEngineLockFile>& get(ServiceContext* service); + /** * Checks existing lock file, if present, to see if it contains data from a previous * unclean shutdown. A clean shutdown should have produced a zero length lock file. diff --git a/src/mongo/db/storage/wiredtiger/SConscript b/src/mongo/db/storage/wiredtiger/SConscript index 826ff227314..142a108aaa7 100644 --- a/src/mongo/db/storage/wiredtiger/SConscript +++ b/src/mongo/db/storage/wiredtiger/SConscript @@ -102,6 +102,7 @@ if wiredtiger: LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/commands/server_status', '$BUILD_DIR/mongo/db/concurrency/lock_manager', + '$BUILD_DIR/mongo/db/storage/storage_engine_common', ], ) diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_init.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_init.cpp index 26ce1200659..9408642b210 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_init.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_init.cpp @@ -41,6 +41,7 @@ #include "mongo/db/service_context.h" #include "mongo/db/service_context_d.h" #include "mongo/db/storage/kv/kv_storage_engine.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_engine_lock_file.h" #include "mongo/db/storage/storage_engine_metadata.h" #include "mongo/db/storage/storage_options.h" @@ -182,9 +183,7 @@ public: MONGO_INITIALIZER_WITH_PREREQUISITES(WiredTigerEngineInit, ("ServiceContext")) (InitializerContext* context) { - getGlobalServiceContext()->registerStorageEngine(kWiredTigerEngineName, - new WiredTigerFactory()); - + registerStorageEngine(getGlobalServiceContext(), std::make_unique<WiredTigerFactory>()); return Status::OK(); } } diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_init_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_init_test.cpp index a2830efebfd..164d52013dd 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_init_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_init_test.cpp @@ -31,6 +31,7 @@ #include "mongo/db/json.h" #include "mongo/db/service_context.h" +#include "mongo/db/storage/storage_engine_init.h" #include "mongo/db/storage/storage_engine_metadata.h" #include "mongo/db/storage/storage_options.h" #include "mongo/db/storage/wiredtiger/wiredtiger_global_options.h" @@ -47,27 +48,15 @@ private: virtual void setUp() { ServiceContext* globalEnv = getGlobalServiceContext(); ASSERT_TRUE(globalEnv); - ASSERT_TRUE(getGlobalServiceContext()->isRegisteredStorageEngine(kWiredTigerEngineName)); - std::unique_ptr<StorageFactoriesIterator> sfi( - getGlobalServiceContext()->makeStorageFactoriesIterator()); - ASSERT_TRUE(sfi); - bool found = false; - while (sfi->more()) { - const StorageEngine::Factory* currentFactory = sfi->next(); - if (currentFactory->getCanonicalName() == kWiredTigerEngineName) { - found = true; - factory = currentFactory; - break; - } - found = true; - } - ASSERT_TRUE(found); + ASSERT_TRUE(isRegisteredStorageEngine(globalEnv, kWiredTigerEngineName)); + factory = getFactoryForStorageEngine(globalEnv, kWiredTigerEngineName); + ASSERT_TRUE(factory); _oldOptions = wiredTigerGlobalOptions; } virtual void tearDown() { wiredTigerGlobalOptions = _oldOptions; - factory = NULL; + factory = nullptr; } WiredTigerGlobalOptions _oldOptions; |