summaryrefslogtreecommitdiff
path: root/jstests/sharding/index_operations_abort_concurrent_outgoing_migrations.js
blob: 0ca446022da5f177a7519e74c1b790fbdd21910d (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
/*
 * Test that the index commands abort concurrent outgoing migrations.
 * @tags: [requires_fcv_44]
 */
(function() {
"use strict";

load('jstests/libs/chunk_manipulation_util.js');
load("jstests/libs/parallelTester.js");
load("jstests/sharding/libs/sharded_index_util.js");

/*
 * Runs moveChunk on the host to move the chunk to the given shard.
 */
function runMoveChunk(host, ns, findCriteria, toShard) {
    const mongos = new Mongo(host);
    return mongos.adminCommand({moveChunk: ns, find: findCriteria, to: toShard});
}

/*
 * Runs moveChunk to move the initial chunk from the primary shard (shard0) to shard1. Pauses
 * the migration at the given step and runs the given command function. Asserts that the command
 * aborts the outgoing migration.
 */
function assertCommandAbortsConcurrentOutgoingMigration(st, stepName, ns, cmdFunc) {
    const fromShard = st.shard0;
    const toShard = st.shard1;

    // Turn on the fail point and wait for moveChunk to hit the fail point.
    pauseMoveChunkAtStep(fromShard, stepName);
    let moveChunkThread = new Thread(runMoveChunk, st.s.host, ns, {_id: MinKey}, toShard.shardName);
    moveChunkThread.start();
    waitForMoveChunkStep(fromShard, stepName);

    cmdFunc();

    // Turn off the fail point and wait for moveChunk to complete.
    unpauseMoveChunkAtStep(fromShard, stepName);
    moveChunkThread.join();
    assert.commandFailedWithCode(moveChunkThread.returnData(), ErrorCodes.Interrupted);
}

const st = new ShardingTest({shards: 2});
const dbName = "test";
const testDB = st.s.getDB(dbName);
const shardKey = {
    _id: 1
};
const index = {
    x: 1
};
// The steps after cloning starts and before the donor enters the critical section.
const stepNames = [moveChunkStepNames.startedMoveChunk, moveChunkStepNames.reachedSteadyState];

assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
st.ensurePrimaryShard(dbName, st.shard0.shardName);

stepNames.forEach((stepName) => {
    jsTest.log(`Testing that createIndexes aborts concurrent outgoing migrations that are in step ${
        stepName}...`);
    const collName = "testCreateIndexesMoveChunkStep" + stepName;
    const ns = dbName + "." + collName;

    assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: shardKey}));

    assertCommandAbortsConcurrentOutgoingMigration(st, stepName, ns, () => {
        const coll = st.s.getCollection(ns);

        // Insert document into collection to avoid optimization for index creation on an empty
        // collection. This allows us to pause index builds on the collection using a fail point.
        assert.commandWorked(coll.insert({a: 1}));

        assert.commandWorked(coll.createIndexes([index]));
    });

    // Verify that the index command succeeds.
    ShardedIndexUtil.assertIndexExistsOnShard(st.shard0, dbName, collName, index);

    // If createIndexes is run after the migration has reached the steady state, shard1
    // will not have the index created by the command because the index just does not
    // exist when shard1 clones the collection options and indexes from shard0. However,
    // if createIndexes is run after the cloning step starts but before the steady state
    // is reached, shard0 may have the index when shard1 does the cloning so shard1 may
    // or may not have the index.
    if (stepName == moveChunkStepNames.reachedSteadyState) {
        ShardedIndexUtil.assertIndexDoesNotExistOnShard(st.shard1, dbName, collName, index);
    }
});

stepNames.forEach((stepName) => {
    jsTest.log(`Testing that dropIndexes aborts concurrent outgoing migrations that are in step ${
        stepName}...`);
    const collName = "testDropIndexesMoveChunkStep" + stepName;
    const ns = dbName + "." + collName;

    assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: shardKey}));

    // Create the index on the primary shard prior to the migration so that the migration is not
    // aborted because of createIndexes instead of dropIndexes.
    assert.commandWorked(st.shard0.getCollection(ns).createIndexes([index]));

    assertCommandAbortsConcurrentOutgoingMigration(st, stepName, ns, () => {
        assert.commandWorked(st.s.getCollection(ns).dropIndexes(index));
    });

    // Verify that the index command succeeds.
    ShardedIndexUtil.assertIndexDoesNotExistOnShard(st.shard0, dbName, collName, index);
    ShardedIndexUtil.assertIndexExistsOnShard(st.shard1, dbName, collName, index);
});

stepNames.forEach((stepName) => {
    jsTest.log(`Testing that collMod aborts concurrent outgoing migrations that are in step ${
        stepName}...`);
    const collName = "testCollModMoveChunkStep" + stepName;
    const ns = dbName + "." + collName;

    assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: shardKey}));

    assertCommandAbortsConcurrentOutgoingMigration(st, stepName, ns, () => {
        assert.commandWorked(
            testDB.runCommand({collMod: collName, validator: {x: {$type: "string"}}}));
    });

    // Verify that the index command succeeds.
    assert.commandFailedWithCode(st.shard0.getCollection(ns).insert({x: 1}),
                                 ErrorCodes.DocumentValidationFailure);
    assert.commandWorked(st.shard1.getCollection(ns).insert({x: 1}));
});

assert.commandWorked(st.s.adminCommand({setFeatureCompatibilityVersion: lastStableFCV}));

stepNames.forEach((stepName) => {
    jsTest.log(
        `Testing that createIndexes in FCV 4.2 aborts concurrent outgoing migrations that are in step ${
            stepName}...`);
    const collName = "testCreateIndexesFCV42MoveChunkStep" + stepName;
    const ns = dbName + "." + collName;

    assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: shardKey}));

    assertCommandAbortsConcurrentOutgoingMigration(st, stepName, ns, () => {
        const coll = st.s.getCollection(ns);

        // Insert document into collection to avoid optimization for index creation on an empty
        // collection. This allows us to pause index builds on the collection using a fail point.
        assert.commandWorked(coll.insert({a: 1}));

        assert.commandWorked(coll.createIndexes([index]));
    });

    // Verify that the index command succeeds.
    ShardedIndexUtil.assertIndexExistsOnShard(st.shard0, dbName, collName, index);

    // If createIndexes is run after the migration has reached the steady state, shard1
    // will not have the index created by the command because the index just does not
    // exist when shard1 clones the collection options and indexes from shard0. However,
    // if createIndexes is run after the cloning step starts but before the steady state
    // is reached, shard0 may have the index when shard1 does the cloning so shard1 may
    // or may not have the index.
    if (stepName == moveChunkStepNames.reachedSteadyState) {
        ShardedIndexUtil.assertIndexDoesNotExistOnShard(st.shard1, dbName, collName, index);
    }
});

st.stop();
})();