summaryrefslogtreecommitdiff
path: root/jstests/libs/override_methods/enable_sessions.js
blob: 9e4954013a0519b1329f7a62bbeb8f1594123a2c (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
/**
 * Enables sessions on the db object
 */
(function() {
    "use strict";

    var runCommandOriginal = Mongo.prototype.runCommand;
    var runCommandWithMetadataOriginal = Mongo.prototype.runCommandWithMetadata;
    var getDBOriginal = Mongo.prototype.getDB;
    var sessionMap = new WeakMap();

    let sessionOptions = {};
    if (typeof TestData !== "undefined" && TestData.hasOwnProperty("sessionOptions")) {
        sessionOptions = TestData.sessionOptions;
    }

    const driverSession = startSession(db.getMongo());
    db = driverSession.getDatabase(db.getName());
    sessionMap.set(db.getMongo(), driverSession);

    var originalStartParallelShell = startParallelShell;
    startParallelShell = function(jsCode, port, noConnect) {
        var newCode;
        var overridesFile = "jstests/libs/override_methods/enable_sessions.js";
        if (typeof(jsCode) === "function") {
            // Load the override file and immediately invoke the supplied function.
            newCode = `load("${overridesFile}"); (${jsCode})();`;
        } else {
            newCode = `load("${overridesFile}"); ${jsCode};`;
        }

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

    function startSession(conn) {
        const driverSession = conn.startSession(sessionOptions);
        // Override the endSession function to be a no-op so fuzzer doesn't accidentally end the
        // session.
        driverSession.endSession = Function.prototype;
        return driverSession;
    }

    // Override the runCommand to check for any command obj that does not contain a logical session
    // and throw an error.
    function runCommandWithLsidCheck(conn, dbName, cmdObj, func, funcArgs) {
        const cmdName = Object.keys(cmdObj)[0];

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

        if (!cmdObjUnwrapped.hasOwnProperty("lsid")) {
            // TODO: SERVER-30848 fixes getMore requests to use a session in the mongo shell.
            // Until that happens, we bypass throwing an error for getMore and only throw an error
            // for other requests not using sessions.
            if (cmdName !== "getMore") {
                throw new Error("command object does not have session id: " + tojson(cmdObj));
            }
        }
        return func.apply(conn, funcArgs);
    }

    Mongo.prototype.runCommand = function(dbName, commandObj, options) {
        return runCommandWithLsidCheck(this, dbName, commandObj, runCommandOriginal, arguments);
    };

    Mongo.prototype.runCommandWithMetadata = function(dbName, metadata, commandObj) {
        return runCommandWithLsidCheck(
            this, dbName, commandObj, runCommandWithMetadataOriginal, arguments);
    };

    // Override the getDB to return a db object with the correct driverSession. We use a WeakMap
    // to cache the session for each connection instance so we can retrieve the same session on
    // subsequent calls to getDB.
    Mongo.prototype.getDB = function(dbName) {
        if (!sessionMap.has(this)) {
            const session = startSession(this);
            sessionMap.set(this, session);
        }

        const db = getDBOriginal.apply(this, arguments);
        db._session = sessionMap.get(this);
        return db;
    };

})();