summaryrefslogtreecommitdiff
path: root/jstests/replsets/storage_commit_out_of_order.js
blob: d32a3553dbf3a96a5043f88dab5e8ad9bd446fa9 (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
/**
 * Tests that single voting primaries can commit majority writes when they storage-commit out of
 * order. This test first inserts a document to set the last applied optime, all committed
 * timestamp, and stable timestamp. It then spawns 'n' threads and holds them behind a barrier. Once
 * the threads are all waiting at the barrier, the threads all do a w:majority insert. We turn on a
 * fail point that will block the first thread to receive an optime from the optime generator for a
 * few seconds while the other threads get later optimes and commit their inserts.  The hung thread
 * is released after a few seconds and asserts that its write concern can be satisfied.
 */
(function() {
    'use strict';

    load('jstests/libs/parallelTester.js');

    const rst = new ReplSetTest({nodes: 1});
    rst.startSet();
    rst.initiate();
    const dbName = 'storage_commit_out_of_order';
    const collName = 'foo';
    const numThreads = 2;
    const primary = rst.getPrimary();
    const coll = primary.getDB(dbName).getCollection(collName);

    /**
     * Waits for the provided latch to reach 0 and then does a single w:majority insert.
     */
    const majorityInsert = function(testData, num, host, dbName, collName, latch) {
        // Pass the TestData into the new thread so that the thread can inherit the auth
        // credentials in the auth passthrough suites.
        TestData = testData;

        const m = new Mongo(host);
        latch.countDown();
        while (latch.getCount() > 0) {
            // do nothing
        }
        return m.getDB(dbName).runCommand({
            insert: collName,
            documents: [{b: num}],
            writeConcern: {w: 'majority', wtimeout: ReplSetTest.kDefaultTimeoutMS}
        });
    };

    assert.commandWorked(primary.setLogLevel(2, 'replication'));
    assert.writeOK(coll.insert(
        {a: 1}, {writeConcern: {w: 'majority', wtimeout: ReplSetTest.kDefaultTimeoutMS}}));

    // Turn on a fail point to force the first thread to receive an optime from the optime
    // generator to wait a few seconds before storage-committing the insert.
    assert.commandWorked(primary.adminCommand({
        configureFailPoint: 'sleepBetweenInsertOpTimeGenerationAndLogOp',
        mode: {times: 1},
        data: {waitForMillis: 3000}
    }));

    // Start a bunch of threads. They will block waiting on the latch to hit 0.
    const t = [];
    const counter = new CountDownLatch(numThreads + 1);
    for (let i = 0; i < numThreads; ++i) {
        t[i] = new ScopedThread(
            majorityInsert, TestData, i, coll.getMongo().host, dbName, collName, counter);
        t[i].start();
    }

    // Release the threads with the latch once they are all blocked on it.
    jsTestLog('All threads started.');
    assert.soon(() => counter.getCount() === 1);
    jsTestLog('All threads at barrier.');
    counter.countDown();
    jsTestLog('All threads finishing.');

    // Wait for all threads to complete and ensure they succeeded.
    for (let i = 0; i < numThreads; ++i) {
        t[i].join();
        assert.commandWorked(t[i].returnData());
    }

    rst.stopSet();
}());