summaryrefslogtreecommitdiff
path: root/jstests/replsets/reconstruct_prepared_transactions_initial_sync_index_build.js
blob: 84b65d831a44a5daaa32bc22e9a95181e0566e69 (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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/**
 * Tests that initial sync successfully applies a prepare oplog entry during oplog application phase
 * of initial sync.  Additionally, we will test that a background index build interleaves without
 * hanging.
 *
 * @tags: [
 *   uses_prepare_transaction,
 *   uses_transactions,
 * ]
 */

(function() {
"use strict";

load("jstests/core/txns/libs/prepare_helpers.js");
load("jstests/libs/fail_point_util.js");
load('jstests/noPassthrough/libs/index_build.js');

const replTest = new ReplSetTest({nodes: 2});
replTest.startSet();

const config = replTest.getReplSetConfig();
// Increase the election timeout so that we do not accidentally trigger an election while the
// secondary is restarting.
config.settings = {
    "electionTimeoutMillis": 12 * 60 * 60 * 1000
};
replTest.initiate(config);

const primary = replTest.getPrimary();
let secondary = replTest.getSecondary();
// The default WC is majority and this test can't satisfy majority writes.
assert.commandWorked(primary.adminCommand(
    {setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}}));

const dbName = "test";
const collName = "reconstruct_prepared_transactions_initial_sync_index_build";
let testDB = primary.getDB(dbName);
let testColl = testDB.getCollection(collName);

assert.commandWorked(testColl.insert({_id: 0}));

jsTestLog("Restarting the secondary");

// Restart the secondary with startClean set to true so that it goes through initial sync. Also
// restart the node with a failpoint turned on that will pause initial sync. This way we can do
// some writes on the sync source while initial sync is paused and know that its operations
// won't be copied during collection cloning. Instead, the writes must be applied during oplog
// application.
replTest.stop(secondary, undefined /* signal */, {skipValidation: true});
secondary = replTest.start(
    secondary,
    {
        startClean: true,
        setParameter: {
            'failpoint.initialSyncHangDuringCollectionClone': tojson(
                {mode: 'alwaysOn', data: {namespace: testColl.getFullName(), numDocsToClone: 1}}),
            'numInitialSyncAttempts': 1
        }
    },
    true /* wait */);

// Wait for failpoint to be reached so we know that collection cloning is paused.
assert.commandWorked(secondary.adminCommand({
    waitForFailPoint: "initialSyncHangDuringCollectionClone",
    timesEntered: 1,
    maxTimeMS: kDefaultWaitForFailPointTimeout
}));

jsTestLog("Running operations while collection cloning is paused");

// Perform writes while collection cloning is paused so that we know they must be applied during
// the oplog application stage of initial sync.
assert.commandWorked(testColl.insert({_id: 1, a: 1}));

// Make the index build hang on the primary so that only a startIndexBuild oplog entry is
// replicated and initial sync gets to the prepared-txn reconstruct stage with the index build
// still running.
jsTest.log("Hanging index build on the primary node");
IndexBuildTest.pauseIndexBuilds(primary);

jsTest.log("Beginning index build");
let awaitIndexBuild = IndexBuildTest.startIndexBuild(primary, testColl.getFullName(), {a: 1});

let session = primary.startSession();
let sessionDB = session.getDatabase(dbName);
const sessionColl = sessionDB.getCollection(collName);

jsTestLog("Preparing the transaction");

// Prepare a transaction while collection cloning is paused so that its oplog entry must be
// applied during the oplog application phase of initial sync.
session.startTransaction();
assert.commandWorked(sessionColl.update({_id: 1, a: 1}, {_id: 1, a: 2}));
const prepareTimestamp = PrepareHelpers.prepareTransaction(session, {w: 1});

jsTestLog("Resuming initial sync");

// Resume initial sync.
assert.commandWorked(secondary.adminCommand(
    {configureFailPoint: "initialSyncHangDuringCollectionClone", mode: "off"}));

// Unblock index build.
// Let the primary finish its index build and replicate a commit to the secondary.
IndexBuildTest.resumeIndexBuilds(primary);

// Wait for the secondary to complete initial sync.
replTest.awaitSecondaryNodes();

jsTestLog("Initial sync completed");

secondary.setSecondaryOk();
const secondaryColl = secondary.getDB(dbName).getCollection(collName);

// Make sure that while reading from the node that went through initial sync, we can't read
// changes to the documents from the prepared transaction after initial sync. Also, make
// sure that the writes that happened when collection cloning was paused happened.
const res = secondaryColl.find().sort({_id: 1}).toArray();
assert.eq(res, [{_id: 0}, {_id: 1, a: 1}], res);

// Wait for the prepared transaction oplog entry to be majority committed before committing the
// transaction.
PrepareHelpers.awaitMajorityCommitted(replTest, prepareTimestamp);

jsTestLog("Committing the transaction");

assert.commandWorked(PrepareHelpers.commitTransaction(session, prepareTimestamp));
replTest.awaitReplication();
awaitIndexBuild();

// Make sure that we can see the data from the committed transaction on the secondary.
assert.docEq(secondaryColl.findOne({_id: 1}), {_id: 1, a: 2});

replTest.stopSet();
})();