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
|
/**
* Tests that a recipientForgetMigration is received after the recipient state doc has been deleted.
*
* @tags: [
* incompatible_with_eft,
* incompatible_with_macos,
* incompatible_with_windows_tls,
* requires_majority_read_concern,
* requires_persistence,
* serverless,
* ]
*/
(function() {
"use strict";
load("jstests/libs/fail_point_util.js"); // For configureFailPoint().
load("jstests/libs/parallelTester.js"); // For Thread()
load("jstests/libs/uuid_util.js"); // For extractUUIDFromObject().
load("jstests/replsets/libs/tenant_migration_test.js");
load("jstests/replsets/libs/tenant_migration_util.js");
const tenantMigrationTest = new TenantMigrationTest({name: jsTestName()});
const migrationId = UUID();
const tenantId = 'testTenantId';
const recipientCertificateForDonor =
TenantMigrationUtil.getCertificateAndPrivateKey("jstests/libs/tenant_migration_recipient.pem");
const dbName = tenantMigrationTest.tenantDB(tenantId, "test");
const collName = "coll";
const recipientPrimary = tenantMigrationTest.getRecipientPrimary();
// Not doing a migration before writing to the recipient to mimic that a migration has completed and
// the state doc has been garbage collected.
assert.commandWorked(recipientPrimary.getDB(dbName)[collName].insert({_id: 1}));
function runRecipientForgetMigration(host, {
migrationIdString,
donorConnectionString,
tenantId,
readPreference,
recipientCertificateForDonor
}) {
const db = new Mongo(host);
return db.adminCommand({
recipientForgetMigration: 1,
migrationId: UUID(migrationIdString),
donorConnectionString,
tenantId,
readPreference,
recipientCertificateForDonor
});
}
const fp = configureFailPoint(recipientPrimary, "hangBeforeTaskCompletion");
const recipientForgetMigrationThread =
new Thread(runRecipientForgetMigration, recipientPrimary.host, {
migrationIdString: extractUUIDFromObject(migrationId),
donorConnectionString: tenantMigrationTest.getDonorRst().getURL(),
tenantId,
readPreference: {mode: "primary"},
recipientCertificateForDonor
});
// Run a delayed/retried recipientForgetMigration command after the state doc has been deleted.
recipientForgetMigrationThread.start();
// Block the recipient before it updates the state doc with an expireAt field.
fp.wait();
let currOp = assert
.commandWorked(recipientPrimary.adminCommand(
{currentOp: true, desc: "tenant recipient migration"}))
.inprog[0];
assert.eq(currOp.state, 3 /* kDone */, currOp);
assert(!currOp.hasOwnProperty("expireAt"), currOp);
// Test that we can still read from the recipient.
assert.eq(1, recipientPrimary.getDB(dbName)[collName].find().itcount());
const newRecipientPrimary = tenantMigrationTest.getRecipientRst().getSecondary();
const newPrimaryFp = configureFailPoint(newRecipientPrimary, "hangBeforeTaskCompletion");
// Step up a new recipient primary before the state doc is truly marked as garbage collectable.
assert.commandWorked(newRecipientPrimary.adminCommand({replSetStepUp: 1}));
fp.off();
// The new primary should skip all tenant migration steps but wait for another
// recipientForgetMigration command.
newPrimaryFp.wait();
assert.commandFailedWithCode(recipientForgetMigrationThread.returnData(),
ErrorCodes.InterruptedDueToReplStateChange);
// Test that we can still read from the recipient.
assert.eq(1, newRecipientPrimary.getDB(dbName)[collName].find().itcount());
// Test that we can retry the recipientForgetMigration on the new primary.
newPrimaryFp.off();
assert.commandWorked(runRecipientForgetMigration(newRecipientPrimary.host, {
migrationIdString: extractUUIDFromObject(migrationId),
donorConnectionString: tenantMigrationTest.getDonorRst().getURL(),
tenantId,
readPreference: {mode: "primary"},
recipientCertificateForDonor
}));
currOp = assert
.commandWorked(newRecipientPrimary.adminCommand(
{currentOp: true, desc: "tenant recipient migration"}))
.inprog[0];
assert.eq(currOp.state, 3 /* kDone */, currOp);
assert(currOp.hasOwnProperty("expireAt"), currOp);
tenantMigrationTest.stop();
})();
|