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
128
129
130
131
132
133
|
/*
* This test exercises the "linearizable" readConcern option on a simple sharded cluster.
* Note that a full linearizable read concern test exists in
* "replsets/linearizable_read_concern.js". This test exists mainly to affirm that a
* readConcern "linearizable" propagates correctly through a sharded cluster i.e. we
* execute database commands only through a mongos, not directly on a replica set.
*
* There is one mongos and two shards (each a 3-node replica set). We put one
* chunk on each shard, each containing five documents. We then execute a
* linearizable read targeting both shards with readPreference "secondary", to
* make sure it fails. We next execute a linearizable read targeting both
* shards with readPreference "primary" to make sure it succeeds. The primary
* is then partitioned from the other two secondaries in the first shard, and
* we make sure that a linearizable read targeting primaries in both shards
* times out, since the partitioned primary can no longer communicate with a
* majority of nodes.
*
* NOTE: Linearizability guarantees only apply when a query specifies a unique
* document. This test is mainly trying to ensure that system behavior is
* reasonable when executing linearizable reads in a sharded cluster, so as to
* exercise possible (invalid) user behavior.
*/
load("jstests/replsets/rslib.js");
load("jstests/libs/write_concern_util.js");
(function() {
"use strict";
// Skip the following checks since this test leaves a replica set shard partitioned.
TestData.skipCheckDBHashes = true;
TestData.skipAwaitingReplicationOnShardsBeforeCheckingUUIDs = true;
TestData.skipCheckShardFilteringMetadata = true;
var testName = "linearizable_read_concern";
var st = new ShardingTest({
name: testName,
other: {rs0: {nodes: 3}, rs1: {nodes: 3}, useBridge: true},
mongos: 1,
config: TestData.configShard ? undefined : 1,
enableBalancer: false
});
jsTestLog("Setting up sharded cluster.");
// Set up the sharded cluster.
var dbName = testName;
var collName = "test";
var collNamespace = dbName + "." + collName;
var shard0ReplTest = st.rs0;
var shard1ReplTest = st.rs1;
var testDB = st.s.getDB(dbName);
// Set high election timeout so that primary doesn't step down during linearizable read test.
var cfg = shard0ReplTest.getReplSetConfigFromNode(0);
cfg.settings.electionTimeoutMillis = shard0ReplTest.kDefaultTimeoutMS;
reconfig(shard0ReplTest, cfg, true);
// Set up sharded collection. Put 5 documents on each shard, with keys {x: 0...9}.
var numDocs = 10;
shardCollectionWithChunks(st, testDB[collName], numDocs);
// Make sure the 'shardIdentity' document on each shard is replicated to all secondary nodes
// before issuing reads against them.
shard0ReplTest.awaitReplication();
shard1ReplTest.awaitReplication();
// Print current sharding stats for debugging.
st.printShardingStatus(5);
// Filter to target one document in each shard.
var shard0DocKey = 2;
var shard1DocKey = 7;
var dualShardQueryFilter = {$or: [{x: shard0DocKey}, {x: shard1DocKey}]};
jsTestLog("Testing linearizable read from secondaries");
// Execute a linearizable read from secondaries (targeting both shards) which should fail.
st.s.setReadPref("secondary");
var res = assert.commandFailed(testDB.runReadCommand({
find: collName,
filter: dualShardQueryFilter,
readConcern: {level: "linearizable"},
maxTimeMS: shard0ReplTest.kDefaultTimeoutMS
}));
assert.eq(res.code, ErrorCodes.doMongosRewrite(st.s, ErrorCodes.NotWritablePrimary));
jsTestLog("Testing linearizable read from primaries.");
// Execute a linearizable read from primaries (targeting both shards) which should succeed.
st.s.setReadPref("primary");
var res = assert.commandWorked(testDB.runReadCommand({
find: collName,
sort: {x: 1},
filter: dualShardQueryFilter,
readConcern: {level: "linearizable"},
maxTimeMS: shard0ReplTest.kDefaultTimeoutMS
}));
// Make sure data was returned from both shards correctly.
assert.eq(res.cursor.firstBatch[0].x, shard0DocKey);
assert.eq(res.cursor.firstBatch[1].x, shard1DocKey);
jsTestLog("Testing linearizable read targeting partitioned primary.");
var primary = shard0ReplTest.getPrimary();
var secondaries = shard0ReplTest.getSecondaries();
// Partition the primary in the first shard.
secondaries[0].disconnect(primary);
secondaries[1].disconnect(primary);
jsTestLog("Current Replica Set Topology of First Shard: [Secondary-Secondary] [Primary]");
// Execute a linearizable read targeting the partitioned primary in first shard, and good
// primary in the second shard. This should time out due to partitioned primary.
var result = testDB.runReadCommand({
find: collName,
filter: dualShardQueryFilter,
readConcern: {level: "linearizable"},
maxTimeMS: 3000
});
assert.commandFailedWithCode(result, ErrorCodes.MaxTimeMSExpired);
if (TestData.configShard) {
// Reconnect so the config server is available for shutdown hooks.
secondaries[0].reconnect(primary);
secondaries[1].reconnect(primary);
}
st.stop();
})();
|