summaryrefslogtreecommitdiff
path: root/jstests/replsets/apply_batch_only_goes_forward.js
blob: a461e9fad12cf85623c7d5ca185053da366a12af (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
/**
 * This test will ensure that a failed a batch apply will become consistent only when passing
 * the end boundary (minvalid) in subsequent applies.
 *
 * To do this we:
 * -- Set minvalid manually on primary (node0) way ahead (5 minutes)
 * -- Restart primary (node0)
 * -- Ensure restarted primary (node0) comes up in recovering
 * -- Ensure node0 replicates a batch, and keeps the old minvalid
 * -- Success!
 *
 * This test requires persistence to test that a restarted primary will stay in the RECOVERING state
 * when minvalid is set to the future. An ephemeral storage engine will not have a minvalid after
 * restarting, so will initial sync in this scenario, invalidating the test.
 * @tags: [requires_persistence]
 */
(function() {
    "use strict";

    function tsToDate(ts) {
        return new Date(ts.getTime() * 1000);
    }

    var replTest = new ReplSetTest({name: "apply_batch_only_goes_forward", nodes: 3});

    var nodes = replTest.startSet();
    replTest.initiate();
    var master = replTest.getPrimary();
    var mTest = master.getDB("test");
    var mLocal = master.getDB("local");
    var mMinvalid = mLocal["replset.minvalid"];

    var slave = replTest.getSecondary();
    var sTest = slave.getDB("test");
    var sLocal = slave.getDB("local");
    var sMinvalid = sLocal["replset.minvalid"];
    var stepDownSecs = 30;
    var stepDownCmd = {replSetStepDown: stepDownSecs, force: true};

    // Write op
    assert.writeOK(mTest.foo.save({}, {writeConcern: {w: 3}}));
    replTest.waitForState(slave, ReplSetTest.State.SECONDARY, 30000);
    assert.writeOK(mTest.foo.save({}, {writeConcern: {w: 3}}));

    // Set minvalid to something far in the future for the current primary, to simulate recovery.
    // Note: This is so far in the future (5 days) that it will never become secondary.
    var farFutureTS = new Timestamp(
        Math.floor(new Date().getTime() / 1000) + (60 * 60 * 24 * 5 /* in five days*/), 0);
    var rsgs = assert.commandWorked(mLocal.adminCommand("replSetGetStatus"));
    var primaryOpTime = rsgs.members
                            .filter(function(member) {
                                return member.self;
                            })[0]
                            .optime;
    jsTest.log("future TS: " + tojson(farFutureTS) + ", date:" + tsToDate(farFutureTS));
    // We do an update in case there is a minvalid document on the primary already.
    // If the doc doesn't exist then upsert:true will create it, and the writeConcern ensures
    // that update returns details of the write, like whether an update or insert was performed.
    printjson(
        assert.writeOK(mMinvalid.update({},
                                        {ts: farFutureTS, t: NumberLong(-1), begin: primaryOpTime},
                                        {upsert: true, writeConcern: {w: 1}})));

    jsTest.log("restart primary");
    replTest.restart(master);
    printjson(sLocal.adminCommand("isMaster"));
    replTest.waitForState(master, ReplSetTest.State.RECOVERING, 90000);

    // Slave is now master... so do a write to get a minvalid entry on the secondary.
    assert.writeOK(replTest.getPrimary().getDB("test").foo.save({}, {writeConcern: {w: 3}}));

    assert.soon(function() {
        var mv;
        try {
            mv = mMinvalid.findOne();
        } catch (e) {
            return false;
        }
        var msg = "ts !=, " + farFutureTS + "(" + tsToDate(farFutureTS) + "), mv:" + tojson(mv) +
            " - " + tsToDate(mv.ts);
        assert.eq(farFutureTS, mv.ts, msg);
        return true;
    });

    // Shut down the set and finish the test.
    replTest.stopSet();
})();