summaryrefslogtreecommitdiff
path: root/jstests/multiVersion/multikey_paths_downgrade.js
blob: 24a445ff75c061eee341aebe3220ebd8f4a63b19 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/**
 * Test the downgrade process for indexes with path-level multikey information:
 *   - Having an index with path-level multikey information should cause versions earlier than 3.2.7
 *     to fail to start up.
 *   - It should be possible to downgrade to earlier versions of 3.2 as well as to versions of 3.0
 *     if the mongod is first downgraded to a version of 3.2 at least as new as 3.2.7.
 *
 * @tags: [requires_persistence]
 */
(function() {
    "use strict";

    load("jstests/libs/analyze_plan.js");

    const versionDowngradeSuccess = "last-stable";

    // We cannot downgrade to any version of 3.2 earlier than 3.2.7 after creating an index while
    // running the "latest" version.
    const version32DowngradeFailure = "3.2.1";

    // We cannot downgrade to any version of 3.0 after creating an index while running the "latest"
    // version.
    const version30DowngradeFailure = "3.0";

    var dbpath = MongoRunner.dataPath + "multikey_paths_downgrade";
    resetDbpath(dbpath);

    var defaultOptions = {
        dbpath: dbpath,
        noCleanData: true,
        // We explicitly set the storage engine as part of the options because not all versions
        // being tested automatically detect it from the storage.bson file.
        storageEngine: jsTest.options().storageEngine,
    };

    if (defaultOptions.storageEngine === "mmapv1") {
        // Path-level multikey tracking is supported for all storage engines that use the KVCatalog.
        // MMAPv1 is the only storage engine that does not.
        //
        // TODO SERVER-22727: Store path-level multikey information in MMAPv1 index catalog.
        print("Skipping test because mmapv1 doesn't yet support path-level multikey tracking");
        return;
    }

    /**
     * Returns whether the index with the specified key pattern is multikey and its path-level
     * multikey information, if present.
     */
    function extractMultikeyInfoFromExplainOutput(db, keyPattern) {
        var explain = db.runCommand({explain: {find: "multikey_paths", hint: keyPattern}});
        assert.commandWorked(explain);

        assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN"),
               "expected stage to be present: " + tojson(explain));
        var stage = getPlanStage(explain.queryPlanner.winningPlan, "IXSCAN");
        assert(stage.hasOwnProperty("isMultiKey"),
               "expected IXSCAN to have isMultiKey property: " + tojson(stage));

        var multikeyInfo = {
            isMultiKey: stage.isMultiKey,
        };
        if (stage.hasOwnProperty("multiKeyPaths")) {
            multikeyInfo.multiKeyPaths = stage.multiKeyPaths;
        }
        return multikeyInfo;
    }

    //
    // Create a multikey index on 3.2.
    //
    var options = Object.extend({binVersion: version32DowngradeFailure}, defaultOptions);
    var conn = MongoRunner.runMongod(options);
    assert.neq(null, conn, "mongod was unable to start up with options: " + tojson(options));

    var testDB = conn.getDB("test");
    assert.commandWorked(testDB.multikey_paths.createIndex({createdOn32: 1}));
    assert.writeOK(testDB.multikey_paths.insert({createdOn32: [1, 2, 3]}));

    // The index created on 3.2 shouldn't have path-level multikey information, but it should be
    // marked as multikey.
    var multikeyInfo = extractMultikeyInfoFromExplainOutput(testDB, {createdOn32: 1});
    assert.eq(true, multikeyInfo.isMultiKey, tojson(multikeyInfo));
    assert(!multikeyInfo.hasOwnProperty("multiKeyPaths"), tojson(multikeyInfo));

    //
    // Upgrade from 3.2 to the "latest" version.
    //
    MongoRunner.stopMongod(conn);

    options = Object.extend({binVersion: "latest"}, defaultOptions);
    conn = MongoRunner.runMongod(options);
    assert.neq(null,
               conn,
               "mongod should have been able to upgrade directly from " +
                   version32DowngradeFailure + " to the latest version; options: " +
                   tojson(options));
    testDB = conn.getDB("test");

    // The index created on 3.2 shouldn't have path-level multikey information, but it should be
    // marked as multikey.
    multikeyInfo = extractMultikeyInfoFromExplainOutput(testDB, {createdOn32: 1});
    assert.eq(true, multikeyInfo.isMultiKey, tojson(multikeyInfo));
    assert(!multikeyInfo.hasOwnProperty("multiKeyPaths"), tojson(multikeyInfo));

    //
    // Create a multikey index on the "latest" version.
    //
    assert.commandWorked(testDB.multikey_paths.createIndex({createdOnLatest: 1}));
    assert.writeOK(testDB.multikey_paths.insert({createdOnLatest: [1, 2, 3]}));

    // The index created on the "latest" version should have path-level multikey information.
    multikeyInfo = extractMultikeyInfoFromExplainOutput(testDB, {createdOnLatest: 1});
    assert.eq(true, multikeyInfo.isMultiKey, tojson(multikeyInfo));
    assert.eq(
        {createdOnLatest: ["createdOnLatest"]}, multikeyInfo.multiKeyPaths, tojson(multikeyInfo));

    //
    // Attempt to downgrade from the "latest" version to 3.2.
    //
    MongoRunner.stopMongod(conn);
    options = Object.extend({binVersion: version32DowngradeFailure}, defaultOptions);
    conn = MongoRunner.runMongod(options);
    assert.eq(null,
              conn,
              "mongod shouldn't have been able to downgrade from the latest version to " +
                  version32DowngradeFailure + " after creating an index on the latest version;" +
                  " options: " + tojson(options));

    //
    // Attempt to downgrade from the "latest" version to 3.0.
    //
    options = Object.extend({binVersion: version30DowngradeFailure}, defaultOptions);
    conn = MongoRunner.runMongod(options);
    assert.eq(null,
              conn,
              "mongod shouldn't have been able to downgrade from the latest version to " +
                  version30DowngradeFailure + " after creating an index on the latest version;" +
                  " options: " + tojson(options));

    //
    // Downgrade from the "latest" version to the "last-stable" version.
    //
    options = Object.extend({binVersion: versionDowngradeSuccess}, defaultOptions);
    conn = MongoRunner.runMongod(options);
    assert.neq(null,
               conn,
               "mongod should have been able to downgrade from the latest version to the last" +
                   " stable version, even after creating an index on the latest version;" +
                   " options: " + tojson(options));
    testDB = conn.getDB("test");

    //
    // Upgrade from the "last-stable" version to the "latest" version.
    //
    MongoRunner.stopMongod(conn);
    options = Object.extend({binVersion: "latest"}, defaultOptions);
    conn = MongoRunner.runMongod(options);
    assert.neq(
        null,
        conn,
        "mongod should have been able to upgrade from the last-stable version to the latest" +
            " version; options: " + tojson(options));
    testDB = conn.getDB("test");

    // The index created on 3.2 shouldn't have path-level multikey information.
    multikeyInfo = extractMultikeyInfoFromExplainOutput(testDB, {createdOn32: 1});
    assert.eq(true, multikeyInfo.isMultiKey, tojson(multikeyInfo));
    assert(!multikeyInfo.hasOwnProperty("multiKeyPaths"), tojson(multikeyInfo));

    // The index created on the "latest" version should no longer have path-level multikey
    // information either; the path-level multikey information should have been deleted when we
    // downgraded to the "last-stable" version.
    multikeyInfo = extractMultikeyInfoFromExplainOutput(testDB, {createdOnLatest: 1});
    assert.eq(true, multikeyInfo.isMultiKey, tojson(multikeyInfo));
    assert(!multikeyInfo.hasOwnProperty("multiKeyPaths"), tojson(multikeyInfo));

    //
    // Downgrade from the "latest" version to 3.2.
    //
    MongoRunner.stopMongod(conn);
    options = Object.extend({binVersion: version32DowngradeFailure}, defaultOptions);
    conn = MongoRunner.runMongod(options);
    assert.neq(null,
               conn,
               "mongod should have been able to downgrade from the latest version to 3.2 because" +
                   " no index with path-level multikey information exists; options: " +
                   tojson(options));

    //
    // Downgrade from 3.2 to 3.0.
    //
    MongoRunner.stopMongod(conn);
    options = Object.extend({binVersion: version30DowngradeFailure}, defaultOptions);
    conn = MongoRunner.runMongod(options);
    assert.neq(
        null,
        conn,
        "mongod should have been able to downgrade from 3.2 to 3.0; options: " + tojson(options));

    MongoRunner.stopMongod(conn);
})();