summaryrefslogtreecommitdiff
path: root/jstests/replsets/snapshot_reads_before_initial_sync_finishes.js
blob: e22a237635db54478e9bad3dd1cbba20008ee4e4 (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
/**
 * Tests that reading at an atClusterTime earlier than the timestamp when initial sync finishes
 * would result in SnapshotTooOld error regardless of the snapshot history window.
 *
 * @tags: [
 *   requires_fcv_47,
 *   requires_majority_read_concern,
 *   requires_persistence,
 * ]
 */
(function() {
"use strict";

load("jstests/libs/fail_point_util.js");

const replSet = new ReplSetTest({nodes: 2});

replSet.startSet();
replSet.initiateWithHighElectionTimeout();

const collName = "coll";
const primary = replSet.getPrimary();
const secondary = replSet.getSecondary();
const primaryDB = primary.getDB('test');
const secondaryDB = secondary.getDB('test');

assert.commandWorked(
    primaryDB.runCommand({insert: collName, documents: [{_id: 0}], writeConcern: {w: "majority"}}));

jsTestLog("Adding a new node");
const newNode = replSet.add({
    rsConfig: {priority: 0},
    setParameter: {
        'failpoint.forceSyncSourceCandidate':
            tojson({mode: 'alwaysOn', data: {"hostAndPort": primary.host}}),
        'failpoint.initialSyncHangAfterDataCloning': tojson({mode: 'alwaysOn'}),
        'numInitialSyncAttempts': 1,
        // Set a large snapshot history window of 1 hour.
        'minSnapshotHistoryWindowInSeconds': 3600,
    }
});
replSet.reInitiate();
replSet.waitForState(newNode, ReplSetTest.State.STARTUP_2);

const newNodeDB = newNode.getDB('test');

assert.commandWorked(newNode.adminCommand({
    waitForFailPoint: "initialSyncHangAfterDataCloning",
    timesEntered: 1,
    maxTimeMS: kDefaultWaitForFailPointTimeout
}));

// Perform another write after the new node finishes cloning.
const timestampDuringInitialSync =
    assert
        .commandWorked(primaryDB.runCommand(
            {insert: collName, documents: [{_id: 1}], writeConcern: {w: "majority"}}))
        .operationTime;
jsTestLog("timestampDuringInitialSync is " + tojson(timestampDuringInitialSync));

// Perform snapshot reads on both the primary and the secondary and test that we can see the
// majority committed writes.
const findAtClusterTimeDuringInitialSync = {
    find: collName,
    readConcern: {level: "snapshot", atClusterTime: timestampDuringInitialSync},
};
const documents = [{_id: 0}, {_id: 1}];
assert.sameMembers(primaryDB.runCommand(findAtClusterTimeDuringInitialSync).cursor.firstBatch,
                   documents);
assert.sameMembers(secondaryDB.runCommand(findAtClusterTimeDuringInitialSync).cursor.firstBatch,
                   documents);

// Test reading at a timestamp before initial sync finishes is not allowed while the node is in
// initial sync.
assert.commandFailedWithCode(newNodeDB.runCommand(findAtClusterTimeDuringInitialSync),
                             ErrorCodes.NotPrimaryOrSecondary);

// Perform another write so that the new node will finish initial sync at a timestamp higher than
// the timestampDuringInitialSync.
assert.commandWorked(primaryDB.runCommand({insert: collName, documents: [{_id: 2}]}));

// Allow the new node to complete initial sync.
assert.commandWorked(
    newNode.adminCommand({configureFailPoint: "initialSyncHangAfterDataCloning", mode: "off"}));
replSet.awaitSecondaryNodes(null, [newNode]);
replSet.awaitLastOpCommitted();

// Test reading at a timestamp before initial sync finishes is not allowed even if the node has
// finished initial sync and has a large snapshot history window size.
assert.commandFailedWithCode(newNodeDB.runCommand(findAtClusterTimeDuringInitialSync),
                             ErrorCodes.SnapshotTooOld);

// Test snapshot readConcern reads all committed writes.
assert.sameMembers(newNodeDB
                       .runCommand({
                           find: collName,
                           readConcern: {level: "snapshot"},
                       })
                       .cursor.firstBatch,
                   [{_id: 0}, {_id: 1}, {_id: 2}]);

replSet.stopSet();
})();