summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/transaction_api_commit_errors.js
blob: 1613599a78b26d1a20b3443e806c167add417938 (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
/**
 * Tests that the transaction API handles commit errors correctly.
 */
(function() {
"use strict";

load("jstests/libs/fail_point_util.js");

const kDbName = "testDb";
const kCollName = "testColl";

function makeSingleInsertTxn(doc) {
    return [{
        dbName: kDbName,
        command: {
            insert: kCollName,
            documents: [doc],
        }
    }];
}

function runTxn(conn, commandInfos) {
    return conn.adminCommand({testInternalTransactions: 1, commandInfos: commandInfos});
}

const st = new ShardingTest({config: 1, shards: 1});
const shardPrimary = st.rs0.getPrimary();

// Set up the test collection.
assert.commandWorked(st.s.getDB(kDbName)[kCollName].insert([{_id: 0}]));

//
// Error codes where the API should retry and eventually commit the transaction, either by retrying
// commit until it succeeds or retrying the entire transaction until it succeeds. Fail commands 10
// times to exhaust internal retries at layers below the transaction API.
//

// Retryable error. Note this error is not a NotPrimary error so it won't be rewritten by mongos.
let commitFailPoint =
    configureFailPoint(shardPrimary,
                       "failCommand",
                       {
                           errorCode: ErrorCodes.ReadConcernMajorityNotAvailableYet,
                           failCommands: ["commitTransaction"],
                           failInternalCommands: true,
                       },
                       {times: 10});
let res = assert.commandWorked(runTxn(st.s, makeSingleInsertTxn({_id: 1})));
commitFailPoint.off();

// No command error with a retryable write concern error.
commitFailPoint = configureFailPoint(
    shardPrimary,
    "failCommand",
    {
        writeConcernError:
            {code: NumberInt(ErrorCodes.ReadConcernMajorityNotAvailableYet), errmsg: "foo"},
        failCommands: ["commitTransaction"],
        failInternalCommands: true,
    },
    {times: 10});
res = assert.commandWorked(runTxn(st.s, makeSingleInsertTxn({_id: 2})));
commitFailPoint.off();

//
// Error codes where the API should not retry.
//

// Non-transient commit error with a non-retryable write concern error.
commitFailPoint = configureFailPoint(shardPrimary,
                                     "failCommand",
                                     {
                                         errorCode: ErrorCodes.InternalError,
                                         failCommands: ["commitTransaction"],
                                         failInternalCommands: true,
                                     },
                                     {times: 10});
res = assert.commandFailedWithCode(runTxn(st.s, makeSingleInsertTxn({_id: 3})),
                                   ErrorCodes.InternalError);
commitFailPoint.off();

// No commit error with a non-retryable write concern error.
commitFailPoint = configureFailPoint(
    shardPrimary,
    "failCommand",
    {
        writeConcernError: {code: NumberInt(ErrorCodes.InternalError), errmsg: "foo"},
        failCommands: ["commitTransaction"],
        failInternalCommands: true,
    },
    {times: 10});
// The internal transaction test command will rethrow a write concern error as a top-level error.
res = assert.commandFailedWithCode(runTxn(st.s, makeSingleInsertTxn({_id: 4})),
                                   ErrorCodes.InternalError);
commitFailPoint.off();

// Non-transient commit error that is normally transient. Note NoSuchTransaction is not transient
// with a write concern error, which is what this is meant to simulate. Also note the fail command
// fail point can't take both a write concern error and write concern error so we "cheat" and
// override the error labels.
commitFailPoint = configureFailPoint(shardPrimary,
                                     "failCommand",
                                     {
                                         errorCode: ErrorCodes.NoSuchTransaction,
                                         errorLabels: [],
                                         failCommands: ["commitTransaction"],
                                         failInternalCommands: true,
                                     },
                                     {times: 10});
res = assert.commandFailedWithCode(runTxn(st.s, makeSingleInsertTxn({_id: 5})),
                                   ErrorCodes.NoSuchTransaction);
commitFailPoint.off();

st.stop();
}());