summaryrefslogtreecommitdiff
path: root/jstests/replsets/tenant_migration_abort_forget_retry.js
blob: 4f3eb5e6381ff5cae9f1e348570c595dd8641b7e (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
/**
 * Starts a tenant migration that aborts, either due to the
 * abortTenantMigrationBeforeLeavingBlockingState failpoint or due to receiving donorAbortMigration,
 * and then issues a donorForgetMigration command. Finally, starts a second tenant migration with
 * the same tenantId as the aborted migration, and expects this second migration to go through.
 *
 * @tags: [
 *   incompatible_with_macos,
 *   incompatible_with_windows_tls,
 *   requires_majority_read_concern,
 *   requires_persistence,
 *   serverless,
 * ]
 */

import {TenantMigrationTest} from "jstests/replsets/libs/tenant_migration_test.js";
import {tryAbortMigrationAsync} from "jstests/replsets/libs/tenant_migration_util.js";

load("jstests/libs/fail_point_util.js");
load("jstests/libs/parallelTester.js");
load("jstests/libs/uuid_util.js");
load("jstests/replsets/rslib.js");  // 'createRstArgs'

function makeTenantId() {
    return ObjectId().str;
}

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

(() => {
    const migrationId1 = extractUUIDFromObject(UUID());
    const migrationId2 = extractUUIDFromObject(UUID());
    const tenantId = makeTenantId();

    // Start a migration with the "abortTenantMigrationBeforeLeavingBlockingState" failPoint
    // enabled. The migration will abort as a result, and a status of "kAborted" should be returned.
    jsTestLog(
        "Starting a migration that is expected to abort due to setting abortTenantMigrationBeforeLeavingBlockingState failpoint. migrationId: " +
        migrationId1 + ", tenantId: " + tenantId);
    const donorPrimary = tenantMigrationTest.getDonorPrimary();
    const abortFp =
        configureFailPoint(donorPrimary, "abortTenantMigrationBeforeLeavingBlockingState");
    TenantMigrationTest.assertAborted(tenantMigrationTest.runMigration(
        {migrationIdString: migrationId1, tenantId: tenantId}, {automaticForgetMigration: false}));
    abortFp.off();

    // Forget the aborted migration.
    jsTestLog("Forgetting aborted migration with migrationId: " + migrationId1);
    assert.commandWorked(tenantMigrationTest.forgetMigration(migrationId1));

    // Try running a new migration with the same tenantId. It should succeed, since the previous
    // migration with the same tenantId was aborted.
    jsTestLog("Attempting to run a new migration with the same tenantId. New migrationId: " +
              migrationId2 + ", tenantId: " + tenantId);
    TenantMigrationTest.assertCommitted(
        tenantMigrationTest.runMigration({migrationIdString: migrationId2, tenantId: tenantId}));
    tenantMigrationTest.waitForMigrationGarbageCollection(migrationId2, tenantId);
})();

(() => {
    const migrationId1 = extractUUIDFromObject(UUID());
    const migrationId2 = extractUUIDFromObject(UUID());
    const tenantId = makeTenantId();

    jsTestLog(
        "Starting a migration that is expected to abort in blocking state due to receiving donorAbortMigration. migrationId: " +
        migrationId1 + ", tenantId: " + tenantId);

    const donorPrimary = tenantMigrationTest.getDonorPrimary();
    let fp = configureFailPoint(donorPrimary, "pauseTenantMigrationBeforeLeavingBlockingState");
    assert.commandWorked(
        tenantMigrationTest.startMigration({migrationIdString: migrationId1, tenantId: tenantId}));

    fp.wait();

    const donorRstArgs = createRstArgs(tenantMigrationTest.getDonorRst());
    const tryAbortThread = new Thread(tryAbortMigrationAsync,
                                      {migrationIdString: migrationId1, tenantId: tenantId},
                                      donorRstArgs,
                                      true /* retryOnRetryableErrors */);
    tryAbortThread.start();

    // Wait for donorAbortMigration command to start.
    assert.soon(() => {
        const res = assert.commandWorked(
            donorPrimary.adminCommand({currentOp: true, desc: "tenant donor migration"}));
        const op = res.inprog.find(op => extractUUIDFromObject(op.instanceID) === migrationId1);
        return op.receivedCancellation;
    });

    fp.off();

    tryAbortThread.join();
    assert.commandWorked(tryAbortThread.returnData());

    TenantMigrationTest.assertAborted(tenantMigrationTest.waitForMigrationToComplete(
        {migrationIdString: migrationId1, tenantId: tenantId}));

    // Forget the aborted migration.
    jsTestLog("Forgetting aborted migration with migrationId: " + migrationId1);
    assert.commandWorked(tenantMigrationTest.forgetMigration(migrationId1));

    // Try running a new migration with the same tenantId. It should succeed, since the previous
    // migration with the same tenantId was aborted.
    jsTestLog("Attempting to run a new migration with the same tenantId. New migrationId: " +
              migrationId2 + ", tenantId: " + tenantId);
    TenantMigrationTest.assertCommitted(
        tenantMigrationTest.runMigration({migrationIdString: migrationId2, tenantId: tenantId}));
    tenantMigrationTest.waitForMigrationGarbageCollection(migrationId2, tenantId);
})();

tenantMigrationTest.stop();