summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/temporarily_unavailable_error.js
blob: d70841733c81711d41dcf9da148a3dd8b4bff7de (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
/**
 * Validate that under significant WiredTiger cache pressure an operation can fail
 * with TemporarilyUnavailable error.
 *
 * @tags: [
 *   // Exclude in-memory engine, rollbacks due to pinned cache content rely on eviction.
 *   requires_journaling,
 *   requires_persistence,
 *   requires_replication,
 *   requires_wiredtiger,
 * ]
 */
(function() {
"use strict";

const replSet = new ReplSetTest({
    nodes: 1,
    nodeOptions: {
        wiredTigerCacheSizeGB: 0.256,
        setParameter: {
            logComponentVerbosity: tojson({control: 1, write: 1}),
            // Lower these values from the defaults to speed up the test.
            temporarilyUnavailableMaxRetries: 3,
            temporarilyUnavailableBackoffBaseMs: 10,
        }
    },
});
replSet.startSet();
replSet.initiate();
const db = replSet.getPrimary().getDB("test");

// Generate a workload pinning enough dirty data in cache that causes WiredTiger
// to roll back transactions. This workload is adapted from the reproducer in the
// SERVER-61909 ticket description.
assert.commandWorked(db.c.createIndex({x: "text"}));
let doc = {x: []};
for (let j = 0; j < 50000; j++)
    doc.x.push("" + Math.random() + Math.random());

(function temporarilyUnavailableNonTransaction() {
    let caughtTUerror = false;
    let attempts;
    for (attempts = 1; attempts <= 20; attempts++) {
        print("temporarilyUnavailableNonTransaction attempt " + attempts);
        const ret = db.c.insert(doc);

        if (ret["nInserted"] === 1) {
            // The write succeeded.
            continue;
        }
        assert.eq(0, ret["nInserted"]);
        assert.commandFailedWithCode(ret, ErrorCodes.TemporarilyUnavailable);
        caughtTUerror = true;
        jsTestLog("returned the expected TemporarilyUnavailable code at attempt " + attempts);
        break;
    }

    assert(caughtTUerror,
           "did not return the expected TemporarilyUnavailable error after " + (attempts - 1) +
               " attempts");
})();

(function temporarilyUnavailableInTransactionIsConvertedToWriteConflict() {
    // Inside a transaction, TemporarilyUnavailable errors should be converted to
    // WriteConflicts and tagged as TransientTransactionErrors.
    let caughtWriteConflict = false;
    let attempts;
    let ret;
    for (attempts = 1; attempts <= 20; attempts++) {
        print("temporarilyUnavailableInTransactionIsConvertedToWriteConflict attempt " + attempts);
        const session = db.getMongo().startSession();
        session.startTransaction();
        const sessionDB = session.getDatabase('test');
        ret = sessionDB.c.insert(doc);

        if (ret["nInserted"] === 1) {
            // The write succeeded.
            session.commitTransaction();
            continue;
        }
        assert.commandFailedWithCode(
            ret, ErrorCodes.WriteConflict, "Did not get WriteConflict. Result: " + tojson(ret));
        assert(ret.hasOwnProperty("errorLabels"), "missing errorLabels. Result: " + tojson(ret));
        assert.contains("TransientTransactionError",
                        ret.errorLabels,
                        "did not find TransientTransaction error label. Result: " + tojson(ret));
        caughtWriteConflict = true;
        jsTestLog("returned the expected WriteConflict code at attempt " + attempts);
        session.abortTransaction();
        break;
    }

    assert(caughtWriteConflict,
           "did not return the expected WriteConflict error after " + (attempts - 1) +
               " attempts. Result: " + tojson(ret));
})();

replSet.stopSet();
})();