summaryrefslogtreecommitdiff
path: root/jstests
diff options
context:
space:
mode:
authorWilliam Schultz <william.schultz@mongodb.com>2018-12-21 16:33:36 -0500
committerWilliam Schultz <william.schultz@mongodb.com>2018-12-21 16:43:58 -0500
commit435200964a1fc9de566e74bd579e99c308551670 (patch)
treef6d4e2723f794200b900c914818c8d9645df50f0 /jstests
parentf15556ae1ba4f78d2823d54e38d7025c7e9ca4fb (diff)
downloadmongo-435200964a1fc9de566e74bd579e99c308551670.tar.gz
SERVER-37560 Allow change streams to work with speculative majority reads
This patch allows change stream queries to use speculative majority reads so that they can be used even when enableMajorityReadConcern:false. Change stream aggregation commands are allowed to use speculative majority reads as well as 'find' commands that specify a special flag. This commit also enables all change streams tests and suites on the enableMajorityReadConcern:false Evergreen variant. No optimizations for speculative majority change streams are included in this commit.
Diffstat (limited to 'jstests')
-rw-r--r--jstests/replsets/change_stream_speculative_majority.js84
-rw-r--r--jstests/replsets/change_stream_speculative_majority_latest_oplog_timestamp.js89
-rw-r--r--jstests/replsets/change_stream_speculative_majority_rollback.js104
-rw-r--r--jstests/replsets/speculative_majority_find.js155
-rw-r--r--jstests/replsets/speculative_majority_supported_commands.js74
-rw-r--r--jstests/sharding/change_stream_lookup_single_shard_cluster.js8
-rw-r--r--jstests/sharding/change_stream_read_preference.js8
-rw-r--r--jstests/sharding/change_stream_update_lookup_collation.js8
-rw-r--r--jstests/sharding/change_streams_primary_shard_unaware.js8
9 files changed, 538 insertions, 0 deletions
diff --git a/jstests/replsets/change_stream_speculative_majority.js b/jstests/replsets/change_stream_speculative_majority.js
new file mode 100644
index 00000000000..abd08675bee
--- /dev/null
+++ b/jstests/replsets/change_stream_speculative_majority.js
@@ -0,0 +1,84 @@
+/**
+ * Test basic, steady-state replication change stream functionality with speculative majority reads.
+ */
+(function() {
+ "use strict";
+
+ load("jstests/libs/write_concern_util.js"); // for [stop|restart]ServerReplication.
+
+ const name = "change_stream_speculative_majority";
+ const replTest = new ReplSetTest({
+ name: name,
+ nodes: [{}, {rsConfig: {priority: 0}}],
+ nodeOptions: {enableMajorityReadConcern: 'false'}
+ });
+ replTest.startSet();
+ replTest.initiate();
+
+ const dbName = name;
+ const collName = "coll";
+
+ let primary = replTest.getPrimary();
+ let secondary = replTest.getSecondary();
+ let primaryDB = primary.getDB(dbName);
+ let primaryColl = primaryDB[collName];
+
+ // Open a change stream.
+ let res = primaryDB.runCommand(
+ {aggregate: collName, pipeline: [{$changeStream: {}}], cursor: {}, maxTimeMS: 5000});
+ assert.commandWorked(res);
+ let cursorId = res.cursor.id;
+
+ // Insert a document on primary and let it majority commit.
+ assert.commandWorked(primaryColl.insert({_id: 1}, {writeConcern: {w: "majority"}}));
+
+ // Receive the first change event.
+ res = primary.getDB(dbName).runCommand({getMore: cursorId, collection: collName});
+ let changes = res.cursor.nextBatch;
+ assert.eq(changes.length, 1);
+ assert.eq(changes[0]["fullDocument"], {_id: 1});
+ assert.eq(changes[0]["operationType"], "insert");
+
+ // Save the resume token.
+ let resumeToken = changes[0]["_id"];
+
+ // This query should time out waiting for new results and return an empty batch.
+ res = primary.getDB(dbName).runCommand(
+ {getMore: cursorId, collection: collName, maxTimeMS: 5000});
+ assert.eq(res.cursor.nextBatch, []);
+
+ // Pause replication on the secondary so that writes won't majority commit.
+ stopServerReplication(secondary);
+
+ // Do a new write on primary.
+ assert.commandWorked(primaryColl.insert({_id: 2}));
+
+ // The change stream query should time out waiting for the new result to majority commit.
+ res = primary.getDB(dbName).runCommand(
+ {getMore: cursorId, collection: collName, maxTimeMS: 5000});
+ assert.commandFailedWithCode(res, ErrorCodes.MaxTimeMSExpired);
+
+ // An aggregate trying to resume a stream that includes the change should also time out.
+ res = primaryDB.runCommand({
+ aggregate: collName,
+ pipeline: [{$changeStream: {resumeAfter: resumeToken}}],
+ cursor: {},
+ maxTimeMS: 5000
+ });
+ assert.commandFailedWithCode(res, ErrorCodes.MaxTimeMSExpired);
+
+ // Resume the stream after restarting replication. We should now be able to see the new event.
+ restartServerReplication(secondary);
+ replTest.awaitReplication();
+
+ // Re-open the stream, and receive the new event.
+ res = primaryDB.runCommand(
+ {aggregate: collName, pipeline: [{$changeStream: {resumeAfter: resumeToken}}], cursor: {}});
+ assert.commandWorked(res);
+ changes = res.cursor.firstBatch;
+ assert.eq(changes.length, 1);
+ assert.eq(changes[0]["fullDocument"], {_id: 2});
+ assert.eq(changes[0]["operationType"], "insert");
+
+ replTest.stopSet();
+})(); \ No newline at end of file
diff --git a/jstests/replsets/change_stream_speculative_majority_latest_oplog_timestamp.js b/jstests/replsets/change_stream_speculative_majority_latest_oplog_timestamp.js
new file mode 100644
index 00000000000..01359b1caa9
--- /dev/null
+++ b/jstests/replsets/change_stream_speculative_majority_latest_oplog_timestamp.js
@@ -0,0 +1,89 @@
+/**
+ * Test that change streams using speculative majority wait for the latest observed oplog timestamp
+ * to majority commit.
+ *
+ * If a change stream query returns a batch containing oplog entries no newer than timestamp T, the
+ * server may still report the latest majority committed oplog timestamp that it observed while
+ * scanning the oplog, which may be greater than T. A mongoS will use this timestamp as a guarantee
+ * that no new change events will occur at a lesser timestamp. This guarantee is only valid if the
+ * timestamp is actually majority committed, so we need to make sure that guarantee holds, even when
+ * using speculative majority.
+ */
+(function() {
+ "use strict";
+
+ load("jstests/libs/write_concern_util.js"); // for [stop|restart]ServerReplication.
+
+ const name = "change_stream_speculative_majority_latest_oplog_timestamp";
+ const replTest = new ReplSetTest({
+ name: name,
+ nodes: [{}, {rsConfig: {priority: 0}}],
+ nodeOptions: {enableMajorityReadConcern: 'false'}
+ });
+ replTest.startSet();
+ replTest.initiate();
+
+ const dbName = name;
+ const collName = "coll";
+ const otherCollName = "coll_other";
+
+ const primary = replTest.getPrimary();
+ const secondary = replTest.getSecondary();
+
+ const primaryDB = primary.getDB(dbName);
+ const primaryColl = primaryDB[collName];
+
+ assert.commandWorked(primaryColl.insert({_id: 0}, {writeConcern: {w: "majority"}}));
+
+ let res = primaryDB.runCommand({
+ aggregate: collName,
+ pipeline: [{$changeStream: {}}],
+ cursor: {},
+ maxTimeMS: 5000,
+ needsMerge: true,
+ fromMongos: true
+ });
+
+ assert.commandWorked(res);
+ let cursorId = res.cursor.id;
+
+ // Insert a document on primary and let it majority commit.
+ assert.commandWorked(primaryColl.insert({_id: 1}, {writeConcern: {w: "majority"}}));
+
+ // Receive the first change event.
+ res = primary.getDB(dbName).runCommand({getMore: cursorId, collection: collName});
+ let changes = res.cursor.nextBatch;
+ assert.eq(changes.length, 1);
+ assert.eq(changes[0]["fullDocument"], {_id: 1});
+ assert.eq(changes[0]["operationType"], "insert");
+
+ // Pause replication on the secondary so that writes won't majority commit.
+ jsTestLog("Stopping replication to secondary.");
+ stopServerReplication(secondary);
+
+ // Do a write on a collection that we are not watching changes for.
+ let otherWriteRes = primaryDB.runCommand({insert: otherCollName, documents: [{_id: 1}]});
+ let otherWriteOpTime = otherWriteRes.operationTime;
+
+ // Replication to the secondary is paused, so the write to 'otherCollName' cannot majority
+ // commit. A change stream getMore is expected to return the "latest oplog timestamp" which it
+ // scanned and this timestamp must be majority committed. So, this getMore should time out
+ // waiting for the previous write to majority commit, even though it's on a collection that is
+ // not being watched.
+ res = primary.getDB(dbName).runCommand(
+ {getMore: cursorId, collection: collName, maxTimeMS: 5000});
+ assert.commandFailedWithCode(res, ErrorCodes.MaxTimeMSExpired);
+
+ jsTestLog("Restarting replication to secondary.");
+ restartServerReplication(secondary);
+ replTest.awaitReplication();
+
+ // Now that writes can replicate again, the previous operation should have majority committed,
+ // making it safe to return as the latest oplog timestamp.
+ res = primary.getDB(dbName).runCommand(
+ {getMore: cursorId, collection: collName, maxTimeMS: 5000});
+ assert.eq(res.cursor.nextBatch, []);
+ assert.eq(otherWriteOpTime, res.$_internalLatestOplogTimestamp);
+
+ replTest.stopSet();
+})(); \ No newline at end of file
diff --git a/jstests/replsets/change_stream_speculative_majority_rollback.js b/jstests/replsets/change_stream_speculative_majority_rollback.js
new file mode 100644
index 00000000000..104269ff8a3
--- /dev/null
+++ b/jstests/replsets/change_stream_speculative_majority_rollback.js
@@ -0,0 +1,104 @@
+/**
+ * Test change stream behavior with speculative majority reads in the face of replication rollback.
+ */
+(function() {
+ 'use strict';
+
+ load("jstests/replsets/libs/rollback_test.js"); // for RollbackTest.
+
+ // Disable implicit sessions so it's easy to run commands from different threads.
+ TestData.disableImplicitSessions = true;
+
+ const name = "change_stream_speculative_majority_rollback";
+ const dbName = name;
+ const collName = "coll";
+
+ // Set up a replica set for use in RollbackTest. We disable majority reads on all nodes so we
+ // will utilize speculative majority reads for change streams.
+ const replTest = new ReplSetTest(
+ {name, nodes: 3, useBridge: true, nodeOptions: {enableMajorityReadConcern: "false"}});
+ replTest.startSet();
+ const nodes = replTest.nodeList();
+ replTest.initiate({
+ _id: name,
+ members: [
+ {_id: 0, host: nodes[0]},
+ {_id: 1, host: nodes[1]},
+ {_id: 2, host: nodes[2], arbiterOnly: true}
+ ]
+ });
+
+ const rollbackTest = new RollbackTest(name, replTest);
+ const primary = rollbackTest.getPrimary();
+ const primaryDB = primary.getDB(dbName);
+ let coll = primaryDB[collName];
+
+ // Create a collection.
+ assert.commandWorked(coll.insert({_id: 0}, {writeConcern: {w: "majority"}}));
+
+ // Open a change stream on the initial primary.
+ let res =
+ primaryDB.runCommand({aggregate: collName, pipeline: [{$changeStream: {}}], cursor: {}});
+ assert.commandWorked(res);
+ let cursorId = res.cursor.id;
+
+ // Receive an initial change event and save the resume token.
+ assert.commandWorked(coll.insert({_id: 1}, {writeConcern: {w: "majority"}}));
+ res = primaryDB.runCommand({getMore: cursorId, collection: collName});
+ let changes = res.cursor.nextBatch;
+ assert.eq(changes.length, 1);
+ assert.eq(changes[0]["fullDocument"], {_id: 1});
+ assert.eq(changes[0]["operationType"], "insert");
+ let resumeToken = changes[0]["_id"];
+
+ let rollbackNode = rollbackTest.transitionToRollbackOperations();
+ assert.eq(rollbackNode, primary);
+
+ // Insert a few items that will be rolled back.
+ assert.commandWorked(coll.insert({_id: 2}));
+ assert.commandWorked(coll.insert({_id: 3}));
+ assert.commandWorked(coll.insert({_id: 4}));
+
+ let getChangeEvent = new ScopedThread(function(host, cursorId, dbName, collName) {
+ jsTestLog("Trying to receive change event from divergent primary.");
+ const nodeDB = new Mongo(host).getDB(dbName);
+ try {
+ return nodeDB.runCommand({getMore: eval(cursorId), collection: collName});
+ } catch (e) {
+ return isNetworkError(e);
+ }
+ }, rollbackNode.host, tojson(cursorId), dbName, collName);
+ getChangeEvent.start();
+
+ // Make sure the change stream query started.
+ assert.soon(() => primaryDB.currentOp({"command.getMore": cursorId}).inprog.length === 1);
+
+ // Do some operations on the new primary that we can receive in a resumed stream.
+ let syncSource = rollbackTest.transitionToSyncSourceOperationsBeforeRollback();
+ coll = syncSource.getDB(dbName)[collName];
+ assert.commandWorked(coll.insert({_id: 5}));
+ assert.commandWorked(coll.insert({_id: 6}));
+ assert.commandWorked(coll.insert({_id: 7}));
+
+ // Let rollback begin and complete.
+ rollbackTest.transitionToSyncSourceOperationsDuringRollback();
+ rollbackTest.transitionToSteadyStateOperations();
+
+ // The change stream query should have failed when the node entered rollback.
+ assert(getChangeEvent.returnData());
+
+ jsTestLog("Resuming change stream against new primary.");
+ res = syncSource.getDB(dbName).runCommand(
+ {aggregate: collName, pipeline: [{$changeStream: {resumeAfter: resumeToken}}], cursor: {}});
+ changes = res.cursor.firstBatch;
+ assert.eq(changes.length, 3);
+ assert.eq(changes[0]["fullDocument"], {_id: 5});
+ assert.eq(changes[0]["operationType"], "insert");
+ assert.eq(changes[1]["fullDocument"], {_id: 6});
+ assert.eq(changes[1]["operationType"], "insert");
+ assert.eq(changes[2]["fullDocument"], {_id: 7});
+ assert.eq(changes[2]["operationType"], "insert");
+
+ rollbackTest.stop();
+
+})();
diff --git a/jstests/replsets/speculative_majority_find.js b/jstests/replsets/speculative_majority_find.js
new file mode 100644
index 00000000000..e6c3a41ef88
--- /dev/null
+++ b/jstests/replsets/speculative_majority_find.js
@@ -0,0 +1,155 @@
+/**
+ * Test speculative majority reads using the 'find' command.
+ *
+ * Speculative majority reads allow the server to provide "majority" read guarantees without storage
+ * engine support for reading from a historical snapshot. Instead of reading historical, majority
+ * committed data, we just read the newest data available on a node, and then, before returning to a
+ * client, block until we know the data has become majority committed. Currently this is an internal
+ * feature used only by change streams.
+ */
+(function() {
+ "use strict";
+
+ load("jstests/libs/write_concern_util.js"); // for [stop|restart]ServerReplication.
+ load("jstests/libs/parallelTester.js"); // for ScopedThread.
+
+ let name = "speculative_majority_find";
+ let replTest = new ReplSetTest({
+ name: name,
+ nodes: [{}, {rsConfig: {priority: 0}}],
+ nodeOptions: {enableMajorityReadConcern: 'false'}
+ });
+ replTest.startSet();
+ replTest.initiate();
+
+ let dbName = name;
+ let collName = "coll";
+
+ let primary = replTest.getPrimary();
+ let secondary = replTest.getSecondary();
+
+ let primaryDB = primary.getDB(dbName);
+ let secondaryDB = secondary.getDB(dbName);
+ let primaryColl = primaryDB[collName];
+ // Create a collection.
+ assert.commandWorked(primaryColl.insert({}, {writeConcern: {w: "majority"}}));
+
+ //
+ // Test basic reads with speculative majority.
+ //
+
+ // Pause replication on the secondary so that writes won't majority commit.
+ stopServerReplication(secondary);
+ assert.commandWorked(primaryColl.insert({_id: 1}));
+
+ jsTestLog("Do a speculative majority read that should time out.");
+ let res = primaryDB.runCommand({
+ find: collName,
+ readConcern: {level: "majority"},
+ filter: {_id: 1},
+ allowSpeculativeMajorityRead: true,
+ maxTimeMS: 5000
+ });
+ assert.commandFailedWithCode(res, ErrorCodes.MaxTimeMSExpired);
+
+ restartServerReplication(secondary);
+ replTest.awaitReplication();
+
+ jsTestLog("Do a speculative majority read that should succeed.");
+ res = primaryDB.runCommand({
+ find: collName,
+ readConcern: {level: "majority"},
+ filter: {_id: 1},
+ allowSpeculativeMajorityRead: true
+ });
+ assert.commandWorked(res);
+ assert.eq(res.cursor.firstBatch.length, 1);
+ assert.eq(res.cursor.firstBatch[0], {_id: 1});
+
+ //
+ // Test that blocked reads can succeed when a write majority commits.
+ //
+
+ // Pause replication on the secondary so that writes won't majority commit.
+ stopServerReplication(secondary);
+ assert.commandWorked(primaryColl.insert({_id: 2}));
+
+ jsTestLog("Do a speculative majority that should block until write commits.");
+ let speculativeRead = new ScopedThread(function(host, dbName, collName) {
+ const nodeDB = new Mongo(host).getDB(dbName);
+ return nodeDB.runCommand({
+ find: collName,
+ readConcern: {level: "majority"},
+ filter: {_id: 2},
+ allowSpeculativeMajorityRead: true
+ });
+ }, primary.host, dbName, collName);
+ speculativeRead.start();
+
+ // Wait for the read to start on the server.
+ assert.soon(() => primaryDB.currentOp({ns: primaryColl.getFullName(), "command.find": collName})
+ .inprog.length === 1);
+
+ // Let the previous write commit.
+ restartServerReplication(secondary);
+ assert.commandWorked(
+ primaryColl.insert({_id: "commit_last_write"}, {writeConcern: {w: "majority"}}));
+
+ // Make sure the read finished and returned correct results.
+ speculativeRead.join();
+ res = speculativeRead.returnData();
+ assert.commandWorked(res);
+ assert.eq(res.cursor.firstBatch.length, 1);
+ assert.eq(res.cursor.firstBatch[0], {_id: 2});
+
+ //
+ // Test 'afterClusterTime' reads with speculative majority.
+ //
+ stopServerReplication(secondary);
+
+ // Insert a document on the primary and record the response.
+ let writeRes = primaryDB.runCommand({insert: collName, documents: [{_id: 3}]});
+ assert.commandWorked(writeRes);
+
+ jsTestLog(
+ "Do a speculative majority read on primary with 'afterClusterTime' that should time out.");
+ res = primaryDB.runCommand({
+ find: collName,
+ readConcern: {level: "majority", afterClusterTime: writeRes.operationTime},
+ filter: {_id: 3},
+ $clusterTime: writeRes.$clusterTime,
+ allowSpeculativeMajorityRead: true,
+ maxTimeMS: 5000
+ });
+ assert.commandFailedWithCode(res, ErrorCodes.MaxTimeMSExpired);
+
+ jsTestLog(
+ "Do a speculative majority read on secondary with 'afterClusterTime' that should time out.");
+ res = secondaryDB.runCommand({
+ find: collName,
+ readConcern: {level: "majority", afterClusterTime: writeRes.operationTime},
+ filter: {_id: 3},
+ $clusterTime: writeRes.$clusterTime,
+ allowSpeculativeMajorityRead: true,
+ maxTimeMS: 5000
+ });
+ assert.commandFailedWithCode(res, ErrorCodes.MaxTimeMSExpired);
+
+ // Let the previous write majority commit.
+ restartServerReplication(secondary);
+ replTest.awaitReplication();
+
+ jsTestLog("Do a speculative majority read with 'afterClusterTime' that should succeed.");
+ res = primaryDB.runCommand({
+ find: collName,
+ readConcern: {level: "majority", afterClusterTime: writeRes.operationTime},
+ filter: {_id: 3},
+ $clusterTime: res.$clusterTime,
+ allowSpeculativeMajorityRead: true
+ });
+ assert.commandWorked(res);
+ assert.eq(res.cursor.firstBatch.length, 1);
+ assert.eq(res.cursor.firstBatch[0], {_id: 3});
+
+ replTest.stopSet();
+})(); \ No newline at end of file
diff --git a/jstests/replsets/speculative_majority_supported_commands.js b/jstests/replsets/speculative_majority_supported_commands.js
new file mode 100644
index 00000000000..ad8d898dbf9
--- /dev/null
+++ b/jstests/replsets/speculative_majority_supported_commands.js
@@ -0,0 +1,74 @@
+/**
+ * Verify that speculative majority is only allowed on supported commands.
+ *
+ * Currently, only change stream aggregation commands and the 'find' command with the
+ * 'allowSpeculativeMajorityRead' flag are permitted.
+ */
+(function() {
+ "use strict";
+
+ let name = "speculative_majority_supported_commands";
+ let replTest =
+ new ReplSetTest({name: name, nodes: 1, nodeOptions: {enableMajorityReadConcern: 'false'}});
+ replTest.startSet();
+ replTest.initiate();
+
+ let dbName = name;
+ let collName = "coll";
+
+ let primary = replTest.getPrimary();
+ let primaryDB = primary.getDB(dbName);
+
+ // Create a collection.
+ assert.commandWorked(primaryDB[collName].insert({_id: 0}, {writeConcern: {w: "majority"}}));
+
+ /**
+ * Allowed commands.
+ */
+
+ // Change stream aggregation is allowed.
+ let res = primaryDB.runCommand({
+ aggregate: collName,
+ pipeline: [{$changeStream: {}}],
+ cursor: {},
+ readConcern: {level: "majority"}
+ });
+ assert.commandWorked(res);
+
+ // Find query with speculative flag is allowed.
+ res = primaryDB.runCommand(
+ {find: collName, readConcern: {level: "majority"}, allowSpeculativeMajorityRead: true});
+ assert.commandWorked(res);
+
+ /**
+ * Disallowed commands.
+ */
+
+ // A non change stream aggregation is not allowed.
+ res = primaryDB.runCommand({
+ aggregate: collName,
+ pipeline: [{$project: {}}],
+ cursor: {},
+ readConcern: {level: "majority"}
+ });
+ assert.commandFailedWithCode(res, ErrorCodes.ReadConcernMajorityNotEnabled);
+
+ // The 'find' command without requisite flag is unsupported.
+ res = primaryDB.runCommand({find: collName, readConcern: {level: "majority"}});
+ assert.commandFailedWithCode(res, ErrorCodes.ReadConcernMajorityNotEnabled);
+
+ res = primaryDB.runCommand(
+ {find: collName, readConcern: {level: "majority"}, allowSpeculativeMajorityRead: false});
+ assert.commandFailedWithCode(res, ErrorCodes.ReadConcernMajorityNotEnabled);
+
+ // Another basic read command. We don't exhaustively check all commands.
+ res = primaryDB.runCommand({count: collName, readConcern: {level: "majority"}});
+ assert.commandFailedWithCode(res, ErrorCodes.ReadConcernMajorityNotEnabled);
+
+ // Speculative flag is only allowed on find commands.
+ res = primaryDB.runCommand(
+ {count: collName, readConcern: {level: "majority"}, allowSpeculativeMajorityRead: true});
+ assert.commandFailedWithCode(res, ErrorCodes.ReadConcernMajorityNotEnabled);
+
+ replTest.stopSet();
+})(); \ No newline at end of file
diff --git a/jstests/sharding/change_stream_lookup_single_shard_cluster.js b/jstests/sharding/change_stream_lookup_single_shard_cluster.js
index ac00ea33d34..60ded0f352d 100644
--- a/jstests/sharding/change_stream_lookup_single_shard_cluster.js
+++ b/jstests/sharding/change_stream_lookup_single_shard_cluster.js
@@ -8,6 +8,14 @@
// For supportsMajorityReadConcern.
load('jstests/multiVersion/libs/causal_consistency_helpers.js');
+ // TODO (SERVER-38673): Remove this once BACKPORT-3428, BACKPORT-3429 are completed.
+ if (!jsTestOptions().enableMajorityReadConcern &&
+ jsTestOptions().mongosBinVersion === 'last-stable') {
+ jsTestLog(
+ "Skipping test since 'last-stable' mongos doesn't support speculative majority update lookup queries.");
+ return;
+ }
+
// This test only works on storage engines that support committed reads, skip it if the
// configured engine doesn't support it.
if (!supportsMajorityReadConcern()) {
diff --git a/jstests/sharding/change_stream_read_preference.js b/jstests/sharding/change_stream_read_preference.js
index 25a7b5ef061..4f35b42424a 100644
--- a/jstests/sharding/change_stream_read_preference.js
+++ b/jstests/sharding/change_stream_read_preference.js
@@ -9,6 +9,14 @@
// For supportsMajorityReadConcern.
load('jstests/multiVersion/libs/causal_consistency_helpers.js');
+ // TODO (SERVER-38673): Remove this once BACKPORT-3428, BACKPORT-3429 are completed.
+ if (!jsTestOptions().enableMajorityReadConcern &&
+ jsTestOptions().mongosBinVersion === 'last-stable') {
+ jsTestLog(
+ "Skipping test since 'last-stable' mongos doesn't support speculative majority update lookup queries.");
+ return;
+ }
+
// This test only works on storage engines that support committed reads, skip it if the
// configured engine doesn't support it.
if (!supportsMajorityReadConcern()) {
diff --git a/jstests/sharding/change_stream_update_lookup_collation.js b/jstests/sharding/change_stream_update_lookup_collation.js
index 6c57aba30e0..eefff9d463f 100644
--- a/jstests/sharding/change_stream_update_lookup_collation.js
+++ b/jstests/sharding/change_stream_update_lookup_collation.js
@@ -9,6 +9,14 @@
// For supportsMajorityReadConcern().
load("jstests/multiVersion/libs/causal_consistency_helpers.js");
+ // TODO (SERVER-38673): Remove this once BACKPORT-3428, BACKPORT-3429 are completed.
+ if (!jsTestOptions().enableMajorityReadConcern &&
+ jsTestOptions().mongosBinVersion === 'last-stable') {
+ jsTestLog(
+ "Skipping test since 'last-stable' mongos doesn't support speculative majority update lookup queries.");
+ return;
+ }
+
if (!supportsMajorityReadConcern()) {
jsTestLog("Skipping test since storage engine doesn't support majority read concern.");
return;
diff --git a/jstests/sharding/change_streams_primary_shard_unaware.js b/jstests/sharding/change_streams_primary_shard_unaware.js
index 0dade0d553c..1fdb86564ae 100644
--- a/jstests/sharding/change_streams_primary_shard_unaware.js
+++ b/jstests/sharding/change_streams_primary_shard_unaware.js
@@ -12,6 +12,14 @@
// For supportsMajorityReadConcern().
load("jstests/multiVersion/libs/causal_consistency_helpers.js");
+ // TODO (SERVER-38673): Remove this once BACKPORT-3428, BACKPORT-3429 are completed.
+ if (!jsTestOptions().enableMajorityReadConcern &&
+ jsTestOptions().mongosBinVersion === 'last-stable') {
+ jsTestLog(
+ "Skipping test since 'last-stable' mongos doesn't support speculative majority update lookup queries.");
+ return;
+ }
+
if (!supportsMajorityReadConcern()) {
jsTestLog("Skipping test since storage engine doesn't support majority read concern.");
return;