summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/shell_gossip_cluster_time.js
blob: 462ad7e34da4be1f836a587b6caef2fc9753fe73 (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
/**
 * Tests that the mongo shell gossips the greater of the client's clusterTime and the session's
 * clusterTime.
 * @tags: [requires_replication]
 */
(function() {
    "use strict";

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

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

    const primary = rst.getPrimary();

    const session1 = primary.startSession();
    const session2 = primary.startSession();

    const db = primary.getDB("test");
    const coll = db.shell_gossip_cluster_time;

    function testCommandGossipedWithClusterTime(func, expectedClusterTime) {
        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 (expectedClusterTime === undefined) {
            assert(!cmdObjSeen.hasOwnProperty("$clusterTime"),
                   "Expected operation " + tojson(cmdObjSeen) +
                       " to not have a $clusterTime object: " + func.toString());
        } else {
            assert(cmdObjSeen.hasOwnProperty("$clusterTime"),
                   "Expected operation " + tojson(cmdObjSeen) + " to have a $clusterTime object: " +
                       func.toString());

            assert(bsonBinaryEqual(expectedClusterTime, cmdObjSeen.$clusterTime));
        }
    }

    assert(
        session1.getClusterTime() === undefined,
        "session1 has yet to be used, but has clusterTime: " + tojson(session1.getClusterTime()));
    assert(
        session2.getClusterTime() === undefined,
        "session2 has yet to be used, but has clusterTime: " + tojson(session2.getClusterTime()));

    // Advance the clusterTime outside of either of the sessions.
    testCommandGossipedWithClusterTime(function() {
        assert.writeOK(coll.insert({}));
    }, primary.getClusterTime());

    assert(
        session1.getClusterTime() === undefined,
        "session1 has yet to be used, but has clusterTime: " + tojson(session1.getClusterTime()));
    assert(
        session2.getClusterTime() === undefined,
        "session2 has yet to be used, but has clusterTime: " + tojson(session2.getClusterTime()));

    // Performing an operation with session1 should use the highest clusterTime seen by the client
    // since session1 hasn't been used yet.
    testCommandGossipedWithClusterTime(function() {
        const coll = session1.getDatabase("test").mycoll;
        assert.writeOK(coll.insert({}));
    }, primary.getClusterTime());

    assert.eq(session1.getClusterTime(), primary.getClusterTime());

    testCommandGossipedWithClusterTime(function() {
        const coll = session1.getDatabase("test").mycoll;
        assert.writeOK(coll.insert({}));
    }, session1.getClusterTime());

    assert(
        session2.getClusterTime() === undefined,
        "session2 has yet to be used, but has clusterTime: " + tojson(session2.getClusterTime()));

    primary.resetClusterTime_forTesting();
    assert(primary.getClusterTime() === undefined,
           "client's cluster time should have been reset, but has clusterTime: " +
               tojson(primary.getClusterTime()));

    // Performing an operation with session2 should use the highest clusterTime seen by session2
    // since the client's clusterTime has been reset.
    session2.advanceClusterTime(session1.getClusterTime());
    testCommandGossipedWithClusterTime(function() {
        const coll = session2.getDatabase("test").mycoll;
        assert.writeOK(coll.insert({}));
    }, session2.getClusterTime());

    assert.eq(session2.getClusterTime(), primary.getClusterTime());

    primary.resetClusterTime_forTesting();
    assert(primary.getClusterTime() === undefined,
           "client's cluster time should have been reset, but has clusterTime: " +
               tojson(primary.getClusterTime()));

    // Performing an operation with session2 should use the highest clusterTime seen by session2
    // since the highest clusterTime seen by session1 is behind that of session2's.
    primary.advanceClusterTime(session1.getClusterTime());
    testCommandGossipedWithClusterTime(function() {
        const coll = session2.getDatabase("test").mycoll;
        assert.writeOK(coll.insert({}));
    }, session2.getClusterTime());

    rst.stopSet();
})();