summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/background_validation_repl.js
blob: f567283b93ec76ab776a7a79cc5f4d964648db18 (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
/**
 * Tests the validate command with {background:true} in a replica set.
 *
 * Checks that {full:true} cannot be run with {background:true}.
 * Checks that {background:true} runs.
 * Checks that {background:true} can run concurrently with CRUD ops on the same collection.
 *
 * @tags: [
 *   # inMemory does not have checkpoints; background validation only runs on a checkpoint.
 *   requires_persistence,
 *   # A failpoint is set that only exists on the mongod.
 *   assumes_against_mongod_not_mongos,
 *   # A failpoint is set against the primary only.
 *   does_not_support_stepdowns,
 *   # Checkpoint cursors cannot be open in lsm.
 *   does_not_support_wiredtiger_lsm,
 *   requires_replication,
 * ]
 */

(function() {
'use strict';

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

const dbName = "db_background_validation_repl";
const collName = "coll_background_validation_repl";

// Starts and returns a replica set.
const initTest = () => {
    const replSet = new ReplSetTest({nodes: 1, name: "rs"});
    replSet.startSet();
    replSet.initiate();
    const primary = replSet.getPrimary();

    let testColl = primary.getDB(dbName)[collName];
    testColl.drop();
    return replSet;
};

const doTest = replSet => {
    /*
     * Create some indexes and insert some data, so we can validate them more meaningfully.
     */
    const testDB = replSet.getPrimary().getDB(dbName);
    const testColl = testDB[collName];
    assert.commandWorked(testColl.createIndex({a: 1}));
    assert.commandWorked(testColl.createIndex({b: 1}));
    assert.commandWorked(testColl.createIndex({c: 1}));

    const numDocs = 100;
    for (let i = 0; i < numDocs; ++i) {
        assert.commandWorked(testColl.insert({a: i, b: i, c: i}));
    }

    /**
     * Ensure {full:true} and {background:true} cannot be run together.
     */
    assert.commandFailedWithCode(testColl.validate({background: true, full: true}),
                                 ErrorCodes.InvalidOptions);

    assert.commandWorked(testDB.adminCommand({fsync: 1}));

    // Check that {backround:true} is successful.
    let res = testColl.validate({background: true});
    assert.commandWorked(res);
    assert(res.valid, "Validate cmd with {background:true} failed: " + tojson(res));

    /*
     * Test background validation with concurrent CRUD operations.
     */

    // Set a failpoint in the background validation code to pause validation while holding a
    // collection lock.
    let failPoint = configureFailPoint(testDB, "pauseCollectionValidationWithLock");

    jsTest.log(`Starting parallel shell on port ${replSet.getPrimary().port}`);
    // Start an asynchronous thread to run collection validation with {background:true}.
    // Ensure we can perform multiple collection validations on the same collection
    // concurrently.
    let awaitValidateCommand = startParallelShell(function() {
        const asyncTestDB = db.getSiblingDB("db_background_validation_repl");
        const asyncTestColl = asyncTestDB.coll_background_validation_repl;
        const validateRes = asyncTestColl.validate({background: true});
        assert.commandWorked(
            validateRes, "asynchronous background validate command failed: " + tojson(validateRes));
        assert(validateRes.valid,
               "asynchronous background validate command was not valid: " + tojson(validateRes));
    }, replSet.getPrimary().port);

    // Wait for background validation command to start.
    jsTest.log("Waiting for failpoint to hit...");
    failPoint.wait();

    // Check that CRUD ops are succesful while validation is in progress.
    assert.commandWorked(testColl.remove({a: 1, b: 1, c: 1}));
    assert.commandWorked(testColl.insert({a: 1, b: 1, c: 1, d: 100}));
    assert.commandWorked(testColl.update({d: 100}, {"e": "updated"}));
    let docRes = testColl.find({"e": "updated"});
    assert.eq(1,
              docRes.toArray().length,
              "expected to find a single document, found: " + tojson(docRes.toArray()));

    // Clear the failpoint and make sure the validate command was successful.
    failPoint.off();
    awaitValidateCommand();

    /**
     * Verify everything is still OK by running foreground validation.
     */
    res = testColl.validate({background: false});
    assert.commandWorked(res);
    assert(res.valid, "Validate cmd with {background:true} failed: " + tojson(res));
    assert.eq(res.nIndexes, 4, "Expected 4 indexes: " + tojson(res));
    assert.eq(res.nrecords, numDocs, "Expected " + numDocs + " collection records:" + tojson(res));
    assert.eq(res.keysPerIndex._id_,
              numDocs,
              "Expected " + numDocs + " _id index records: " + tojson(res));
    assert.eq(res.keysPerIndex.a_1,
              numDocs,
              "Expected " + numDocs + " a_1 index records: " + tojson(res));
    assert.eq(res.keysPerIndex.b_1,
              numDocs,
              "Expected " + numDocs + " b_1 index records: " + tojson(res));
    assert.eq(res.keysPerIndex.c_1,
              numDocs,
              "Expected " + numDocs + " c_1 index records: " + tojson(res));
};

const replSet = initTest();
doTest(replSet);
replSet.stopSet();
})();