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
|
/**
* 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!
*/
(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.getMaster();
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, replTest.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, replTest.RECOVERING, 90000);
// Slave is now master... so do a write to get a minvalid entry on the secondary.
assert.writeOK(replTest.getMaster().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();
})();
|