1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
/**
* Tests that non-timestamped reads are not allowed on the donor after the split has committed
* so that we typically provide read-your-own-write guarantees for primary reads across shard
* split when there is no other failover.
*
* @tags: [
* incompatible_with_eft,
* incompatible_with_macos,
* incompatible_with_windows_tls,
* requires_majority_read_concern,
* requires_persistence,
* serverless,
* requires_fcv_62
* ]
*/
(function() {
"use strict";
load("jstests/serverless/libs/shard_split_test.js");
load("jstests/libs/uuid_util.js");
load("jstests/libs/fail_point_util.js"); // For configureFailPoint().
const test =
new ShardSplitTest({recipientSetName: "recipientSet", recipientTagName: "recipientTag"});
test.addRecipientNodes();
const kTenantId = ObjectId();
const kDbName = test.tenantDB(kTenantId.str, "testDB");
const kCollName = "testColl";
const donorPrimary = test.donor.getPrimary();
jsTestLog(`Inserting data on the donor`);
const donorDB = donorPrimary.getDB(kDbName);
assert.commandWorked(donorDB.runCommand({
insert: kCollName,
documents: [...Array(10).keys()].map(x => ({x: x})),
writeConcern: {w: 'majority'}
}));
const cursor = assert
.commandWorked(donorDB.runCommand({
find: kCollName,
batchSize: 5,
}))
.cursor;
assert.eq(5, cursor.firstBatch.length, tojson(cursor));
assert.neq(0, cursor.id, tojson(cursor));
jsTestLog(`Started cursor id ${cursor.id} on the donor before the migration`);
const operation = test.createSplitOperation([kTenantId]);
assert.commandWorked(operation.commit());
test.removeAndStopRecipientNodes();
// Test that getMore works after the migration has committed.
jsTestLog(`Testing getMore on cursor id ${cursor.id} on the donor after the migration`);
assert.commandWorked(donorDB.runCommand({getMore: cursor.id, collection: kCollName}));
// Test that local and majority reads are no longer allowed on the donor.
const testCases = {
find: {command: {find: kCollName}},
count: {command: {count: kCollName}},
distinct: {command: {distinct: kCollName, key: "x", query: {}}},
aggregate: {command: {aggregate: kCollName, pipeline: [{$match: {}}], cursor: {}}},
mapReduce: {
command: {
mapReduce: kCollName,
map: () => {
emit(this.x, 1);
},
reduce: (key, value) => {
return 1;
},
out: {inline: 1}
},
skipReadConcernMajority: true,
},
findAndModify: {
command: {findAndModify: kCollName, query: {x: 1}, update: {$set: {x: 1}}},
skipReadConcernMajority: true,
},
update: {
// No-op write due to stale read is also not allowed.
command: {update: kCollName, updates: [{q: {x: 1}, u: {$set: {x: 1}}}]},
skipReadConcernMajority: true,
},
delete: {
// No-op write due to stale read is also not allowed.
command: {delete: kCollName, deletes: [{q: {x: 100}, limit: 1}]},
skipReadConcernMajority: true,
},
listCollections: {
command: {listCollections: 1},
skipReadConcernMajority: true,
},
listIndexes: {
command: {listIndexes: kCollName},
skipReadConcernMajority: true,
},
};
const readConcerns = {
local: {level: "local"},
majority: {level: "majority"},
};
for (const [testCaseName, testCase] of Object.entries(testCases)) {
for (const [readConcernName, readConcern] of Object.entries(readConcerns)) {
if (testCase.skipReadConcernMajority && readConcernName === "majority") {
continue;
}
jsTest.log(`Testing ${testCaseName} with readConcern ${readConcernName}`);
let cmd = testCase.command;
cmd.readConcern = readConcern;
assert.commandFailedWithCode(donorDB.runCommand(cmd), ErrorCodes.TenantMigrationCommitted);
}
}
// Enable stale reads on the donor set so that end of test data consistency check can pass.
test.donor.nodes.forEach(
node => assert.commandWorked(node.adminCommand(
{configureFailPoint: "tenantMigrationDonorAllowsNonTimestampedReads", mode: "alwaysOn"})));
test.stop();
})();
|