summaryrefslogtreecommitdiff
path: root/jstests/replsets/tenant_migration_concurrent_writes_on_donor_blocking_then_aborted.js
blob: a6446dbfc1a28fb70fd9e6204fc7338cc95e84b7 (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 that the donor blocks writes that are executed while the migration in the blocking state,
 * then rejects the writes when the migration aborted.
 *
 * @tags: [
 *   incompatible_with_macos,
 *   incompatible_with_windows_tls,
 *   requires_majority_read_concern,
 *   requires_persistence,
 *   serverless,
 * ]
 */
(function() {
'use strict';

load("jstests/libs/fail_point_util.js");
load("jstests/libs/parallelTester.js");
load("jstests/libs/uuid_util.js");
load("jstests/replsets/libs/tenant_migration_test.js");
load("jstests/replsets/libs/tenant_migration_util.js");
load("jstests/replsets/tenant_migration_concurrent_writes_on_donor_util.js");

const tenantMigrationTest = new TenantMigrationTest({
    name: jsTestName(),
    quickGarbageCollection: true,
});

const donorRst = tenantMigrationTest.getDonorRst();
const donorPrimary = donorRst.getPrimary();

const kCollName = "testColl";

const kTenantDefinedDbName = "0";

/**
 * To be used to resume a migration that is paused after entering the blocking state. Waits for the
 * number of blocked reads to reach 'targetNumBlockedWrites' and unpauses the migration.
 */
function resumeMigrationAfterBlockingWrite(host, tenantId, targetNumBlockedWrites) {
    load("jstests/libs/fail_point_util.js");
    load("jstests/replsets/libs/tenant_migration_util.js");
    const primary = new Mongo(host);

    assert.soon(() => TenantMigrationUtil.getNumBlockedWrites(primary, tenantId) ==
                    targetNumBlockedWrites);

    assert.commandWorked(primary.adminCommand(
        {configureFailPoint: "pauseTenantMigrationBeforeLeavingBlockingState", mode: "off"}));
}

/**
 * Tests that the donor blocks writes that are executed in the blocking state and rejects them after
 * the migration aborts.
 */
function testRejectBlockedWritesAfterMigrationAborted(testCase, testOpts) {
    const tenantId = testOpts.dbName.split('_')[0];
    const migrationOpts = {
        migrationIdString: extractUUIDFromObject(UUID()),
        tenantId,
    };

    let blockingFp =
        configureFailPoint(testOpts.primaryDB, "pauseTenantMigrationBeforeLeavingBlockingState");
    let abortFp =
        configureFailPoint(testOpts.primaryDB, "abortTenantMigrationBeforeLeavingBlockingState");

    let resumeMigrationThread =
        new Thread(resumeMigrationAfterBlockingWrite, testOpts.primaryHost, tenantId, 1);

    // Run the command after the migration enters the blocking state.
    assert.commandWorked(
        tenantMigrationTest.startMigration(migrationOpts, {enableDonorStartMigrationFsync: true}));
    resumeMigrationThread.start();
    blockingFp.wait();

    // The migration should unpause and abort after the write is blocked. Verify that the write is
    // rejected.
    runCommandForConcurrentWritesTest(testOpts, ErrorCodes.TenantMigrationAborted);

    // Verify that the migration aborted due to the simulated error.
    resumeMigrationThread.join();
    TenantMigrationTest.assertAborted(tenantMigrationTest.waitForMigrationToComplete(
        migrationOpts, false /* retryOnRetryableErrors */));
    abortFp.off();

    testCase.assertCommandFailed(testOpts.primaryDB, testOpts.dbName, testOpts.collName);
    checkTenantMigrationAccessBlockerForConcurrentWritesTest(
        testOpts.primaryDB, tenantId, {numBlockedWrites: 1, numTenantMigrationAbortedErrors: 1});

    assert.commandWorked(tenantMigrationTest.forgetMigration(migrationOpts.migrationIdString));
    tenantMigrationTest.waitForMigrationGarbageCollection(migrationOpts.migrationIdString);
}

const testCases = TenantMigrationConcurrentWriteUtil.testCases;

// Run test cases while the migration is blocked and then rejects after aborted.
for (const [commandName, testCase] of Object.entries(testCases)) {
    let baseDbName = commandName + "-inBlockingThenAborted0";

    if (testCase.skip) {
        print("Skipping " + commandName + ": " + testCase.skip);
        continue;
    }

    runTestForConcurrentWritesTest(donorPrimary,
                                   testCase,
                                   testRejectBlockedWritesAfterMigrationAborted,
                                   baseDbName + "Basic_" + kTenantDefinedDbName,
                                   kCollName);

    if (testCase.testInTransaction) {
        runTestForConcurrentWritesTest(donorPrimary,
                                       testCase,
                                       testRejectBlockedWritesAfterMigrationAborted,
                                       baseDbName + "Txn_" + kTenantDefinedDbName,
                                       kCollName,
                                       {testInTransaction: true});
    }

    if (testCase.testAsRetryableWrite) {
        runTestForConcurrentWritesTest(donorPrimary,
                                       testCase,
                                       testRejectBlockedWritesAfterMigrationAborted,
                                       baseDbName + "Retryable_" + kTenantDefinedDbName,
                                       kCollName,
                                       {testAsRetryableWrite: true});
    }
}

tenantMigrationTest.stop();
})();