summaryrefslogtreecommitdiff
path: root/jstests/disk/wt_repair_corrupt_metadata.js
blob: 46dd25391147a4d00e3c5050dec99e362bf6cf86 (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
/**
 * Tests that --repair on WiredTiger correctly and gracefully handles corrupt metadata files.
 * This test should not run on debug builds because WiredTiger's diagnostic mode is enabled.
 *
 * @tags: [requires_wiredtiger,requires_journaling]
 */

(function() {

load('jstests/disk/libs/wt_file_helper.js');

const baseName = "wt_repair_corrupt_metadata";
const collName = "test";
const dbpath = MongoRunner.dataPath + baseName + "/";

/**
 * This test runs repair using a version of the WiredTiger.turtle file that has checkpoint
 * information before the collection was created. The turtle file contains checkpoint
 * information about the WiredTiger.wt file, so if these two files become out of sync,
 * WiredTiger will have to attempt a salvage operation on the .wt file and rebuild the .turtle
 * file.
 *
 * The expectation is that the metadata salvage will be successful, and that the collection will
 * be recreated with all of its data.
 */
let runTest = function(mongodOptions) {
    resetDbpath(dbpath);
    jsTestLog("Running test with args: " + tojson(mongodOptions));

    const turtleFile = dbpath + "WiredTiger.turtle";
    const turtleFileWithoutCollection = dbpath + "WiredTiger.turtle.1";

    let mongod = startMongodOnExistingPath(dbpath, mongodOptions);
    // Unfortunately using --nojournal triggers a WT_PANIC and aborts in debug builds, which the
    // following test case can exercise.
    // TODO: This return can be removed once WT-4310 is completed.
    let isDebug = mongod.getDB(baseName).adminCommand('buildInfo').debug;
    if (isDebug) {
        jsTestLog("Skipping test case because this is a debug build");
        MongoRunner.stopMongod(mongod);
        return;
    }

    // Force a checkpoint and make a copy of the turtle file.
    assert.commandWorked(mongod.getDB(baseName).adminCommand({fsync: 1}));
    jsTestLog("Making copy of metadata file before creating the collection: " +
              turtleFileWithoutCollection);
    copyFile(turtleFile, turtleFileWithoutCollection);

    let testColl = mongod.getDB(baseName)[collName];
    assert.commandWorked(testColl.insert({a: 1}));

    // Force another checkpoint before a clean shutdown.
    assert.commandWorked(mongod.getDB(baseName).adminCommand({fsync: 1}));
    MongoRunner.stopMongod(mongod);

    // Guarantee the turtle files changed between checkpoints.
    assert.neq(md5sumFile(turtleFileWithoutCollection), md5sumFile(turtleFile));

    jsTestLog("Replacing metadata file with a version before the collection existed.");
    removeFile(turtleFile);
    copyFile(turtleFileWithoutCollection, turtleFile);

    // This test characterizes the current WiredTiger salvage behaviour, which may be subject to
    // change in the future. See SERVER-41667.
    assertRepairSucceeds(dbpath, mongod.port, mongodOptions);

    mongod = startMongodOnExistingPath(dbpath, mongodOptions);
    testColl = mongod.getDB(baseName)[collName];

    // The collection exists despite using an older turtle file because salvage is able to find
    // the table in the WiredTiger.wt file.
    assert(testColl.exists());
    // We can assert that the data exists because the salvage only took place on the metadata,
    // not the data.
    assert.eq(testColl.find({}).itcount(), 1);
    MongoRunner.stopMongod(mongod);

    // Corrupt the .turtle file in a very specific way such that the log sequence numbers are
    // invalid.
    if (mongodOptions.hasOwnProperty('journal')) {
        // TODO: This return can be removed once WT-4459 is completed.
        if (_isAddressSanitizerActive()) {
            jsTestLog("Skipping log file corruption because the address sanitizer is active.");
            return;
        }

        jsTestLog("Corrupting log file metadata");

        let data = cat(turtleFile, true /* useBinaryMode */);
        let re = /checkpoint_lsn=\(([0-9,]+)\)/g;
        let newData = data.replace(re, "checkpoint_lsn=(1,2)");

        print('writing data to new turtle file: \n' + newData);
        removeFile(turtleFile);
        writeFile(turtleFile, newData, true /* useBinaryMode */);

        assertRepairSucceeds(dbpath, mongod.port, mongodOptions);

        mongod = startMongodOnExistingPath(dbpath, mongodOptions);
        testColl = mongod.getDB(baseName)[collName];

        // The collection exists despite using a salvaged turtle file because salvage is able to
        // find the table in the WiredTiger.wt file.
        assert(testColl.exists());

        // We can assert that the data exists because the salvage only took place on the
        // metadata, not the data.
        assert.eq(testColl.find({}).itcount(), 1);
        MongoRunner.stopMongod(mongod);
    }
};

// Repair may behave differently with journaling enabled or disabled, but the end result should
// be the same.
runTest({journal: ""});
runTest({nojournal: ""});
})();