summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/auto_retry_on_network_error.js
blob: 1c5f8465ebb3ec9cf4fb1606b3bd6d4fb43f78d8 (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
/**
 * Tests that the auto_retry_on_network_error.js override automatically retries commands on network
 * errors for commands run under a session.
 * @tags: [requires_replication]
 */
(function() {
"use strict";

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

if (!RetryableWritesUtil.storageEngineSupportsRetryableWrites(jsTest.options().storageEngine)) {
    jsTestLog("Retryable writes are not supported, skipping test");
    return;
}

TestData.networkErrorAndTxnOverrideConfig = {
    retryOnNetworkErrors: true
};
load('jstests/libs/override_methods/network_error_and_txn_override.js');
load("jstests/replsets/rslib.js");

function getThreadName(db) {
    let myUri = db.adminCommand({whatsmyuri: 1}).you;
    return db.getSiblingDB("admin")
        .aggregate([{$currentOp: {localOps: true}}, {$match: {client: myUri}}])
        .toArray()[0]
        .desc;
}

function failNextCommand(db, command) {
    let threadName = getThreadName(db);

    assert.commandWorked(db.adminCommand({
        configureFailPoint: "failCommand",
        mode: {times: 1},
        data: {
            closeConnection: true,
            failCommands: [command],
            threadName: threadName,
        }
    }));
}

const rst = new ReplSetTest({nodes: 1});
rst.startSet();

// awaitLastStableRecoveryTimestamp runs an 'appendOplogNote' command which is not retryable.
rst.initiateWithAnyNodeAsPrimary(
    null, "replSetInitiate", {doNotWaitForStableRecoveryTimestamp: true});

const dbName = "test";
const collName = "auto_retry";

// The override requires the connection to be run under a session. Use the replica set URL to
// allow automatic re-targeting of the primary on NotMaster errors.
const db = new Mongo(rst.getURL()).startSession({retryWrites: true}).getDatabase(dbName);

// Commands with no disconnections should work as normal.
assert.commandWorked(db.runCommand({ping: 1}));
assert.commandWorked(db.runCommandWithMetadata({ping: 1}, {}).commandReply);

// Read commands are automatically retried on network errors.
failNextCommand(db, "find");
assert.commandWorked(db.runCommand({find: collName}));

failNextCommand(db, "find");
assert.commandWorked(db.runCommandWithMetadata({find: collName}, {}).commandReply);

// Retryable write commands that can be retried succeed.
failNextCommand(db, "insert");
assert.writeOK(db[collName].insert({x: 1}));

failNextCommand(db, "insert");
assert.commandWorked(db.runCommandWithMetadata({
                           insert: collName,
                           documents: [{x: 2}, {x: 3}],
                           txnNumber: NumberLong(10),
                           lsid: {id: UUID()}
                       },
                                               {})
                         .commandReply);

// Retryable write commands that cannot be retried (i.e. no transaction number, no session id,
// or are unordered) throw.
failNextCommand(db, "insert");
assert.throws(function() {
    db.runCommand({insert: collName, documents: [{x: 1}, {x: 2}], ordered: false});
});

// The previous command shouldn't have been retried, so run a command to successfully re-target
// the primary, so the connection to it can be closed.
assert.commandWorked(db.runCommandWithMetadata({ping: 1}, {}).commandReply);

failNextCommand(db, "insert");
assert.throws(function() {
    db.runCommandWithMetadata({insert: collName, documents: [{x: 1}, {x: 2}], ordered: false}, {});
});

// getMore commands can't be retried because we won't know whether the cursor was advanced or
// not.
let cursorId = assert.commandWorked(db.runCommand({find: collName, batchSize: 0})).cursor.id;
failNextCommand(db, "getMore");
assert.throws(function() {
    db.runCommand({getMore: cursorId, collection: collName});
});

cursorId = assert.commandWorked(db.runCommand({find: collName, batchSize: 0})).cursor.id;
failNextCommand(db, "getMore");
assert.throws(function() {
    db.runCommandWithMetadata({getMore: cursorId, collection: collName}, {});
});

rst.stopSet();
})();