summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorJames Wahlin <james@mongodb.com>2018-02-28 16:28:20 -0500
committerJames Wahlin <james@mongodb.com>2018-03-03 08:33:17 -0500
commit13b8e62cd9609f10688a1896cb80efa5d3c82859 (patch)
treeebd8a953e6ab3c9342ae17030ae6d1878ad24ea6 /src/mongo
parenta0617148fdce05cd5705613da1a206c16cd9e2c0 (diff)
downloadmongo-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.cpp4
-rw-r--r--src/mongo/db/commands/get_last_error.cpp5
-rw-r--r--src/mongo/db/service_entry_point_common.cpp20
-rw-r--r--src/mongo/db/service_entry_point_common.h4
-rw-r--r--src/mongo/db/service_entry_point_mongod.cpp7
-rw-r--r--src/mongo/db/write_concern.cpp11
-rw-r--r--src/mongo/db/write_concern.h10
-rw-r--r--src/mongo/db/write_concern_options.cpp2
-rw-r--r--src/mongo/db/write_concern_options.h4
-rw-r--r--src/mongo/s/commands/strategy.cpp4
-rw-r--r--src/mongo/shell/replsettest.js11
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.
*/