summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLingzhi Deng <lingzhi.deng@mongodb.com>2020-06-15 13:52:06 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-06-15 21:17:51 +0000
commit1261db4a593fe06c5139fc0c9877c406d76b5bb4 (patch)
treec01de597d19ddc99496c8c5557ca04e9ae4d8cf3
parent97631cdd42360d4a2d07941ae989a0047707914e (diff)
downloadmongo-1261db4a593fe06c5139fc0c9877c406d76b5bb4.tar.gz
SERVER-48557: Omit cursor.atClusterTime field from read replies in transactions
-rw-r--r--jstests/noPassthrough/readConcern_snapshot.js33
-rw-r--r--jstests/noPassthrough/readConcern_snapshot_mongos.js38
-rw-r--r--src/mongo/db/commands/distinct.cpp3
-rw-r--r--src/mongo/db/commands/find_cmd.cpp4
-rw-r--r--src/mongo/db/commands/getmore_cmd.cpp4
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp4
-rw-r--r--src/mongo/s/commands/cluster_distinct_cmd.cpp3
-rw-r--r--src/mongo/s/commands/cluster_find_cmd.cpp5
-rw-r--r--src/mongo/s/query/cluster_aggregation_planner.cpp4
-rw-r--r--src/mongo/s/query/cluster_find.cpp4
10 files changed, 76 insertions, 26 deletions
diff --git a/jstests/noPassthrough/readConcern_snapshot.js b/jstests/noPassthrough/readConcern_snapshot.js
index 2e2a7779415..a4d335af6b4 100644
--- a/jstests/noPassthrough/readConcern_snapshot.js
+++ b/jstests/noPassthrough/readConcern_snapshot.js
@@ -1,4 +1,5 @@
-// Test parsing of readConcern level 'snapshot'.
+// Test parsing of readConcern level 'snapshot' and the presence of the 'atClusterTime' field in
+// snapshot cursor responses.
// @tags: [requires_majority_read_concern, requires_replication, uses_transactions]
(function() {
"use strict";
@@ -113,17 +114,25 @@ sessionDb = session.getDatabase(dbName);
// readConcern 'snapshot' is supported by find in a transaction.
session.startTransaction({readConcern: {level: "snapshot"}, writeConcern: {w: "majority"}});
-assert.commandWorked(sessionDb.runCommand({find: collName}));
+let res = assert.commandWorked(sessionDb.runCommand({find: collName, batchSize: 0}));
+assert(!res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
+
+// readConcern 'snapshot' is supported by getMore in a transaction.
+res = assert.commandWorked(sessionDb.runCommand({getMore: res.cursor.id, collection: collName}));
+assert(!res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is supported by aggregate in a transaction.
-assert.commandWorked(sessionDb.runCommand({aggregate: collName, pipeline: [], cursor: {}}));
+res = assert.commandWorked(sessionDb.runCommand({aggregate: collName, pipeline: [], cursor: {}}));
+assert(!res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is supported by distinct in a transaction.
-assert.commandWorked(sessionDb.runCommand({distinct: collName, key: "x"}));
+res = assert.commandWorked(sessionDb.runCommand({distinct: collName, key: "x"}));
+assert(!res.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is supported by geoSearch in a transaction.
-assert.commandWorked(
+res = assert.commandWorked(
sessionDb.runCommand({geoSearch: collName, near: [0, 0], maxDistance: 1, search: {a: 1}}));
+assert(!res.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is not supported by non-CRUD commands in a transaction.
assert.commandFailedWithCode(sessionDb.runCommand({dropIndexes: collName, index: "a_1"}),
@@ -136,15 +145,23 @@ const snapshotReadConcern = {
level: "snapshot"
};
// readConcern 'snapshot' is supported by find outside of transactions.
-assert.commandWorked(testDB.runCommand({find: collName, readConcern: snapshotReadConcern}));
+res = assert.commandWorked(
+ testDB.runCommand({find: collName, batchSize: 0, readConcern: snapshotReadConcern}));
+assert(res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
+
+// readConcern 'snapshot' is supported by getMore outside of a transaction.
+res = assert.commandWorked(testDB.runCommand({getMore: res.cursor.id, collection: collName}));
+assert(res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is supported by aggregate outside of transactions.
-assert.commandWorked(testDB.runCommand(
+res = assert.commandWorked(testDB.runCommand(
{aggregate: collName, pipeline: [], cursor: {}, readConcern: snapshotReadConcern}));
+assert(res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is supported by distinct outside of transactions.
-assert.commandWorked(
+res = assert.commandWorked(
testDB.runCommand({distinct: collName, key: "x", readConcern: snapshotReadConcern}));
+assert(res.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is not supported by geoSearch outside of transactions.
assert.commandFailedWithCode(testDB.runCommand({
diff --git a/jstests/noPassthrough/readConcern_snapshot_mongos.js b/jstests/noPassthrough/readConcern_snapshot_mongos.js
index c1be784e73a..ee61cb86e8d 100644
--- a/jstests/noPassthrough/readConcern_snapshot_mongos.js
+++ b/jstests/noPassthrough/readConcern_snapshot_mongos.js
@@ -1,4 +1,5 @@
-// Test parsing of readConcern level 'snapshot' on mongos.
+// Test parsing of readConcern level 'snapshot' and the presence of the 'atClusterTime' field in
+// snapshot cursor responses on mongos.
// @tags: [requires_replication,requires_sharding, uses_transactions, uses_atclustertime]
(function() {
"use strict";
@@ -9,7 +10,13 @@ load("jstests/sharding/libs/sharded_transactions_helpers.js");
// success.
function expectSuccessInTxnThenAbort(session, sessionConn, cmdObj) {
session.startTransaction();
- assert.commandWorked(sessionConn.runCommand(cmdObj));
+ let res = assert.commandWorked(sessionConn.runCommand(cmdObj));
+ // Transaction reads should not have 'atClusterTime' field in responses.
+ if (res.hasOwnProperty("cursor")) {
+ assert(!res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
+ } else {
+ assert(!res.hasOwnProperty("atClusterTime"), tojson(res));
+ }
assert.commandWorked(session.abortTransaction_forTesting());
}
@@ -65,11 +72,14 @@ expectSuccessInTxnThenAbort(session, sessionDb, {
readConcern: {level: "snapshot"},
});
-// readConcern 'snapshot' is supported by find on mongos in a transaction.
-expectSuccessInTxnThenAbort(session, sessionDb, {
- find: collName,
- readConcern: {level: "snapshot"},
-});
+// readConcern 'snapshot' is supported by find and getMore on mongos in a transaction.
+session.startTransaction();
+let res = assert.commandWorked(
+ sessionDb.runCommand({find: collName, batchSize: 0, readConcern: {level: "snapshot"}}));
+assert(!res.cursor.hasOwnProperty("atClusterTime"));
+res = assert.commandWorked(sessionDb.runCommand({getMore: res.cursor.id, collection: collName}));
+assert(!res.cursor.hasOwnProperty("atClusterTime"));
+assert.commandWorked(session.abortTransaction_forTesting());
// readConcern 'snapshot' is supported by distinct on mongos in a transaction.
expectSuccessInTxnThenAbort(session, sessionDb, {
@@ -101,15 +111,23 @@ const snapshotReadConcern = {
level: "snapshot"
};
// readConcern 'snapshot' is supported by find outside of transactions on mongos.
-assert.commandWorked(testDB.runCommand({find: collName, readConcern: snapshotReadConcern}));
+res = assert.commandWorked(
+ testDB.runCommand({find: collName, batchSize: 0, readConcern: snapshotReadConcern}));
+assert(res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
+
+// readConcern 'snapshot' is supported by getMore outside of transactions on mongos.
+res = assert.commandWorked(testDB.runCommand({getMore: res.cursor.id, collection: collName}));
+assert(res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is supported by aggregate outside of transactions on mongos.
-assert.commandWorked(testDB.runCommand(
+res = assert.commandWorked(testDB.runCommand(
{aggregate: collName, pipeline: [], cursor: {}, readConcern: snapshotReadConcern}));
+assert(res.cursor.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is supported by distinct outside of transactions on mongos.
-assert.commandWorked(
+res = assert.commandWorked(
testDB.runCommand({distinct: collName, key: "x", readConcern: snapshotReadConcern}));
+assert(res.hasOwnProperty("atClusterTime"), tojson(res));
// readConcern 'snapshot' is not supported by count on mongos.
assert.commandFailedWithCode(testDB.runCommand({count: collName, readConcern: snapshotReadConcern}),
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index 3ef8faf0bfa..d43ab866f4f 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -315,7 +315,8 @@ public:
}
valueListBuilder.doneFast();
- if (repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime()) {
+ if (!opCtx->inMultiDocumentTransaction() &&
+ repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime()) {
result.append("atClusterTime"_sd,
repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime()->asTimestamp());
}
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index 8eeb6f8d45b..33585aa5596 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -526,7 +526,9 @@ public:
// Stream query results, adding them to a BSONArray as we go.
CursorResponseBuilder::Options options;
options.isInitialResponse = true;
- options.atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ if (!opCtx->inMultiDocumentTransaction()) {
+ options.atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ }
CursorResponseBuilder firstBatch(result, options);
Document doc;
PlanExecutor::ExecState state = PlanExecutor::ADVANCED;
diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp
index 6ed119df16d..70ef0d0f706 100644
--- a/src/mongo/db/commands/getmore_cmd.cpp
+++ b/src/mongo/db/commands/getmore_cmd.cpp
@@ -573,7 +573,9 @@ public:
CursorId respondWithId = 0;
CursorResponseBuilder::Options options;
- options.atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ if (!opCtx->inMultiDocumentTransaction()) {
+ options.atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ }
CursorResponseBuilder nextBatch(reply, options);
BSONObj obj;
std::uint64_t numResults = 0;
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index 330d74db102..38e9b713853 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -153,7 +153,9 @@ bool handleCursorCommand(OperationContext* opCtx,
CursorResponseBuilder::Options options;
options.isInitialResponse = true;
- options.atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ if (!opCtx->inMultiDocumentTransaction()) {
+ options.atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ }
CursorResponseBuilder responseBuilder(result, options);
auto curOp = CurOp::get(opCtx);
diff --git a/src/mongo/s/commands/cluster_distinct_cmd.cpp b/src/mongo/s/commands/cluster_distinct_cmd.cpp
index 2f98bfbf195..7359c79e910 100644
--- a/src/mongo/s/commands/cluster_distinct_cmd.cpp
+++ b/src/mongo/s/commands/cluster_distinct_cmd.cpp
@@ -259,7 +259,8 @@ public:
result.appendArray("values", b.obj());
// If mongos selected atClusterTime or received it from client, transmit it back.
- if (repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime()) {
+ if (!opCtx->inMultiDocumentTransaction() &&
+ repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime()) {
result.append("atClusterTime"_sd,
repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime()->asTimestamp());
}
diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp
index 7ebab369391..51d6272400b 100644
--- a/src/mongo/s/commands/cluster_find_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_cmd.cpp
@@ -230,7 +230,10 @@ public:
// Build the response document.
CursorResponseBuilder::Options options;
options.isInitialResponse = true;
- options.atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ if (!opCtx->inMultiDocumentTransaction()) {
+ options.atClusterTime =
+ repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ }
CursorResponseBuilder firstBatch(result, options);
for (const auto& obj : batch) {
firstBatch.append(obj);
diff --git a/src/mongo/s/query/cluster_aggregation_planner.cpp b/src/mongo/s/query/cluster_aggregation_planner.cpp
index 5b7ea00286f..79aff1295c6 100644
--- a/src/mongo/s/query/cluster_aggregation_planner.cpp
+++ b/src/mongo/s/query/cluster_aggregation_planner.cpp
@@ -269,7 +269,9 @@ BSONObj establishMergingMongosCursor(OperationContext* opCtx,
rpc::OpMsgReplyBuilder replyBuilder;
CursorResponseBuilder::Options options;
options.isInitialResponse = true;
- options.atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ if (!opCtx->inMultiDocumentTransaction()) {
+ options.atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ }
CursorResponseBuilder responseBuilder(&replyBuilder, options);
bool stashedResult = false;
diff --git a/src/mongo/s/query/cluster_find.cpp b/src/mongo/s/query/cluster_find.cpp
index cb6c4f394ef..6f374202b8f 100644
--- a/src/mongo/s/query/cluster_find.cpp
+++ b/src/mongo/s/query/cluster_find.cpp
@@ -848,7 +848,9 @@ StatusWith<CursorResponse> ClusterFind::runGetMore(OperationContext* opCtx,
"waitBeforeUnpinningOrDeletingCursorAfterGetMoreBatch");
}
- auto atClusterTime = repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime();
+ auto atClusterTime = !opCtx->inMultiDocumentTransaction()
+ ? repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime()
+ : boost::none;
return CursorResponse(request.nss,
idToReturn,
std::move(batch),