summaryrefslogtreecommitdiff
path: root/jstests/serverless/create_indexes_with_tenant_migration.js
blob: a2952dc1659aa969099d72cd722d3e1e5c586653 (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
/**
 * Tests createIndexes returns the expected tenant migration error or succeeds when sent through
 * mongoq after a tenant migration commits or aborts.
 *
 * @tags: [requires_fcv_52, serverless]
 */

(function() {
"use strict";

load("jstests/libs/fail_point_util.js");
load("jstests/serverless/serverlesstest.js");
load('jstests/concurrency/fsm_libs/worker_thread.js');

// A function, not a constant, to ensure unique UUIDs.
function donorStartMigrationCmd(tenantID, realConnUrl) {
    return {
        donorStartMigration: 1,
        tenantId: tenantID.str,
        migrationId: UUID(),
        recipientConnectionString: realConnUrl,
        readPreference: {mode: "primary"}
    };
}

let createIndexesCmd = {createIndexes: "foo", indexes: [{key: {x: 1}, name: "x_1"}]};

let st = new ServerlessTest();
let donor = st.rs0;
let recipient = st.rs1;
let mongoq = st.q0;
let adminDB = donor.getPrimary().getDB('admin');

(() => {
    jsTest.log("Starting test calling createIndexes after the migration has committed.");
    const kTenantID = ObjectId();
    const kDbName = kTenantID.str + "_testDB";
    let db = mongoq.getDB(kDbName);

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

    // Run donorStartMigration command to start migration and poll the migration status with the
    // same command object.
    let startMigrationCmd = donorStartMigrationCmd(kTenantID, recipient.getURL());
    assert.soon(function() {
        let res = assert.commandWorked(adminDB.runCommand(startMigrationCmd));
        return res['state'] == "committed";
    }, "migration not in committed state", 1 * 10000, 1 * 1000);

    assert.commandFailedWithCode(db.runCommand(createIndexesCmd),
                                 ErrorCodes.TenantMigrationCommitted);
})();

(() => {
    jsTest.log("Starting test calling createIndexes after the migration has aborted.");
    const kTenantID = ObjectId();
    const kDbName = kTenantID.str + "_testDB";
    let db = mongoq.getDB(kDbName);

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

    let abortFailPoint =
        configureFailPoint(adminDB, "abortTenantMigrationBeforeLeavingBlockingState");

    let startMigrationCmd = donorStartMigrationCmd(kTenantID, recipient.getURL());
    assert.soon(function() {
        let res = assert.commandWorked(adminDB.runCommand(startMigrationCmd));
        return res['state'] == "aborted";
    }, "migration not in aborted state", 1 * 10000, 1 * 1000);

    assert.commandWorked(db.runCommand(createIndexesCmd));

    abortFailPoint.off();
})();

(() => {
    jsTest.log("Starting test calling createIndexes during migration blocking state then aborts.");
    const kTenantID = ObjectId();
    const kDbName = kTenantID.str + "_testDB";

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

    let blockingFailPoint =
        configureFailPoint(adminDB, "pauseTenantMigrationBeforeLeavingBlockingState");
    let abortFailPoint =
        configureFailPoint(adminDB, "abortTenantMigrationBeforeLeavingBlockingState");

    // Start the migration asynchronously and then immediately return the current state of the
    // migration.
    let startMigrationCmd = donorStartMigrationCmd(kTenantID, recipient.getURL());
    assert.commandWorked(adminDB.runCommand(startMigrationCmd));

    blockingFailPoint.wait();

    // Send createIndexes command to mongoq in an individual thread.
    let createIndexesThread = new Thread((mongoqConnString, kDbName, createIndexesCmd) => {
        let mongoqConn = new Mongo(mongoqConnString);
        // Expect to receive an ok response for the createIndexes command.
        assert.commandWorked(mongoqConn.getDB(kDbName).runCommand(createIndexesCmd));
    }, st.q0.host, kDbName, createIndexesCmd);
    createIndexesThread.start();

    // Poll the numBlockedWrites of tenant migration access blocker from donor and expect it's
    // increased by the blocked createIndexes command.
    assert.soon(function() {
        let mtab = donor.getPrimary()
                       .getDB('admin')
                       .adminCommand({serverStatus: 1})
                       .tenantMigrationAccessBlocker[kTenantID.str]
                       .donor;
        return mtab.numBlockedWrites > 0;
    }, "no blocked writes found", 1 * 10000, 1 * 1000);

    blockingFailPoint.off();

    // Expect to get aborted state when polling the migration state from donor.
    assert.soon(function() {
        let res = assert.commandWorked(adminDB.runCommand(startMigrationCmd));
        return res['state'] == "aborted";
    }, "migration not in aborted state", 1 * 10000, 1 * 1000);

    createIndexesThread.join();

    abortFailPoint.off();
})();

st.stop();
})();