summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/wt_nojournal_toggle.js
blob: 50d5483aa264dd2183e5ed27d71bd683ed61189a (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
/**
 * Tests that journaled write operations that have occurred since the last checkpoint are replayed
 * when the mongod is killed and restarted with --nojournal.
 */
(function() {
'use strict';

// Skip this test if not running with the "wiredTiger" storage engine.
if (jsTest.options().storageEngine && jsTest.options().storageEngine !== 'wiredTiger') {
    jsTest.log('Skipping test because storageEngine is not "wiredTiger"');
    return;
}

// Returns a function that primarily executes unjournaled inserts, but periodically does a
// journaled insert. If 'checkpoint' is true, then the fsync command is run to create a
// checkpoint prior to the mongod being terminated.
function insertFunctionFactory(checkpoint) {
    var insertFunction = function() {
        for (var iter = 0; iter < 1000; ++iter) {
            var bulk = db.nojournal.initializeUnorderedBulkOp();
            for (var i = 0; i < 100; ++i) {
                bulk.insert({unjournaled: i});
            }
            assert.writeOK(bulk.execute({j: false}));
            assert.writeOK(db.nojournal.insert({journaled: iter}, {writeConcern: {j: true}}));
            if (__checkpoint_template_placeholder__ && iter === 50) {
                assert.commandWorked(db.adminCommand({fsync: 1}));
            }
        }
    };

    return '(' +
        insertFunction.toString().replace('__checkpoint_template_placeholder__',
                                          checkpoint.toString()) +
        ')();';
}

function runTest(options) {
    var dbpath = MongoRunner.dataPath + 'wt_nojournal_toggle';
    resetDbpath(dbpath);

    // Start a mongod with journaling enabled.
    var conn = MongoRunner.runMongod({
        dbpath: dbpath,
        noCleanData: true,
        journal: '',
    });
    assert.neq(null, conn, 'mongod was unable to start up');

    // Run a mixture of journaled and unjournaled write operations against the mongod.
    var awaitShell = startParallelShell(insertFunctionFactory(options.checkpoint), conn.port);

    // After some journaled write operations have been performed against the mongod, send a
    // SIGKILL to the process to trigger an unclean shutdown.
    assert.soon(function() {
        var testDB = conn.getDB('test');
        var count = testDB.nojournal.count({journaled: {$exists: true}});
        if (count >= 100) {
            // We saw 100 journaled inserts, but visibility does not guarantee durability, so
            // do an extra journaled write to make all visible commits durable, before killing
            // the mongod.
            assert.writeOK(testDB.nojournal.insert({final: true}, {writeConcern: {j: true}}));
            MongoRunner.stopMongod(conn, 9, {allowedExitCode: MongoRunner.EXIT_SIGKILL});
            return true;
        }
        return false;
    }, 'the parallel shell did not perform at least 100 journaled inserts');

    var exitCode = awaitShell({checkExitSuccess: false});
    assert.neq(0, exitCode, 'expected shell to exit abnormally due to mongod being terminated');

    // Restart the mongod with journaling disabled.
    conn = MongoRunner.runMongod({
        dbpath: dbpath,
        noCleanData: true,
        nojournal: '',
    });
    assert.neq(null, conn, 'mongod was unable to restart after receiving a SIGKILL');

    var testDB = conn.getDB('test');
    assert.eq(1, testDB.nojournal.count({final: true}), 'final journaled write was not found');
    assert.lte(100,
               testDB.nojournal.count({journaled: {$exists: true}}),
               'journaled write operations since the last checkpoint were not replayed');

    var initialNumLogWrites = testDB.serverStatus().wiredTiger.log['log write operations'];
    assert.writeOK(testDB.nojournal.insert({a: 1}, {writeConcern: {fsync: true}}));
    assert.eq(initialNumLogWrites,
              testDB.serverStatus().wiredTiger.log['log write operations'],
              'journaling is still enabled even though --nojournal was specified');

    MongoRunner.stopMongod(conn);

    // Restart the mongod with journaling enabled.
    conn = MongoRunner.runMongod({
        dbpath: dbpath,
        noCleanData: true,
        journal: '',
    });
    assert.neq(null, conn, 'mongod was unable to start up after re-enabling journaling');

    // Change the database object to connect to the restarted mongod.
    testDB = conn.getDB('test');
    initialNumLogWrites = testDB.serverStatus().wiredTiger.log['log write operations'];

    assert.writeOK(testDB.nojournal.insert({a: 1}, {writeConcern: {fsync: true}}));
    assert.lt(initialNumLogWrites,
              testDB.serverStatus().wiredTiger.log['log write operations'],
              'journaling is still disabled even though --journal was specified');

    MongoRunner.stopMongod(conn);
}

// Operations from the journal should be replayed even when the mongod is terminated before
// anything is written to disk.
jsTest.log('Running the test without ever creating a checkpoint');
runTest({checkpoint: false});

// Repeat the test again, but ensure that some data is written to disk before the mongod is
// terminated.
jsTest.log('Creating a checkpoint part-way through running the test');
runTest({checkpoint: true});
})();