diff options
Diffstat (limited to 'jstests/replsets/tenant_migration_drop_state_doc_collection.js')
-rw-r--r-- | jstests/replsets/tenant_migration_drop_state_doc_collection.js | 211 |
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(); +})(); |