summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/index_failover_resolved_key_errors_after_stepdown.js
blob: 67647a34a6e7222900f143e9f582b38b7d0dbb8d (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
/**
 * Confirms that an index build is committed after step-up by a new primary when there are key
 * generation errors that are eventually resolved. This test orchestrates a scenario such that a
 * primary steps down before scanning a collection with invalid documents, and before receiving an
 * update that resolves the error. After step-up, the new primary will apply the update before
 * committing. The expected result is that the node, now primary, commits the index build for the
 * entire replica set.
 *
 * @tags: [
 *   requires_replication,
 * ]
 */
(function() {
"use strict";

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

const rst = new ReplSetTest({
    // We want at least two electable nodes.
    nodes: [
        {},
        {},
    ],
});
const nodes = rst.startSet();
rst.initiate();

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

// Insert a document that cannot be indexed because it causes a CannotIndexParallelArrays error
// code.
const badDoc = {
    _id: 0,
    a: [0, 1],
    b: [2, 3]
};
assert.commandWorked(coll.insert(badDoc));

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

// Start an index build on primary and secondary, but prevent the primary from scanning the
// collection. Do not stop the secondary; intentionally let it scan the invalid document, which we
// will resolve later.

// We are using this fail point to pause the index build before it starts the
// collection scan. This is important for this test because we are mutating the collection state
// before the index builder is able to observe the invalid geo document. By comparison,
// IndexBuildTest.pauseIndexBuilds() stalls the index build in the middle of the collection scan.
assert.commandWorked(testDB.adminCommand(
    {configureFailPoint: 'hangAfterSettingUpIndexBuildUnlocked', mode: 'alwaysOn'}));
// Prevent the index build from scanning on the secondary.
assert.commandWorked(secondaryDB.adminCommand(
    {configureFailPoint: 'hangAfterSettingUpIndexBuildUnlocked', mode: 'alwaysOn'}));
const createIdx = IndexBuildTest.startIndexBuild(primary, coll.getFullName(), {a: 1, b: 1});

IndexBuildTest.waitForIndexBuildToStart(secondaryDB);
IndexBuildTest.assertIndexesSoon(
    secondaryColl, 2, ["_id_"], ["a_1_b_1"], {includeBuildUUIDs: true});

// Step down the primary.
const stepDown = startParallelShell(() => {
    assert.commandWorked(db.adminCommand({"replSetStepDown": 60, "force": true}));
}, primary.port);

// Expect a failed createIndex command invocation in the parallel shell due to stepdown even though
// the index build will continue in the background.
const exitCode = createIdx({checkExitSuccess: false});
assert.neq(0, exitCode, 'expected shell to exit abnormally due to index build being terminated');
checkLog.containsJson(primary, 20444);

// Wait for stepdown to complete.
stepDown();

const newPrimary = rst.getPrimary();
const newPrimaryDB = newPrimary.getDB('test');
const newPrimaryColl = newPrimaryDB.getCollection('test');

// Ensure the old primary doesn't take over again.
assert.neq(primary.port, newPrimary.port);

// Resolve the key generation error so that the index build succeeds on the new primary.
assert.commandWorked(newPrimaryColl.update({_id: 0}, {a: 1, b: 1}));

// Unblock the index build on both nodes so they can finish.
assert.commandWorked(secondaryDB.adminCommand(
    {configureFailPoint: 'hangAfterSettingUpIndexBuildUnlocked', mode: 'off'}));
assert.commandWorked(
    testDB.adminCommand({configureFailPoint: 'hangAfterSettingUpIndexBuildUnlocked', mode: 'off'}));

// A new index should be present on the old primary after processing the commitIndexBuild oplog
// entry from the new primary.
IndexBuildTest.waitForIndexBuildToStop(testDB);
IndexBuildTest.assertIndexes(coll, 2, ['_id_', 'a_1_b_1']);

// Check that index was created on the new primary.
IndexBuildTest.waitForIndexBuildToStop(newPrimaryDB);
IndexBuildTest.assertIndexes(newPrimaryColl, 2, ['_id_', 'a_1_b_1']);

rst.stopSet();
})();