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

/**
 * findAndModify_update_grow.js
 *
 * Each thread inserts a single document into a collection, and then
 * repeatedly performs the findAndModify command. Attempts to trigger
 * a document move by growing the size of the inserted document using
 * the $set and $mul update operators.
 */
load('jstests/concurrency/fsm_workload_helpers/server_types.js'); // for isMongod and isMMAPv1

var $config = (function() {

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

    var states = (function() {

        // Use the workload name as the field name (since it is assumed
        // to be unique) to avoid any potential issues with large keys
        // and indexes on the collection.
        var uniqueFieldName = 'findAndModify_update_grow';

        function makeStringOfLength(length) {
            return new Array(length + 1).join('x');
        }

        function makeDoc(tid) {
            // Use 32-bit integer for representing 'length' property
            // to ensure $mul does integer multiplication
            var doc = { _id: new ObjectId(), tid: tid, length: new NumberInt(1) };
            doc[uniqueFieldName] = makeStringOfLength(doc.length);
            return doc;
        }

        function insert(db, collName) {
            var doc = makeDoc(this.tid);
            this.length = doc.length;
            this.bsonsize = Object.bsonsize(doc);

            var res = db[collName].insert(doc);
            assertAlways.writeOK(res);
            assertAlways.eq(1, res.nInserted);
        }

        function findAndModify(db, collName) {
            // When the size of the document starts to near the 16MB limit,
            // start over with a new document
            if (this.bsonsize > 4 * 1024 * 1024 /* 4MB */) {
                insert.call(this, db, collName);
            }

            // Get the DiskLoc of the document before its potential move
            var before = db[collName].find({ tid: this.tid })
                                     .showDiskLoc()
                                     .sort({ length: 1 }) // fetch document of smallest size
                                     .limit(1)
                                     .next();

            // Increase the length of the 'findAndModify_update_grow' string
            // to double the size of the overall document
            var factor = Math.ceil(2 * this.bsonsize / this.length);
            var updatedLength = factor * this.length;
            var updatedValue = makeStringOfLength(updatedLength);

            var update = { $set: {}, $mul: { length: factor } };
            update.$set[uniqueFieldName] = updatedValue;

            var res = db.runCommand({
                findandmodify: db[collName].getName(),
                query: { tid: this.tid },
                sort: { length: 1 }, // fetch document of smallest size
                update: update,
                new: true
            });
            assertAlways.commandWorked(res);

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

            if (doc === null) {
                return;
            }

            assertAlways.eq(this.tid, doc.tid);
            assertWhenOwnColl.eq(updatedValue, doc[uniqueFieldName]);
            assertWhenOwnColl.eq(updatedLength, doc.length);

            this.length = updatedLength;
            this.bsonsize = Object.bsonsize(doc);

            // Get the DiskLoc of the document after its potential move
            var after = db[collName].find({ _id: before._id }).showDiskLoc().next();

            if (isMongod(db) && isMMAPv1(db)) {
                // Since the document has at least doubled in size, and the default
                // allocation strategy of mmapv1 is to use power of two sizes, the
                // document will have always moved
                assertWhenOwnColl.neq(before.$recordId, after.$recordId,
                                      'document should have moved');
            }
        }

        return {
            insert: insert,
            findAndModify: findAndModify,
        };

    })();

    var transitions = {
        insert: { findAndModify: 1 },
        findAndModify: { findAndModify: 1 }
    };

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

})();