summaryrefslogtreecommitdiff
path: root/jstests/replsets/operation_time_read_and_write_concern.js
blob: 89e813faeb368f8fff8105a37f221cc794777a9a (plain)
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
/**
 * Validates the operationTime value in the command response depends on the read/writeConcern of the
 * the read/write command that produced it.
 * @tags: [requires_majority_read_concern]
 */
(function() {
"use strict";

// Skip db hash check because replication is stopped on secondaries.
TestData.skipCheckDBHashes = true;

load("jstests/libs/write_concern_util.js");  // For stopReplicationOnSecondaries,
                                             // restartReplicationOnSecondaries
var name = "operation_time_read_and_write_concern";

var replTest = new ReplSetTest(
    {name: name, nodes: 3, nodeOptions: {enableMajorityReadConcern: ""}, waitForKeys: true});
replTest.startSet();
replTest.initiate();

var res;
var testDB = replTest.getPrimary().getDB(name);
var collectionName = "foo";

// readConcern level majority:
// operationTime is the cluster time of the last committed op in the oplog.
jsTestLog("Testing operationTime for readConcern level majority with afterClusterTime.");
var majorityDoc = {_id: 10, x: 1};
var localDoc = {_id: 15, x: 2};

res = assert.commandWorked(testDB.runCommand(
    {insert: collectionName, documents: [majorityDoc], writeConcern: {w: "majority"}}));
var majorityWriteOperationTime = res.operationTime;

stopReplicationOnSecondaries(replTest);

res = assert.commandWorked(
    testDB.runCommand({insert: collectionName, documents: [localDoc], writeConcern: {w: 1}}));
var localWriteOperationTime = res.operationTime;

assert.gt(localWriteOperationTime, majorityWriteOperationTime);

res = assert.commandWorked(testDB.runCommand({
    find: collectionName,
    readConcern: {level: "majority", afterClusterTime: majorityWriteOperationTime}
}));
var majorityReadOperationTime = res.operationTime;

assert.eq(res.cursor.firstBatch,
          [majorityDoc],
          "only the committed document, " + tojson(majorityDoc) +
              ", should be returned for the majority read with afterClusterTime: " +
              tojson(majorityWriteOperationTime));
assert.eq(majorityReadOperationTime,
          majorityWriteOperationTime,
          "the operationTime of the majority read, " + tojson(majorityReadOperationTime) +
              ", should be the cluster time of the last committed op in the oplog, " +
              tojson(majorityWriteOperationTime));

// Validate that after replication, the local write data is now returned by the same query.
restartReplicationOnSecondaries(replTest);
replTest.awaitLastOpCommitted();

res = assert.commandWorked(testDB.runCommand({
    find: collectionName,
    sort: {_id: 1},  // So the order of the documents is defined for testing.
    readConcern: {level: "majority", afterClusterTime: majorityWriteOperationTime}
}));
var secondMajorityReadOperationTime = res.operationTime;

assert.eq(res.cursor.firstBatch,
          [majorityDoc, localDoc],
          "expected both inserted documents, " + tojson([majorityDoc, localDoc]) +
              ", to be returned for the second majority read with afterClusterTime: " +
              tojson(majorityWriteOperationTime));
assert.eq(secondMajorityReadOperationTime,
          localWriteOperationTime,
          "the operationTime of the second majority read, " +
              tojson(secondMajorityReadOperationTime) +
              ", should be the cluster time of the replicated local write, " +
              tojson(localWriteOperationTime));

// readConcern level linearizable is not currently supported.
jsTestLog("Verifying readConcern linearizable with afterClusterTime is not supported.");
res = assert.commandFailedWithCode(
    testDB.runCommand({
        find: collectionName,
        filter: localDoc,
        readConcern: {level: "linearizable", afterClusterTime: majorityReadOperationTime}
    }),
    ErrorCodes.InvalidOptions,
    "linearizable reads with afterClusterTime are not supported and should not be allowed");

// writeConcern level majority:
// operationTime is the cluster time of the write if it succeeds, or of the previous successful
// write at the time the write was determined to have failed, or a no-op.
jsTestLog("Testing operationTime for writeConcern level majority.");
var successfulDoc = {_id: 1000, y: 1};
var failedDoc = {_id: 1000, y: 2};

res = assert.commandWorked(testDB.runCommand(
    {insert: collectionName, documents: [successfulDoc], writeConcern: {w: "majority"}}));
var majorityWriteOperationTime = res.operationTime;

stopReplicationOnSecondaries(replTest);

res = testDB.runCommand({
    insert: collectionName,
    documents: [failedDoc],
    writeConcern: {w: "majority", wtimeout: 1000}
});
assert.eq(res.writeErrors[0].code, ErrorCodes.DuplicateKey);
var failedWriteOperationTime = res.operationTime;

assert.eq(failedWriteOperationTime,
          majorityWriteOperationTime,
          "the operationTime of the failed majority write, " + tojson(failedWriteOperationTime) +
              ", should be the cluster time of the last successful write at the time it failed, " +
              tojson(majorityWriteOperationTime));
replTest.stopSet();
})();