summaryrefslogtreecommitdiff
path: root/jstests/multiVersion/shell_retryable_writes_downgrade.js
blob: 854b468913e5f962e3f3aa57367d2eb62d7348c0 (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
/**
 * Tests that the mongo shell doesn't attempt to retry its write operations after downgrading from
 * 3.6 to 3.4.
 */
(function() {
    "use strict";

    load("jstests/replsets/rslib.js");

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

    const primary = rst.getPrimary();

    const db = primary.startSession({retryWrites: true}).getDatabase("test");
    const coll = db.shell_retryable_writes_downgrade;

    function testCommandCanBeRetried(func, {
        expectedLogicalSessionId: expectedLogicalSessionId = true,
        expectedTransactionNumber: expectedTransactionNumber = true
    } = {}) {
        const mongoRunCommandOriginal = Mongo.prototype.runCommand;

        const sentinel = {};
        let cmdObjSeen = sentinel;

        Mongo.prototype.runCommand = function runCommandSpy(dbName, cmdObj, options) {
            cmdObjSeen = cmdObj;
            return mongoRunCommandOriginal.apply(this, arguments);
        };

        try {
            assert.doesNotThrow(func);
        } finally {
            Mongo.prototype.runCommand = mongoRunCommandOriginal;
        }

        if (cmdObjSeen === sentinel) {
            throw new Error("Mongo.prototype.runCommand() was never called: " + func.toString());
        }

        let cmdName = Object.keys(cmdObjSeen)[0];

        // If the command is in a wrapped form, then we look for the actual command object inside
        // the query/$query object.
        if (cmdName === "query" || cmdName === "$query") {
            cmdObjSeen = cmdObjSeen[cmdName];
            cmdName = Object.keys(cmdObjSeen)[0];
        }

        if (expectedLogicalSessionId) {
            assert(cmdObjSeen.hasOwnProperty("lsid"),
                   "Expected operation " + tojson(cmdObjSeen) + " to have a logical session id: " +
                       func.toString());
        } else {
            assert(!cmdObjSeen.hasOwnProperty("lsid"),
                   "Expected operation " + tojson(cmdObjSeen) +
                       " to not have a logical session id: " + func.toString());
        }

        if (expectedTransactionNumber) {
            assert(cmdObjSeen.hasOwnProperty("txnNumber"),
                   "Expected operation " + tojson(cmdObjSeen) +
                       " to be assigned a transaction number since it can be retried: " +
                       func.toString());
        } else {
            assert(!cmdObjSeen.hasOwnProperty("txnNumber"),
                   "Expected operation " + tojson(cmdObjSeen) +
                       " to not be assigned a transaction number since it cannot be retried: " +
                       func.toString());
        }
    }

    testCommandCanBeRetried(function() {
        assert.writeOK(coll.insert({_id: "while fCV=3.6"}));
    });

    assert.commandWorked(db.adminCommand({setFeatureCompatibilityVersion: "3.4"}));

    // The server errors on lsid and txnNumber while in featureCompatibilityVersion=3.4.
    testCommandCanBeRetried(function() {
        assert.writeError(coll.insert({_id: "while fCV=3.4"}));
    });

    rst.restart(primary, {binVersion: "3.4"});
    rst.waitForMaster();

    assert(db.getSession().getOptions().shouldRetryWrites(),
           "Re-establishing the connection shouldn't change the state of the SessionOptions");

    // After downgrading to MongoDB 3.4, the mongo shell shouldn't attempt to automatically retry
    // write operations.
    assert.throws(function() {
        coll.insert({_id: "while in binVersion=3.4 and disconnected"});
    });

    // After downgrading to MongoDB 3.4, the mongo shell shouldn't inject an lsid or assign a
    // transaction number to its write requests.
    testCommandCanBeRetried(function() {
        assert.writeOK(coll.insert({_id: "while binVersion=3.4 and reconnected"}));
    }, {expectedLogicalSessionId: false, expectedTransactionNumber: false});

    rst.restart(primary, {binVersion: "latest", noReplSet: true});
    rst.waitForMaster();
    reconnect(primary);

    // When upgrading to MongoDB 3.6 but running as a stand-alone server, the mongo shell should
    // still assign a transaction number to its write requests (per the Driver's specification).
    testCommandCanBeRetried(function() {
        assert.writeError(
            coll.insert({_id: "while binVersion=3.6 as stand-alone and reconnected"}));
    });

    db.getSession().endSession();
    rst.stopSet();
})();