summaryrefslogtreecommitdiff
path: root/jstests/replsets/tenant_migration_drop_state_doc_collection.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/replsets/tenant_migration_drop_state_doc_collection.js')
-rw-r--r--jstests/replsets/tenant_migration_drop_state_doc_collection.js211
1 files changed, 211 insertions, 0 deletions
diff --git a/jstests/replsets/tenant_migration_drop_state_doc_collection.js b/jstests/replsets/tenant_migration_drop_state_doc_collection.js
new file mode 100644
index 00000000000..e8b1d36649c
--- /dev/null
+++ b/jstests/replsets/tenant_migration_drop_state_doc_collection.js
@@ -0,0 +1,211 @@
+/**
+ * Tests dropping the donor and recipient state doc collections in the middle of a tenant migration.
+ *
+ * @tags: [requires_fcv_47, requires_majority_read_concern, requires_persistence,
+ * incompatible_with_eft, incompatible_with_windows_tls]
+ */
+
+(function() {
+"use strict";
+
+load("jstests/libs/fail_point_util.js");
+load("jstests/libs/uuid_util.js");
+load("jstests/replsets/libs/tenant_migration_test.js");
+load("jstests/replsets/libs/tenant_migration_util.js");
+
+const kMigrationFpNames = [
+ "pauseTenantMigrationAfterPersistingInitialDonorStateDoc",
+ "pauseTenantMigrationBeforeLeavingDataSyncState",
+ "pauseTenantMigrationBeforeLeavingBlockingState",
+ "abortTenantMigrationBeforeLeavingBlockingState",
+ null,
+];
+const kTenantId = "testTenantId";
+let testNum = 0;
+
+function makeTenantId() {
+ return kTenantId + testNum++;
+}
+
+function makeMigrationOpts(tenantMigrationTest, tenantId) {
+ return {
+ migrationIdString: extractUUIDFromObject(UUID()),
+ tenantId: tenantId,
+ recipientConnString: tenantMigrationTest.getRecipientConnString()
+ };
+}
+
+/**
+ * Starts a migration and then either waits for the failpoint or lets the migration run to
+ * completion. Next, drops the donor and/or recipient state doc collections and asserts that the
+ * migration is no longer running on the donor and/or recipient. Then, retries the migration (with a
+ * different migration id if 'retryWithDifferentMigrationId' is true) and verifies that the retry
+ * succeeds or fails as expected.
+ */
+function testDroppingStateDocCollections(tenantMigrationTest, fpName, {
+ dropDonorsCollection = false,
+ dropRecipientsCollection = false,
+ retryWithDifferentMigrationId = false,
+ expectedRunMigrationError,
+ expectedAbortReason
+}) {
+ assert(dropDonorsCollection || dropRecipientsCollection);
+
+ jsTest.log(`Testing with failpoint: ${fpName} dropDonorsCollection: ${
+ dropDonorsCollection}, dropRecipientsCollection: ${
+ dropRecipientsCollection}, retryWithDifferentMigrationId: ${
+ retryWithDifferentMigrationId}`);
+
+ const tenantId = makeTenantId();
+ const migrationOptsBeforeDrop = makeMigrationOpts(tenantMigrationTest, tenantId);
+ let donorPrimary = tenantMigrationTest.getDonorPrimary();
+ let recipientPrimary = tenantMigrationTest.getRecipientPrimary();
+
+ let fp;
+ if (fpName) {
+ fp = configureFailPoint(donorPrimary, fpName, {tenantId: tenantId});
+ assert.commandWorked(
+ tenantMigrationTest.startMigration(migrationOptsBeforeDrop,
+ false /* retryOnRetryableErrors */,
+ false /* automaticForgetMigration */));
+ fp.wait();
+ } else {
+ assert.commandWorked(
+ tenantMigrationTest.runMigration(migrationOptsBeforeDrop,
+ false /* retryOnRetryableErrors */,
+ false /* automaticForgetMigration */));
+ }
+
+ if (dropDonorsCollection) {
+ assert(donorPrimary.getCollection(TenantMigrationTest.kConfigDonorsNS).drop());
+ let donorDoc = donorPrimary.getCollection(TenantMigrationTest.kConfigDonorsNS).findOne({
+ tenantId: tenantId
+ });
+ assert.eq(donorDoc, null);
+
+ const currOpDonor = assert.commandWorked(
+ donorPrimary.adminCommand({currentOp: true, desc: "tenant donor migration"}));
+ assert.eq(currOpDonor.inprog.length, 0);
+
+ // Trigger stepup to allow the donor service to rebuild.
+ assert.commandWorked(donorPrimary.adminCommand({replSetStepDown: 30, force: true}));
+ donorPrimary = tenantMigrationTest.getDonorRst().getPrimary();
+ }
+
+ if (dropRecipientsCollection) {
+ assert(recipientPrimary.getCollection(TenantMigrationTest.kConfigRecipientsNS).drop({
+ writeConcern: {w: "majority"}
+ }));
+ let recipientDoc =
+ recipientPrimary.getCollection(TenantMigrationTest.kConfigRecipientsNS).findOne({
+ tenantId: tenantId
+ });
+ assert.eq(recipientDoc, null);
+ const currOpRecipient = assert.commandWorked(
+ recipientPrimary.adminCommand({currentOp: true, desc: "tenant recipient migration"}));
+ assert.eq(currOpRecipient.inprog.length, 0);
+
+ // Trigger stepup to allow the recipient service to rebuild.
+ assert.commandWorked(recipientPrimary.adminCommand({replSetStepDown: 30, force: true}));
+ recipientPrimary = tenantMigrationTest.getRecipientRst().getPrimary();
+ }
+
+ if (fp) {
+ fp.off();
+ }
+ const migrationOptsAfterDrop = retryWithDifferentMigrationId
+ ? makeMigrationOpts(tenantMigrationTest, tenantId)
+ : migrationOptsBeforeDrop;
+ const runMigrationRes = tenantMigrationTest.runMigration(migrationOptsAfterDrop,
+ false /* retryOnRetryableErrors */,
+ false /* automaticForgetMigration */);
+ if (expectedRunMigrationError) {
+ assert.commandFailedWithCode(runMigrationRes, expectedRunMigrationError);
+ } else {
+ assert.commandWorked(runMigrationRes);
+ if (expectedAbortReason) {
+ assert.eq(runMigrationRes.state, TenantMigrationTest.DonorState.kAborted);
+ assert.eq(runMigrationRes.abortReason.code, expectedAbortReason);
+ } else {
+ assert.eq(runMigrationRes.state, TenantMigrationTest.DonorState.kCommitted);
+ }
+
+ assert.commandWorked(
+ tenantMigrationTest.forgetMigration(migrationOptsAfterDrop.migrationIdString));
+ tenantMigrationTest.waitForMigrationGarbageCollection(
+ UUID(migrationOptsAfterDrop.migrationIdString));
+ }
+
+ if (retryWithDifferentMigrationId && !dropDonorsCollection) {
+ assert(dropRecipientsCollection);
+ // The original migration will still run to completion after the recipient service rebuilds
+ // since the donor will retry the recipientSyncData command on Interrupted error. Wait for
+ // the migration to complete and clean up to avoid concurrent migrations when the next test
+ // case starts.
+ assert.commandWorked(
+ tenantMigrationTest.waitForMigrationToComplete(migrationOptsBeforeDrop));
+ assert.commandWorked(
+ tenantMigrationTest.forgetMigration(migrationOptsBeforeDrop.migrationIdString));
+ tenantMigrationTest.waitForMigrationGarbageCollection(
+ UUID(migrationOptsAfterDrop.migrationIdString));
+ }
+}
+
+const tenantMigrationTest = new TenantMigrationTest({
+ name: jsTestName(),
+ sharedOptions: {
+ setParameter: {
+ tenantMigrationGarbageCollectionDelayMS: 1,
+ ttlMonitorSleepSecs: 1,
+ }
+ },
+ initiateRstWithHighElectionTimeout: false
+});
+
+if (!tenantMigrationTest.isFeatureFlagEnabled()) {
+ jsTestLog("Skipping test because the tenant migrations feature flag is disabled");
+ return;
+}
+
+jsTest.log("Test dropping donor and recipient state doc collections during a migration.");
+kMigrationFpNames.forEach(fpName => {
+ testDroppingStateDocCollections(
+ tenantMigrationTest, fpName, {dropDonorsCollection: true, dropRecipientsCollection: true});
+
+ testDroppingStateDocCollections(tenantMigrationTest, fpName, {
+ dropDonorsCollection: true,
+ dropRecipientsCollection: true,
+ retryWithDifferentMigrationId: true
+ });
+
+ testDroppingStateDocCollections(tenantMigrationTest, fpName, {
+ dropDonorsCollection: false,
+ dropRecipientsCollection: true,
+ expectedAbortReason: (fpName == "abortTenantMigrationBeforeLeavingBlockingState")
+ ? ErrorCodes.InternalError
+ : null
+ });
+
+ testDroppingStateDocCollections(tenantMigrationTest, fpName, {
+ dropDonorsCollection: false,
+ dropRecipientsCollection: true,
+ retryWithDifferentMigrationId: true,
+ // The original migration is still running on the donor so the retry is expected to fail
+ // with ConflictingOperationInProgress.
+ expectedRunMigrationError: ErrorCodes.ConflictingOperationInProgress
+ });
+
+ const sentBlockTimestampToRecipient =
+ (!fpName || fpName == "pauseTenantMigrationBeforeLeavingBlockingState" ||
+ fpName == "abortTenantMigrationBeforeLeavingBlockingState");
+ testDroppingStateDocCollections(tenantMigrationTest, fpName, {
+ dropDonorsCollection: true,
+ dropRecipientsCollection: false,
+ // The retry causes the donor to restart the migration and send a different
+ // returnAfterReachingTimestamp/blockTimestamp to the recipient, which is illegal.
+ expectedAbortReason: sentBlockTimestampToRecipient ? ErrorCodes.IllegalOperation : null
+ });
+});
+
+tenantMigrationTest.stop();
+})();