summaryrefslogtreecommitdiff
path: root/jstests/concurrency/fsm_workloads/kill_rooted_or.js
blob: 0ceb1f71359f99c9d99efa7599455981dc817391 (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
'use strict';

/**
 * kill_rooted_or.js
 *
 * Queries using a rooted $or predicate to cause plan selection to use the subplanner. Tests that
 * the subplanner correctly halts plan execution when the collection is dropped or a candidate index
 * is dropped.
 *
 * This workload was designed to reproduce SERVER-24761.
 */
load("jstests/concurrency/fsm_workload_helpers/assert_handle_fail_in_transaction.js");
var $config = (function() {
    // Use the workload name as the collection name, since the workload name is assumed to be
    // unique. Note that we choose our own collection name instead of using the collection provided
    // by the concurrency framework, because this workload drops its collection.
    var uniqueCollectionName = 'kill_rooted_or';

    var data = {
        collName: uniqueCollectionName,
        indexSpecs: [
            {a: 1},
            {a: 1, c: 1},
            {b: 1},
            {b: 1, c: 1},
        ],
        numDocs: 200,
    };

    var states = {
        query: function query(db, collNameUnused) {
            var cursor = db[this.collName].find({$or: [{a: 0}, {b: 0}]});
            try {
                // We don't know exactly how many documents will be in the collection at the time of
                // the query, so we can't verify this value.
                cursor.itcount();
            } catch (e) {
                // We expect to see errors caused by the plan executor being killed, because of the
                // collection getting dropped on another thread.
                if (ErrorCodes.QueryPlanKilled != e.code) {
                    throw e;
                }
            }
        },

        dropCollection: function dropCollection(db, collNameUnused) {
            db[this.collName].drop();

            // Restore the collection.
            populateIndexes(db[this].collName, this.indexSpecs);
            populateCollection(db[this.collName], this.numDocs);
        },

        dropIndex: function dropIndex(db, collNameUnused) {
            var indexSpec = this.indexSpecs[Random.randInt(this.indexSpecs.length)];

            // We don't assert that the command succeeded when dropping an index because it's
            // possible another thread has already dropped this index.
            db[this.collName].dropIndex(indexSpec);

            // Recreate the index that was dropped. (See populateIndexes() for why we ignore the
            // CannotImplicitlyCreateCollection error.)
            let res = db[this.collName].createIndex(indexSpec);
            assertWorkedOrFailedHandleTxnErrors(res,
                                                [
                                                    ErrorCodes.CannotImplicitlyCreateCollection,
                                                    ErrorCodes.IndexBuildAlreadyInProgress
                                                ],
                                                ErrorCodes.CannotImplicitlyCreateCollection);
        }
    };

    var transitions = {
        query: {query: 0.8, dropCollection: 0.1, dropIndex: 0.1},
        dropCollection: {query: 1},
        dropIndex: {query: 1}
    };

    function populateIndexes(coll, indexSpecs) {
        indexSpecs.forEach(indexSpec => {
            // In sharded configurations, there's a limit to how many times mongos can retry an
            // operation that fails because it wants to implicitly create a collection that is
            // concurrently dropped. Normally, that's fine, but if some jerk keeps dropping our
            // collection (as in the 'dropCollection' state of this test), then we run out of
            // retries and get a CannotImplicitlyCreateCollection error once in a while, which we
            // have to ignore.
            assertWorkedOrFailedHandleTxnErrors(coll.createIndex(indexSpec),
                                                [
                                                    ErrorCodes.CannotImplicitlyCreateCollection,
                                                    ErrorCodes.IndexBuildAlreadyInProgress
                                                ],
                                                ErrorCodes.CannotImplicitlyCreateCollection);
        });
    }

    function populateCollection(coll, numDocs) {
        // See populateIndexes() for why we ignore CannotImplicitlyCreateCollection errors.
        // Similarly, this bulk insert can also give up with a NoProgressMade error after repeated
        // attempts in the sharded causal consistency configuration. We also ignore that error.
        const bulkInsertResult = coll.insert(Array(numDocs).fill({a: 0, b: 0, c: 0}));
        assertAlways(!bulkInsertResult.hasWriteConcernError(), bulkInsertResult);
        bulkInsertResult.getWriteErrors().forEach(err => {
            assertAlways.contains(
                err.code, [ErrorCodes.CannotImplicitlyCreateCollection, ErrorCodes.NoProgressMade]);
        }, bulkInsertResult);
    }

    function setup(db, collNameUnused, cluster) {
        populateIndexes(db[this.collName], this.indexSpecs);
        populateCollection(db[this.collName], this.numDocs);
    }

    return {
        threadCount: 10,
        iterations: 50,
        data: data,
        states: states,
        startState: 'query',
        transitions: transitions,
        setup: setup
    };
})();