Files
mongo/jstests/sharding/change_stream_read_preference.js
William Schultz 435200964a 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.
2018-12-21 16:43:58 -05:00

141 lines
6.2 KiB
JavaScript

// Tests that change streams and their update lookups obey the read preference specified by the
// user.
// @tags: [uses_change_streams]
(function() {
"use strict";
load('jstests/libs/profiler.js'); // For various profiler helpers.
// 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()) {
jsTestLog("Skipping test since storage engine doesn't support majority read concern.");
return;
}
const st = new ShardingTest({
name: "change_stream_read_pref",
shards: 2,
rs: {
nodes: 2,
// Use a higher frequency for periodic noops to speed up the test.
setParameter: {periodicNoopIntervalSecs: 1, writePeriodicNoops: true}
},
});
const dbName = jsTestName();
const mongosDB = st.s0.getDB(dbName);
const mongosColl = mongosDB[jsTestName()];
// Enable sharding on the test DB and ensure its primary is st.shard0.shardName.
assert.commandWorked(mongosDB.adminCommand({enableSharding: mongosDB.getName()}));
st.ensurePrimaryShard(mongosDB.getName(), st.rs0.getURL());
// Shard the test collection on _id.
assert.commandWorked(
mongosDB.adminCommand({shardCollection: mongosColl.getFullName(), key: {_id: 1}}));
// Split the collection into 2 chunks: [MinKey, 0), [0, MaxKey].
assert.commandWorked(
mongosDB.adminCommand({split: mongosColl.getFullName(), middle: {_id: 0}}));
// Move the [0, MaxKey] chunk to st.shard1.shardName.
assert.commandWorked(mongosDB.adminCommand(
{moveChunk: mongosColl.getFullName(), find: {_id: 1}, to: st.rs1.getURL()}));
// Turn on the profiler.
for (let rs of[st.rs0, st.rs1]) {
assert.commandWorked(rs.getPrimary().getDB(dbName).setProfilingLevel(2));
assert.commandWorked(rs.getSecondary().getDB(dbName).setProfilingLevel(2));
}
// Write a document to each chunk.
assert.writeOK(mongosColl.insert({_id: -1}, {writeConcern: {w: "majority"}}));
assert.writeOK(mongosColl.insert({_id: 1}, {writeConcern: {w: "majority"}}));
// Test that change streams go to the primary by default.
let changeStreamComment = "change stream against primary";
const primaryStream = mongosColl.aggregate([{$changeStream: {fullDocument: "updateLookup"}}],
{comment: changeStreamComment});
assert.writeOK(mongosColl.update({_id: -1}, {$set: {updated: true}}));
assert.writeOK(mongosColl.update({_id: 1}, {$set: {updated: true}}));
assert.soon(() => primaryStream.hasNext());
assert.eq(primaryStream.next().fullDocument, {_id: -1, updated: true});
assert.soon(() => primaryStream.hasNext());
assert.eq(primaryStream.next().fullDocument, {_id: 1, updated: true});
for (let rs of[st.rs0, st.rs1]) {
const primaryDB = rs.getPrimary().getDB(dbName);
// Test that the change stream itself goes to the primary. There might be more than one if
// we needed multiple getMores to retrieve the changes.
// TODO SERVER-31650 We have to use 'originatingCommand' here and look for the getMore
// because the initial aggregate will not show up.
profilerHasAtLeastOneMatchingEntryOrThrow(
{profileDB: primaryDB, filter: {'originatingCommand.comment': changeStreamComment}});
// Test that the update lookup goes to the primary as well.
profilerHasSingleMatchingEntryOrThrow({
profileDB: primaryDB,
filter: {
op: "query",
ns: mongosColl.getFullName(), "command.comment": changeStreamComment
}
});
}
primaryStream.close();
// Test that change streams go to the secondary when the readPreference is {mode: "secondary"}.
changeStreamComment = 'change stream against secondary';
const secondaryStream =
mongosColl.aggregate([{$changeStream: {fullDocument: "updateLookup"}}],
{comment: changeStreamComment, $readPreference: {mode: "secondary"}});
assert.writeOK(mongosColl.update({_id: -1}, {$set: {updatedCount: 2}}));
assert.writeOK(mongosColl.update({_id: 1}, {$set: {updatedCount: 2}}));
assert.soon(() => secondaryStream.hasNext());
assert.eq(secondaryStream.next().fullDocument, {_id: -1, updated: true, updatedCount: 2});
assert.soon(() => secondaryStream.hasNext());
assert.eq(secondaryStream.next().fullDocument, {_id: 1, updated: true, updatedCount: 2});
for (let rs of[st.rs0, st.rs1]) {
const secondaryDB = rs.getSecondary().getDB(dbName);
// Test that the change stream itself goes to the secondary. There might be more than one if
// we needed multiple getMores to retrieve the changes.
// TODO SERVER-31650 We have to use 'originatingCommand' here and look for the getMore
// because the initial aggregate will not show up.
profilerHasAtLeastOneMatchingEntryOrThrow(
{profileDB: secondaryDB, filter: {'originatingCommand.comment': changeStreamComment}});
// Test that the update lookup goes to the secondary as well.
profilerHasSingleMatchingEntryOrThrow({
profileDB: secondaryDB,
filter: {
op: "query",
ns: mongosColl.getFullName(), "command.comment": changeStreamComment,
// We need to filter out any profiler entries with a stale config - this is the
// first read on this secondary with a readConcern specified, so it is the first
// read on this secondary that will enforce shard version.
errCode: {$ne: ErrorCodes.StaleConfig}
}
});
}
secondaryStream.close();
st.stop();
}());