summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/read_concern_snapshot_catalog_invalidation.js
blob: ad8ce3a6337e486d8ca33a3bb17d036da0721643 (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
// Tests that snapshot reads return an error when accessing a collection whose metadata is invalid
// for the snapshot's point in time.
// @tags: [uses_transactions]
(function() {
    "use strict";

    const kDbName = "test";
    const kCollName = "coll";

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

    const testDB = rst.getPrimary().getDB(kDbName);
    const adminDB = testDB.getSiblingDB("admin");
    const coll = testDB.getCollection(kCollName);

    function waitForOp(curOpFilter) {
        assert.soon(
            function() {
                const res = adminDB.aggregate([{$currentOp: {}}, {$match: curOpFilter}]).toArray();
                if (res.length === 1) {
                    return true;
                }
                return false;
            },
            function() {
                return "Failed to find operation in $currentOp output: " +
                    tojson(adminDB.aggregate([{$currentOp: {}}]).toArray());
            });
    }

    function testCommand(cmd, curOpFilter) {
        coll.drop({writeConcern: {w: "majority"}});
        assert.commandWorked(testDB.runCommand({
            createIndexes: kCollName,
            indexes:
                [{key: {haystack: "geoHaystack", a: 1}, name: "haystack_geo", bucketSize: 1}]
        }));
        assert.commandWorked(coll.insert({x: 1}, {writeConcern: {w: "majority"}}));

        // Start a command with readConcern "snapshot" that hangs after establishing a storage
        // engine transaction.
        assert.commandWorked(testDB.adminCommand(
            {configureFailPoint: "hangAfterPreallocateSnapshot", mode: "alwaysOn"}));

        const awaitCommand = startParallelShell(
            "const session = db.getMongo().startSession();" +
                "const sessionDb = session.getDatabase('test');" +
                "session.startTransaction({readConcern: {level: 'snapshot'}});" +
                "const res = sessionDb.runCommand(" + tojson(cmd) + ");" +
                "assert.commandFailedWithCode(res, ErrorCodes.SnapshotUnavailable);" +
                "assert.eq(res.errorLabels, ['TransientTransactionError']);" +
                "session.endSession();",
            rst.ports[0]);

        waitForOp(curOpFilter);

        // Create an index on the collection the command was executed against. This will move the
        // collection's minimum visible timestamp to a point later than the point-in-time referenced
        // by the transaction snapshot.
        assert.commandWorked(testDB.runCommand({
            createIndexes: kCollName,
            indexes: [{key: {x: 1}, name: "x_1"}],
            writeConcern: {w: "majority"}
        }));

        // Disable the hang and check for parallel shell success. Success indicates that the command
        // failed due to collection metadata invalidation.
        assert.commandWorked(
            testDB.adminCommand({configureFailPoint: "hangAfterPreallocateSnapshot", mode: "off"}));

        awaitCommand();
    }

    testCommand({aggregate: kCollName, pipeline: [], cursor: {}},
                {"command.aggregate": kCollName, "command.readConcern.level": "snapshot"});
    testCommand({delete: kCollName, deletes: [{q: {x: 1}, limit: 1}]},
                {"command.delete": kCollName, "command.readConcern.level": "snapshot"});
    testCommand({distinct: kCollName, key: "x"},
                {"command.distinct": kCollName, "command.readConcern.level": "snapshot"});
    testCommand({find: kCollName},
                {"command.find": kCollName, "command.readConcern.level": "snapshot"});
    testCommand({findAndModify: kCollName, query: {x: 1}, remove: true}, {
        "command.findAndModify": kCollName,
        "command.remove": true,
        "command.readConcern.level": "snapshot"
    });
    testCommand({findAndModify: kCollName, query: {x: 1}, update: {$set: {x: 2}}}, {
        "command.findAndModify": kCollName,
        "command.update.$set": {x: 2},
        "command.readConcern.level": "snapshot"
    });
    testCommand({geoSearch: kCollName, near: [0, 0], maxDistance: 1, search: {a: 1}},
                {"command.geoSearch": kCollName, "command.readConcern.level": "snapshot"});
    testCommand({insert: kCollName, documents: [{x: 1}]},
                {"command.insert": kCollName, "command.readConcern.level": "snapshot"});
    testCommand({update: kCollName, updates: [{q: {x: 1}, u: {$set: {x: 2}}}]},
                {"command.update": kCollName, "command.readConcern.level": "snapshot"});

    rst.stopSet();
})();