summaryrefslogtreecommitdiff
path: root/jstests/libs/override_methods/set_majority_read_and_write_concerns.js
blob: 232d97e65628614de9c324bf4834553f78da0b5e (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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/**
 * Use prototype overrides to set a read concern of "majority" and a write concern of "majority"
 * while running core tests.
 */
(function() {
    "use strict";
    var defaultWriteConcern = {
        w: "majority",
        // Use a "signature" value that won't typically match a value assigned in normal use.
        wtimeout: 60321
    };
    var defaultReadConcern = {level: "majority"};

    var originalDBQuery = DBQuery;

    DBQuery = function(mongo, db, collection, ns, query, fields, limit, skip, batchSize, options) {
        if (ns.endsWith("$cmd")) {
            if (query.hasOwnProperty("writeConcern") &&
                bsonWoCompare(query.writeConcern, defaultWriteConcern) !== 0) {
                jsTestLog("Warning: DBQuery overriding existing writeConcern of: " +
                          tojson(query.writeConcern));
                query.writeConcern = defaultWriteConcern;
            }
        }

        return originalDBQuery.apply(this, arguments);
    };

    DBQuery.Option = originalDBQuery.Option;

    var originalStartParallelShell = startParallelShell;
    startParallelShell = function(jsCode, port, noConnect) {
        var newCode;
        var overridesFile = "jstests/libs/override_methods/set_majority_read_and_write_concerns.js";

        if (typeof(jsCode) === "function") {
            // Load the override file and immediately invoke the supplied function.
            // clang-format off
            newCode = `load("${overridesFile}"); (${jsCode})();`;
            // clang-format on
        } else {
            // clang-format off
            newCode = `load("${overridesFile}"); ${jsCode};`;
            // clang-format on
        }

        return originalStartParallelShell(newCode, port, noConnect);
    };

    DB.prototype._runCommandImpl = function(dbName, obj, options) {
        var cmdName = "";
        for (var fieldName in obj) {
            cmdName = fieldName;
            break;
        }

        // These commands directly support a writeConcern argument.
        var commandsToForceWriteConcern = [
            "applyOps",
            "authSchemaUpgrade",
            "createRole",
            "createUser",
            "delete",
            "dropAllRolesFromDatabase",
            "dropAllUsersFromDatabase",
            "dropRole",
            "dropUser",
            "findAndModify",
            "findandmodify",
            "grantPrivilegesToRole",
            "grantRolesToRole",
            "grantRolesToUser",
            "insert",
            "revokeRolesFromRole",
            "revokeRolesFromUser",
            "update",
            "updateRole",
            "updateUser",
        ];

        // These commands do writes but do not support a writeConcern argument. Emulate it with a
        // getLastError command.
        var commandsToEmulateWriteConcern = [
            "createIndexes",
        ];

        // These are reading commands that support majority readConcern.
        var commandsToForceReadConcern = [
            "count",
            "distinct",
            "find",
            "geoNear",
            "geoSearch",
            "group",
        ];

        var forceWriteConcern = Array.contains(commandsToForceWriteConcern, cmdName);
        var emulateWriteConcern = Array.contains(commandsToEmulateWriteConcern, cmdName);
        var forceReadConcern = Array.contains(commandsToForceReadConcern, cmdName);

        if (cmdName === "aggregate") {
            // Aggregate can be either a read or a write depending on whether it has a $out stage.
            // $out is required to be the last stage of the pipeline.
            var stages = obj.pipeline;
            var hasOut = stages && (stages.length !== 0) && ('$out' in stages[stages.length - 1]);
            if (hasOut) {
                emulateWriteConcern = true;
            } else {
                forceReadConcern = true;
            }
        }

        if (forceWriteConcern) {
            if (obj.hasOwnProperty("writeConcern")) {
                if (bsonWoCompare(obj.writeConcern, defaultWriteConcern) !== 0) {
                    jsTestLog("Warning: _runCommandImpl overriding existing writeConcern of: " +
                              tojson(obj.writeConcern));
                    obj.writeConcern = defaultWriteConcern;
                }
            } else {
                obj.writeConcern = defaultWriteConcern;
            }

        } else if (forceReadConcern) {
            if (obj.hasOwnProperty("readConcern")) {
                if (bsonWoCompare(obj.readConcern, defaultReadConcern) !== 0) {
                    jsTestLog("Warning: _runCommandImpl overriding existing readConcern of: " +
                              tojson(obj.readConcern));
                    obj.readConcern = defaultReadConcern;
                }
            } else {
                obj.readConcern = defaultReadConcern;
            }
        }

        var res = this.getMongo().runCommand(dbName, obj, options);

        if (res.ok && emulateWriteConcern) {
            // We only emulate WriteConcern if the command succeeded to match the behavior of
            // commands that support WriteConcern.
            var gleCmd = Object.extend({getLastError: 1}, defaultWriteConcern);
            assert.commandWorked(this.getMongo().runCommand(dbName, gleCmd, options));
        }

        return res;
    };

    // Use a majority write concern if the operation does not specify one.
    DBCollection.prototype.getWriteConcern = function() {
        return new WriteConcern(defaultWriteConcern);
    };

})();