summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/query_yields_catch_index_corruption.js
blob: d05119e8b1e87921fdd5e1dc2d0338baf13fde69 (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
// @tags: [
//   requires_journaling,
//   requires_persistence,
// ]
(function() {
"use strict";

const dbName = "test";
const collName = "query_yields_catch_index_corruption";

const replSet = new ReplSetTest({nodes: 1});
replSet.startSet();
replSet.initiate();

const primary = replSet.getPrimary();

let db = primary.getDB(dbName);
assert.commandWorked(db.adminCommand({
    configureFailPoint: "skipUnindexingDocumentWhenDeleted",
    mode: "alwaysOn",
    data: {indexName: "a_1_b_1"}
}));

let coll = db.getCollection(collName);
assert.commandWorked(db.createCollection(collName, {writeConcern: {w: "majority"}}));
assert.commandWorked(coll.createIndex({a: 1, b: 1}));

// Corrupt the collection by inserting a document and then deleting it without deleting its index
// entry (thanks to the "skipUnindexingDocumentWhenDeleted" failpoint).
function createDanglingIndexEntry(doc) {
    assert.commandWorked(coll.insert(doc));
    assert.commandWorked(coll.remove(doc));

    // Validation should now fail.
    const validateRes = assert.commandWorked(coll.validate());
    assert.eq(false, validateRes.valid);

    // A query that accesses the now dangling index entry should fail with a
    // "DataCorruptionDetected" error. Most reads will not detect this problem because they ignore
    // prepare conflicts by default and that exempts them from checking this assertion. Only writes
    // and reads in multi-document transactions enforce prepare conflicts and should encounter this
    // assertion.
    assert.commandFailedWithCode(coll.update(doc, {$set: {c: 1}}),
                                 ErrorCodes.DataCorruptionDetected);

    const session = db.getMongo().startSession();
    const sessionDB = session.getDatabase(dbName);
    session.startTransaction();

    assert.throwsWithCode(() => {
        sessionDB[collName].find(doc).toArray();
    }, ErrorCodes.DataCorruptionDetected);
    session.abortTransaction_forTesting();
}

createDanglingIndexEntry({a: 1, b: 1});

// Fix the index by rebuilding it, and ensure that it validates.
assert.commandWorked(coll.dropIndex({a: 1, b: 1}));
assert.commandWorked(coll.createIndex({a: 1, b: 1}));

let validateRes = assert.commandWorked(coll.validate());
assert.eq(true, validateRes.valid, tojson(validateRes));

// Reintroduce the dangling index entry, and this time fix it using the "repair" flag.
createDanglingIndexEntry({a: 1, b: 1});

const dbpath = replSet.getDbPath(primary);
replSet.stopSet(MongoRunner.EXIT_CLEAN, true /* forRestart */, {skipValidation: true});

let mongod = MongoRunner.runMongod({dbpath: dbpath, noCleanData: true, repair: ""});
assert.eq(null, mongod, "Expect this to exit cleanly");

// Verify that the server starts up successfully after the repair.
mongod = MongoRunner.runMongod({dbpath: dbpath, noCleanData: true});
assert.neq(null, mongod, "mongod failed to start after repair");

db = mongod.getDB("test");
coll = db.getCollection(collName);

// Runs validate before shutting down.
MongoRunner.stopMongod(mongod);
})();