summaryrefslogtreecommitdiff
path: root/jstests/replsets/read_at_cluster_time_outside_transactions.js
blob: 1d19cca776c8d90c7f98421e8ab6189b1f9f3e6d (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
/**
 * Tests that the "dbHash" command support reading at a Timestamp by using the
 * $_internalReadAtClusterTime option.
 *
 * @tags: [
 *   requires_fcv_47,
 *   uses_transactions,
 * ]
 */
(function() {
"use strict";

const rst = new ReplSetTest({nodes: 1});
rst.startSet();
rst.initiate();

const primary = rst.getPrimary();
const db = primary.getDB("test");

const collName = "read_at_cluster_time_outside_transactions";
const collection = db[collName];

// We prevent the replica set from advancing oldest_timestamp. This ensures that the snapshot
// associated with 'clusterTime' is retained for the duration of this test.
rst.nodes.forEach(conn => {
    assert.commandWorked(conn.adminCommand({
        configureFailPoint: "WTPreserveSnapshotHistoryIndefinitely",
        mode: "alwaysOn",
    }));
});

// We insert 3 documents in order to have data to return for both the find and getMore commands
// when using a batch size of 2. We then save the md5sum associated with the opTime of the last
// insert.
assert.commandWorked(collection.insert({_id: 1, comment: "should be seen by find command"}));
assert.commandWorked(collection.insert({_id: 3, comment: "should be seen by find command"}));

const earlierClusterTime = db.getSession().getOperationTime();

assert.commandWorked(collection.insert({_id: 5, comment: "should be seen by getMore command"}));

const clusterTime = db.getSession().getOperationTime();

let res = assert.commandWorked(db.runCommand({dbHash: 1}));
const hashAfterOriginalInserts = {
    collections: res.collections,
    md5: res.md5
};

// The documents with _id=1 and _id=3 should be returned by the find command.
let cursor = collection.find().sort({_id: 1}).batchSize(2);
assert.eq({_id: 1, comment: "should be seen by find command"}, cursor.next());
assert.eq({_id: 3, comment: "should be seen by find command"}, cursor.next());

// We then insert documents with _id=2 and _id=4. The document with _id=2 is positioned behind
// the _id index cursor and won't be returned by the getMore command. However, the document with
// _id=4 is positioned ahead and should end up being returned.
assert.commandWorked(collection.insert({_id: 2, comment: "should not be seen by getMore command"}));
assert.commandWorked(
    collection.insert({_id: 4, comment: "should be seen by non-snapshot getMore command"}));
assert.eq({_id: 4, comment: "should be seen by non-snapshot getMore command"}, cursor.next());
assert.eq({_id: 5, comment: "should be seen by getMore command"}, cursor.next());
assert(!cursor.hasNext());

// Using snapshot read concern with dbHash to read at the opTime of the last of the 3
// original inserts should return the same md5sum as it did originally.
res = assert.commandWorked(db.runCommand({
    dbHash: 1,
    $_internalReadAtClusterTime: clusterTime,
}));

const hashAtClusterTime = {
    collections: res.collections,
    md5: res.md5
};
assert.eq(hashAtClusterTime, hashAfterOriginalInserts);

assert.commandFailedWithCode(db.runCommand({
    dbHash: 1,
    $_internalReadAtClusterTime: new Timestamp(0, 1),
}),
                             ErrorCodes.InvalidOptions);

// Attempting to read at a clusterTime in the future should return an error.
const futureClusterTime = new Timestamp(clusterTime.getTime() + 1000, 1);

assert.commandFailedWithCode(db.runCommand({
    dbHash: 1,
    $_internalReadAtClusterTime: futureClusterTime,
}),
                             ErrorCodes.InvalidOptions);

// $_internalReadAtClusterTime is not supported in transactions.
const session = primary.startSession();
const sessionDB = session.getDatabase("test");

// dbHash is not supported in transactions at all.
session.startTransaction();
assert.commandFailedWithCode(
    sessionDB.runCommand({dbHash: 1, $_internalReadAtClusterTime: clusterTime}),
    ErrorCodes.OperationNotSupportedInTransaction);
assert.commandFailedWithCode(session.abortTransaction_forTesting(), ErrorCodes.NoSuchTransaction);

rst.stopSet();
})();