summaryrefslogtreecommitdiff
path: root/jstests/multiVersion/index_bigkeys.js
blob: 3e8955e1ea3600cc1220be623ffa630269b94b19 (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
/**
 * Test interactions with big keys in different versions:
 * 1. MongoDB 4.2 (FCV 4.0) and MongoDB 4.0 allow reading and deleting long (unique) index keys but
 * does not allow inserting or updating long (unique) index keys.
 * 2. MongoDB 4.2 (FCV 4.2) allows inserting, reading, deleting and updating long (unique) index
 * keys.
 * 3. MongoDB 4.2 could successfully validate index consistency after reindex() if 4.0 intentionally
 * missed out long index keys by setting 'failIndexKeyTooLong' to false.
 * 4. MongoDB 4.0 could successfully validate index consistency after drop() if 4.2 inserted long
 * index keys (with < 127 bytes typebits).
 *
 * TODO SERVER-36385: Remove this test in 4.4
 */

"use strict";

const dbName = "test";
const collName = "index_bigkeys";
const dbpath = MongoRunner.dataPath + "index_bigkeys";

const largeKey = 's'.repeat(12345);
const documentWithLargeKey = {
    x: largeKey
};

/**
 * Assert that number of index keys on 'x' is equal to the collection count.
 */
function assertAllThingsAreIndexed(testColl) {
    assert.eq(testColl.find().sort({x: 1}).itcount(), testColl.count());
}

function testInsertDocumentWithLargeKey(conn, expectKeyTooLongFailure) {
    let testDB = conn.getDB(dbName);
    let testColl = testDB[collName];

    [true, false].forEach(function(buildIndexFirst) {
        [true, false].forEach(function(backgroundIndexBuild) {
            [true, false].forEach(function(uniqueIndex) {
                testColl.drop();
                let res;

                if (buildIndexFirst) {
                    assert.commandWorked(testColl.createIndex(
                        {x: 1},
                        {name: "x_1", background: backgroundIndexBuild, unique: uniqueIndex}));
                    res = testColl.insert(documentWithLargeKey);
                } else {
                    testColl.insert(documentWithLargeKey);
                    res = testColl.createIndex(
                        {x: 1},
                        {name: "x_1", background: backgroundIndexBuild, unique: uniqueIndex});
                }

                if (expectKeyTooLongFailure)
                    assert.commandFailedWithCode(res, ErrorCodes.KeyTooLong);
                else
                    assert.commandWorked(res);

                assertAllThingsAreIndexed(testColl);
            });
        });
    });
}

/**
 * Insert big index keys in FCV 4.2 and downgrade all the way to 4.0 binary and
 * verify the behaviors of FCV 4.0 and 4.0 binary.
 */
function downgradeAndVerifyBehavior(testDowngradeBehaviorFunc) {
    // Only test non-unique index for downgrade path because 4.2 and 4.0 have different formats of
    // unique index.
    let uniqueIndex = false;

    // 1. Downgrade to FCV 4.0 and verify behaviors.
    let conn = MongoRunner.runMongod({binVersion: "latest", cleanData: true, dbpath: dbpath});
    let testColl = conn.getDB(dbName)[collName];
    assert.commandWorked(testColl.createIndex({x: 1}, {name: "x_1", unique: uniqueIndex}));
    assert.commandWorked(testColl.insert(documentWithLargeKey));
    assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: "4.0"}));
    assertAllThingsAreIndexed(testColl);
    testDowngradeBehaviorFunc(testColl);
    // We skip the validation because FCV 4.0 cannot validate a big index key
    // inserted by FCV 4.2.
    MongoRunner.stopMongod(conn, null, {skipValidation: true});

    // 2. Start fresh on a 4.2 binary and insert long index keys. Then downgrade to FCV 4.0 followed
    // by restarting with a 4.0 binary and verify behaviors.
    conn = MongoRunner.runMongod({binVersion: "latest", cleanData: true, dbpath: dbpath});
    testColl = conn.getDB(dbName)[collName];
    assert.commandWorked(testColl.createIndex({x: 1}, {name: "x_1", unique: uniqueIndex}));
    assert.commandWorked(testColl.insert(documentWithLargeKey));
    assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: "4.0"}));
    // We skip the validation because FCV 4.0 cannot validate a big index key
    // inserted by FCV 4.2.
    MongoRunner.stopMongod(conn, null, {skipValidation: true});
    conn = MongoRunner.runMongod({binVersion: "4.0", noCleanData: true, dbpath: dbpath});
    testColl = conn.getDB(dbName)[collName];
    assertAllThingsAreIndexed(testColl);
    testDowngradeBehaviorFunc(testColl);
    MongoRunner.stopMongod(conn, null, {skipValidation: true});
}

(function() {
    load("jstests/libs/feature_compatibility_version.js");

    // Test the behavior of inserting big index keys of each version.
    // 4.2 binary (with FCV 4.2)
    let conn = MongoRunner.runMongod({binVersion: "latest", cleanData: true});
    testInsertDocumentWithLargeKey(conn, false);

    // 4.2 binary (with FCV 4.0)
    assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: "4.0"}));
    testInsertDocumentWithLargeKey(conn, true);
    MongoRunner.stopMongod(conn);

    // 4.0 binary
    conn = MongoRunner.runMongod({binVersion: "4.0", cleanData: true});
    testInsertDocumentWithLargeKey(conn, true);
    MongoRunner.stopMongod(conn);

    // Downgrade path
    // 1. Test that 4.0 binary could read and delete big index keys which got
    // inserted by 4.2 binary.
    downgradeAndVerifyBehavior(testColl => {
        assert.commandWorked(testColl.remove({x: largeKey}));
        assert(testColl.validate().valid);
    });

    // 2. Test that 4.0 binary could update big keys with small keys.
    downgradeAndVerifyBehavior(testColl => {
        assert.commandWorked(testColl.update({x: largeKey}, {$set: {x: "sss"}}));
        assert.eq("sss", testColl.find({x: "sss"}).toArray()[0].x);
    });

    // 3. Test that 4.0 binary could drop the index which has big keys and the
    // validate will succeed after that.
    downgradeAndVerifyBehavior(testColl => {
        assert.eq(2, testColl.getIndexes().length);
        assert(!testColl.validate().valid);
        assert.commandWorked(testColl.dropIndex({x: 1}));
        assert.eq(1, testColl.getIndexes().length);
        assert(testColl.validate().valid);
    });

    // Upgrade path
    // 1. Test the normal upgrade path.
    [true, false].forEach(function(uniqueIndex) {
        // Upgrade all the way to 4.2 binary with FCV 4.2.
        let conn = MongoRunner.runMongod({binVersion: "4.0", cleanData: true, dbpath: dbpath});
        assert.commandWorked(
            conn.getDB(dbName)[collName].createIndex({x: 1}, {name: "x_1", unique: uniqueIndex}));
        assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: "4.0"}));
        MongoRunner.stopMongod(conn);
        conn = MongoRunner.runMongod({binVersion: "latest", noCleanData: true, dbpath: dbpath});
        // Setting the FCV to 4.2
        assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: latestFCV}));
        assert.commandWorked(
            conn.getDB(dbName).runCommand({insert: collName, documents: [documentWithLargeKey]}));
        MongoRunner.stopMongod(conn, null, {skipValidation: false});
    });

    // 2. If 4.0 binary has already inserted documents with large keys by setting
    // 'failIndexKeyTooLong' to be false (which bypasses inserting the index key), 4.2 binary cannot
    // successfully validate the index consistency because some index keys are missing. But reindex
    // should solve this problem.
    [true, false].forEach(function(uniqueIndex) {
        let conn = MongoRunner.runMongod({
            binVersion: "4.0",
            cleanData: true,
            setParameter: "failIndexKeyTooLong=false",
            dbpath: dbpath
        });
        assert.commandWorked(
            conn.getDB(dbName)[collName].createIndex({x: 1}, {name: "x_1", unique: uniqueIndex}));
        assert.commandWorked(conn.getDB(dbName)[collName].insert(documentWithLargeKey));
        assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: "4.0"}));
        MongoRunner.stopMongod(conn);
        conn = MongoRunner.runMongod({binVersion: "latest", noCleanData: true, dbpath: dbpath});
        assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: latestFCV}));

        let testColl = conn.getDB(dbName)[collName];
        assert(!testColl.validate().valid);
        testColl.reIndex();
        assert(testColl.validate().valid);

        MongoRunner.stopMongod(conn, null, {skipValidation: false});
    });

}());