diff options
author | James Wahlin <james@mongodb.com> | 2018-02-28 16:28:20 -0500 |
---|---|---|
committer | James Wahlin <james@mongodb.com> | 2018-03-03 08:33:17 -0500 |
commit | 13b8e62cd9609f10688a1896cb80efa5d3c82859 (patch) | |
tree | ebd8a953e6ab3c9342ae17030ae6d1878ad24ea6 /src/mongo | |
parent | a0617148fdce05cd5705613da1a206c16cd9e2c0 (diff) | |
download | mongo-13b8e62cd9609f10688a1896cb80efa5d3c82859.tar.gz |
SERVER-33355 Support readConcern level snapshot reads on secondaries
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/client/embedded/service_entry_point_embedded.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/commands/get_last_error.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 20 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.h | 4 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_mongod.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/write_concern.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/write_concern.h | 10 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.h | 4 | ||||
-rw-r--r-- | src/mongo/s/commands/strategy.cpp | 4 | ||||
-rw-r--r-- | src/mongo/shell/replsettest.js | 11 |
11 files changed, 32 insertions, 50 deletions
diff --git a/src/mongo/client/embedded/service_entry_point_embedded.cpp b/src/mongo/client/embedded/service_entry_point_embedded.cpp index 2eb715adc14..2a0e2ecb3b3 100644 --- a/src/mongo/client/embedded/service_entry_point_embedded.cpp +++ b/src/mongo/client/embedded/service_entry_point_embedded.cpp @@ -93,9 +93,7 @@ public: void waitForReadConcern(OperationContext*, const CommandInvocation*, - const std::string&, - const OpMsgRequest&, - const BSONObj&) const override {} + const OpMsgRequest&) const override {} void waitForWriteConcern(OperationContext* opCtx, const std::string& commandName, diff --git a/src/mongo/db/commands/get_last_error.cpp b/src/mongo/db/commands/get_last_error.cpp index c7673052e8b..900434db737 100644 --- a/src/mongo/db/commands/get_last_error.cpp +++ b/src/mongo/db/commands/get_last_error.cpp @@ -236,10 +236,7 @@ public: // Validate write concern no matter what, this matches 2.4 behavior // if (status.isOK()) { - // Ensure options are valid for this host. Since getLastError doesn't do writes itself, - // treat it as if these are admin database writes, which need to be replicated so we do - // the strictest checks write concern checks. - status = validateWriteConcern(opCtx, writeConcern, NamespaceString::kAdminDb); + status = validateWriteConcern(opCtx, writeConcern); } if (!status.isOK()) { diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index 753add5bb6d..52623b256a9 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -403,21 +403,13 @@ bool runCommandImpl(OperationContext* opCtx, bytesToReserve = 0; #endif - // run expects non-const bsonobj - BSONObj cmd = request.body; - - // run expects const db std::string (can't bind to temporary) - const std::string db = request.getDatabase().toString(); - CommandReplyBuilder crb(replyBuilder->getInPlaceReplyBuilder(bytesToReserve)); - behaviors.waitForReadConcern(opCtx, invocation, db, request, cmd); - if (!invocation->supportsWriteConcern()) { - behaviors.uassertCommandDoesNotSpecifyWriteConcern(cmd); + behaviors.uassertCommandDoesNotSpecifyWriteConcern(request.body); invocation->run(opCtx, &crb); } else { - auto wcResult = uassertStatusOK(extractWriteConcern(opCtx, cmd, db)); + auto wcResult = uassertStatusOK(extractWriteConcern(opCtx, request.body)); auto lastOpBeforeRun = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp(); @@ -647,12 +639,6 @@ void execCommandDatabase(OperationContext* opCtx, "readConcernLevel snapshot requires a txnNumber", opCtx->getTxnNumber()); - // TODO SERVER-33355: Remove once readConcern level snapshot is supported on - // secondaries. - uassert(ErrorCodes::InvalidOptions, - "readConcern level snapshot only supported on primaries", - iAmPrimary); - opCtx->lockState()->setSharedLocksShouldTwoPhaseLock(true); } @@ -693,6 +679,8 @@ void execCommandDatabase(OperationContext* opCtx, rpc::TrackingMetadata::get(opCtx).setIsLogged(true); } + behaviors.waitForReadConcern(opCtx, invocation.get(), request); + sessionTxnState.unstashTransactionResources(); retval = runCommandImpl( opCtx, invocation.get(), request, replyBuilder, startOperationTime, behaviors); diff --git a/src/mongo/db/service_entry_point_common.h b/src/mongo/db/service_entry_point_common.h index a7e7155881a..f90fa0660af 100644 --- a/src/mongo/db/service_entry_point_common.h +++ b/src/mongo/db/service_entry_point_common.h @@ -62,9 +62,7 @@ struct ServiceEntryPointCommon { virtual bool lockedForWriting() const = 0; virtual void waitForReadConcern(OperationContext* opCtx, const CommandInvocation* invocation, - const std::string& db, - const OpMsgRequest& request, - const BSONObj& cmdObj) const = 0; + const OpMsgRequest& request) const = 0; virtual void waitForWriteConcern(OperationContext* opCtx, const std::string& commandName, const repl::OpTime& lastOpBeforeRun, diff --git a/src/mongo/db/service_entry_point_mongod.cpp b/src/mongo/db/service_entry_point_mongod.cpp index a442f40ee27..eb281981054 100644 --- a/src/mongo/db/service_entry_point_mongod.cpp +++ b/src/mongo/db/service_entry_point_mongod.cpp @@ -52,16 +52,15 @@ public: void waitForReadConcern(OperationContext* opCtx, const CommandInvocation* invocation, - const std::string& db, - const OpMsgRequest& request, - const BSONObj& cmdObj) const override { + const OpMsgRequest& request) const override { Status rcStatus = mongo::waitForReadConcern( opCtx, repl::ReadConcernArgs::get(opCtx), invocation->allowsAfterClusterTime()); + if (!rcStatus.isOK()) { if (rcStatus == ErrorCodes::ExceededTimeLimit) { const int debugLevel = serverGlobalParams.clusterRole == ClusterRole::ConfigServer ? 0 : 2; - LOG(debugLevel) << "Command on database " << db + LOG(debugLevel) << "Command on database " << request.getDatabase() << " timed out waiting for read concern to be satisfied. Command: " << redact(ServiceEntryPointCommon::getRedactedCopyForLogging( invocation->definition(), request.body)); diff --git a/src/mongo/db/write_concern.cpp b/src/mongo/db/write_concern.cpp index 0668e4c54ad..2237cdcbdf0 100644 --- a/src/mongo/db/write_concern.cpp +++ b/src/mongo/db/write_concern.cpp @@ -67,12 +67,11 @@ bool commandSpecifiesWriteConcern(const BSONObj& cmdObj) { } StatusWith<WriteConcernOptions> extractWriteConcern(OperationContext* opCtx, - const BSONObj& cmdObj, - const std::string& dbName) { + const BSONObj& cmdObj) { // The default write concern if empty is {w:1}. Specifying {w:0} is/was allowed, but is // interpreted identically to {w:1}. auto wcResult = WriteConcernOptions::extractWCFromCommand( - cmdObj, dbName, repl::ReplicationCoordinator::get(opCtx)->getGetLastErrorDefault()); + cmdObj, repl::ReplicationCoordinator::get(opCtx)->getGetLastErrorDefault()); if (!wcResult.isOK()) { return wcResult.getStatus(); } @@ -88,7 +87,7 @@ StatusWith<WriteConcernOptions> extractWriteConcern(OperationContext* opCtx, WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, Seconds(30)}; } } else { - Status wcStatus = validateWriteConcern(opCtx, writeConcern, dbName); + Status wcStatus = validateWriteConcern(opCtx, writeConcern); if (!wcStatus.isOK()) { return wcStatus; } @@ -97,9 +96,7 @@ StatusWith<WriteConcernOptions> extractWriteConcern(OperationContext* opCtx, return writeConcern; } -Status validateWriteConcern(OperationContext* opCtx, - const WriteConcernOptions& writeConcern, - StringData dbName) { +Status validateWriteConcern(OperationContext* opCtx, const WriteConcernOptions& writeConcern) { if (writeConcern.syncMode == WriteConcernOptions::SyncMode::JOURNAL && !opCtx->getServiceContext()->getGlobalStorageEngine()->isDurable()) { return Status(ErrorCodes::BadValue, diff --git a/src/mongo/db/write_concern.h b/src/mongo/db/write_concern.h index 60c31e6ff26..6a3021c969c 100644 --- a/src/mongo/db/write_concern.h +++ b/src/mongo/db/write_concern.h @@ -52,16 +52,12 @@ bool commandSpecifiesWriteConcern(const BSONObj& cmdObj); * Verifies that the writeConcern is of type Object (BSON type) and * that the resulting writeConcern is valid for this particular host. */ -StatusWith<WriteConcernOptions> extractWriteConcern(OperationContext* opCtx, - const BSONObj& cmdObj, - const std::string& dbName); +StatusWith<WriteConcernOptions> extractWriteConcern(OperationContext* opCtx, const BSONObj& cmdObj); /** - * Verifies that a WriteConcern is valid for this particular host and database. + * Verifies that a WriteConcern is valid for this particular host. */ -Status validateWriteConcern(OperationContext* opCtx, - const WriteConcernOptions& writeConcern, - StringData dbName); +Status validateWriteConcern(OperationContext* opCtx, const WriteConcernOptions& writeConcern); struct WriteConcernResult { WriteConcernResult() { diff --git a/src/mongo/db/write_concern_options.cpp b/src/mongo/db/write_concern_options.cpp index 51b8484a51b..26f5778be37 100644 --- a/src/mongo/db/write_concern_options.cpp +++ b/src/mongo/db/write_concern_options.cpp @@ -151,7 +151,7 @@ Status WriteConcernOptions::parse(const BSONObj& obj) { } StatusWith<WriteConcernOptions> WriteConcernOptions::extractWCFromCommand( - const BSONObj& cmdObj, const std::string& dbName, const WriteConcernOptions& defaultWC) { + const BSONObj& cmdObj, const WriteConcernOptions& defaultWC) { WriteConcernOptions writeConcern = defaultWC; writeConcern.usedDefault = true; if (writeConcern.wNumNodes == 0 && writeConcern.wMode.empty()) { diff --git a/src/mongo/db/write_concern_options.h b/src/mongo/db/write_concern_options.h index d0a3be810e3..a809a2a1de5 100644 --- a/src/mongo/db/write_concern_options.h +++ b/src/mongo/db/write_concern_options.h @@ -69,9 +69,7 @@ public: * Verifies that the writeConcern is of type Object (BSON type). */ static StatusWith<WriteConcernOptions> extractWCFromCommand( - const BSONObj& cmdObj, - const std::string& dbName, - const WriteConcernOptions& defaultWC = WriteConcernOptions()); + const BSONObj& cmdObj, const WriteConcernOptions& defaultWC = WriteConcernOptions()); /** * Return true if the server needs to wait for other secondary nodes to satisfy this diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index f51fdabe7c0..c9e9276764d 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -151,7 +151,7 @@ void execCommandClient(OperationContext* opCtx, appendRequiredFieldsToResponse(opCtx, &body); }); - const auto dbname = request.getDatabase().toString(); + const auto dbname = request.getDatabase(); uassert(ErrorCodes::IllegalOperation, "Can't use 'local' database through mongos", dbname != NamespaceString::kLocalDb); @@ -191,7 +191,7 @@ void execCommandClient(OperationContext* opCtx, } StatusWith<WriteConcernOptions> wcResult = - WriteConcernOptions::extractWCFromCommand(request.body, dbname); + WriteConcernOptions::extractWCFromCommand(request.body); if (!wcResult.isOK()) { auto body = result->getBodyBuilder(); CommandHelpers::appendCommandStatus(body, wcResult.getStatus()); diff --git a/src/mongo/shell/replsettest.js b/src/mongo/shell/replsettest.js index c11edd7c4c8..d848aa761d1 100644 --- a/src/mongo/shell/replsettest.js +++ b/src/mongo/shell/replsettest.js @@ -338,6 +338,17 @@ var ReplSetTest = function(opts) { } /** + * Returns the {readConcern: majority} OpTime for the host. Throws if not available. + */ + this.getReadConcernMajorityOpTimeOrThrow = function(conn) { + const majorityOpTime = _getReadConcernMajorityOpTime(conn); + if (friendlyEqual(majorityOpTime, {ts: Timestamp(0, 0), t: NumberLong(0)})) { + throw new Error("readConcern majority optime not available"); + } + return majorityOpTime; + }; + + /** * Returns the last durable OpTime for the host if running with journaling. * Returns the last applied OpTime otherwise. */ |