summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/index_secondary_awaiting_primary_abort_crash_on_commit.js
blob: b1208086bf7b1df352608c4d09ffd09649154e8b (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
/**
 * When an index build encounters an error, it signals the primary to abort and waits for an abort
 * oplog entry to be replicated. If a commit entry is received instead, the secondary should crash.
 *
 * @tags: [
 *   requires_fcv_71,
 *   requires_replication,
 * ]
 */
(function() {
"use strict";

load('jstests/noPassthrough/libs/index_build.js');
load('jstests/libs/fail_point_util.js');

const rst = new ReplSetTest({
    nodes: [
        {},
        {
            // Disallow elections on secondary.
            rsConfig: {
                priority: 0,
            },
        },
    ]
});
rst.startSet();
rst.initiate();

const primary = rst.getPrimary();

const testDB = primary.getDB('test');
const coll = testDB.getCollection('test');

const secondary = rst.getSecondary();
const secondaryDB = secondary.getDB(testDB.getName());
const secondaryColl = secondaryDB.getCollection('test');

// Avoid optimization on empty colls.
assert.commandWorked(coll.insert({a: 1}));

// Pause the index builds on the secondary, using the 'hangAfterStartingIndexBuild' failpoint.
const failpointHangAfterInit = configureFailPoint(secondaryDB, "hangAfterInitializingIndexBuild");

// Block the primary before commit.
const failpointHangBeforeCommitPrimary = configureFailPoint(testDB, "hangIndexBuildBeforeCommit");

// Create the index and start the build. Set commitQuorum of 1 node explicitly, we want the primary
// to commit even if the secondary is failing.
const createIdx = IndexBuildTest.startIndexBuild(primary,
                                                 coll.getFullName(),
                                                 {a: 1},
                                                 {},
                                                 [ErrorCodes.InterruptedDueToReplStateChange],
                                                 /*commitQuorum: */ 1);

failpointHangAfterInit.wait();

// Extract the index build UUID. Use assertIndexesSoon to retry until the oplog applier is done with
// the entry, and the index is visible to listIndexes. The failpoint does not ensure this.
const buildUUID =
    IndexBuildTest
        .assertIndexesSoon(secondaryColl, 2, ['_id_'], ['a_1'], {includeBuildUUIDs: true})['a_1']
        .buildUUID;

const failSecondaryBuild =
    configureFailPoint(secondaryDB,
                       "failIndexBuildWithError",
                       {buildUUID: buildUUID, error: ErrorCodes.OutOfDiskSpace});

// Hang while in kAwaitPrimaryAbort, but before signaling the primary.
const failpointHangBeforeSignalingAbort =
    configureFailPoint(secondaryDB, "hangIndexBuildBeforeSignalingPrimaryForAbort");

// Unblock index builds, causing the failIndexBuildWithError failpoint to throw an error.
failpointHangAfterInit.off();

// Unblock primary commit.
failpointHangBeforeCommitPrimary.off();

let res;
assert.soon(function() {
    res = checkProgram(secondary.pid);
    return !res.alive;
});

assert.eq(MongoRunner.EXIT_ABORT, res.exitCode);

// Expect the secondary to crash. Depending on timing, this can be either because the secondary was
// waiting for a primary abort when a 'commitIndexBuild' is applied, or because the build fails and
// tries to request an abort while a 'commitIndexBuild' is being applied.
assert(rawMongoProgramOutput().match('Fatal assertion.*(7329403|7329407)'),
       'Receiving a commit from the primary for a failing index build should crash the secondary');

createIdx();

// Assert index exists on the primary.
IndexBuildTest.assertIndexes(coll, 2, ['_id_', 'a_1']);

TestData.skipCheckDBHashes = true;
rst.stopSet();
})();