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

/**
 * findAndModify_upsert.js
 *
 * Each thread repeatedly performs the findAndModify command, specifying
 * upsert as either true or false. A single document is selected (or
 * created) based on the 'query' specification, and updated using the
 * $push operator.
 */
var $config = (function() {

    var data = {
        sort: false,
        shardKey: {tid: 1}
    };

    var states = (function() {

        // Returns true if the specified array is sorted in ascending order,
        // and false otherwise.
        function isSorted(arr) {
            for (var i = 0; i < arr.length - 1; ++i) {
                if (arr[i] > arr[i + 1]) {
                    return false;
                }
            }

            return true;
        }

        function init(db, collName) {
            this.iter = 0;

            // Need to guarantee that an upsert has occurred prior to an update,
            // which is not enforced by the transition table under composition
            upsert.call(this, db, collName);
        }

        function upsert(db, collName) {
            var updatedValue = this.iter++;

            // Use a query specification that does not match any existing documents
            var query = {
                _id: new ObjectId(),
                tid: this.tid
            };

            var cmdObj = {
                findandmodify: db[collName].getName(),
                query: query,
                update: {$setOnInsert: {values: [updatedValue]}}, new: true,
                upsert: true
            };

            if (this.sort) {
                cmdObj.sort = this.sort;
            }

            var res = db.runCommand(cmdObj);
            assertAlways.commandWorked(res);

            var doc = res.value;
            assertAlways(doc !== null, 'a document should have been inserted');

            assertAlways((function() {
                assertAlways.eq(this.tid, doc.tid);
                assertAlways(Array.isArray(doc.values), 'expected values to be an array');
                assertAlways.eq(1, doc.values.length);
                assertAlways.eq(updatedValue, doc.values[0]);
            }).bind(this));
        }

        function update(db, collName) {
            var updatedValue = this.iter++;

            var cmdObj = {
                findandmodify: db[collName].getName(),
                query: {tid: this.tid},
                update: {$push: {values: updatedValue}}, new: true,
                upsert: false
            };

            if (this.sort) {
                cmdObj.sort = this.sort;
            }

            var res = db.runCommand(cmdObj);
            assertAlways.commandWorked(res);

            var doc = res.value;
            assertWhenOwnColl(doc !== null, 'query spec should have matched a document');

            if (doc !== null) {
                assertAlways.eq(this.tid, doc.tid);
                assertWhenOwnColl(Array.isArray(doc.values), 'expected values to be an array');
                assertWhenOwnColl(function() {
                    assertWhenOwnColl.gte(doc.values.length, 2);
                    assertWhenOwnColl.eq(updatedValue, doc.values[doc.values.length - 1]);
                    assertWhenOwnColl(isSorted(doc.values),
                                      'expected values to be sorted: ' + tojson(doc.values));
                });
            }
        }

        return {
            init: init,
            upsert: upsert,
            update: update
        };

    })();

    var transitions = {
        init: {upsert: 0.1, update: 0.9},
        upsert: {upsert: 0.1, update: 0.9},
        update: {upsert: 0.1, update: 0.9}
    };

    return {
        threadCount: 20,
        iterations: 20,
        data: data,
        states: states,
        transitions: transitions
    };

})();