117 lines
5.1 KiB
JavaScript
117 lines
5.1 KiB
JavaScript
/**
|
|
* Tests that during tenant migration, when the migration has reached but not completed the
|
|
* migration committed state, a new node added to the donor replica set is still able to
|
|
* successfully complete initial sync.
|
|
*
|
|
* @tags: [
|
|
* # Tenant migrations will not be run with the ephemeralForTest storage engine in production.
|
|
* incompatible_with_eft,
|
|
* # Running tenant migrations on macOS depends on plumbing transient SSL params through the
|
|
* # Apple SSL manager (see SERVER-56100).
|
|
* incompatible_with_macos,
|
|
* # Shard merge not resilient to failover, which will be caused by issuing startClean on an
|
|
* # existing node to force an initial sync.
|
|
* incompatible_with_shard_merge,
|
|
* # Running tenant migrations on Windows with TLS depends on plumbing transient SSL params
|
|
* # through the Windows SSL manager (see SERVER-53883 and SERVER-53763).
|
|
* incompatible_with_windows_tls,
|
|
* # failpoint pauseTenantMigrationBeforeLeavingCommittedState only exists on the latest branch.
|
|
* requires_fcv_60,
|
|
* # Tenant migrations will not be run with enableMajorityReadConcern=false in production.
|
|
* requires_majority_read_concern,
|
|
* # Tenant migrations will not be run with the inMemory storage engine in production.
|
|
* requires_persistence,
|
|
* # Tenant migrations are only used in serverless.
|
|
* serverless,
|
|
* ]
|
|
*/
|
|
|
|
(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/rslib.js'); // for waitForNewlyAddedRemovalForNodeToBeCommitted
|
|
|
|
const testDBName = 'testDB';
|
|
const testCollName = 'testColl';
|
|
const tenantId = 'tenantId1';
|
|
|
|
const tenantMigrationTest = new TenantMigrationTest({
|
|
name: jsTestName(),
|
|
allowStaleReadsOnDonor: false // turn off tenantMigrationDonorAllowsNonTimestampedReads fp
|
|
});
|
|
|
|
// This test does the following:
|
|
// 1. Configures a failpoint on the donor primary
|
|
// 2. Starts a tenant migration.
|
|
// 3. Waits for the donor failpoint to be hit. Restarts a node to undergo initial sync.
|
|
// 4. Makes sure the initial sync completes successfully
|
|
// 5. Releases the donor failpoint, and allows the migration to complete.
|
|
|
|
const migrationOpts = {
|
|
migrationIdString: extractUUIDFromObject(UUID()),
|
|
tenantId: tenantId
|
|
};
|
|
const dbName = tenantMigrationTest.tenantDB(tenantId, testDBName);
|
|
const donorRst = tenantMigrationTest.getDonorRst();
|
|
const originalDonorPrimary = tenantMigrationTest.getDonorPrimary();
|
|
|
|
// In order to validate the fix, we want to ensure we hit a server failpoint that will cause an
|
|
// error should the fix be bypassed/fail. Since initial sync does not produce a readTimestamp, this
|
|
// means hitting the BlockerState::State::kRejected in order to generate an error in
|
|
// getCanReadFuture, which can only happen after the state doc has been updated to kCommitted.
|
|
const fpOnDonor =
|
|
configureFailPoint(originalDonorPrimary, "pauseTenantMigrationBeforeLeavingCommittedState");
|
|
tenantMigrationTest.insertDonorDB(dbName, testCollName);
|
|
|
|
jsTestLog(`Starting a tenant migration with migrationID ${
|
|
migrationOpts.migrationIdString}, and tenantId ${tenantId}`);
|
|
assert.commandWorked(tenantMigrationTest.startMigration(migrationOpts));
|
|
|
|
jsTestLog("Waiting for failpoint where read access blockers are enabled on donor");
|
|
fpOnDonor.wait();
|
|
|
|
// Restart a node and allow it to undergo initial sync.
|
|
jsTestLog("Restarting a node from the donor replica set.");
|
|
|
|
let initialSyncNode = donorRst.getSecondaries()[0];
|
|
initialSyncNode = donorRst.restart(initialSyncNode, {startClean: true, skipValidation: true});
|
|
|
|
// Allow the new node to finish initial sync.
|
|
waitForNewlyAddedRemovalForNodeToBeCommitted(originalDonorPrimary,
|
|
donorRst.getNodeId(initialSyncNode));
|
|
donorRst.awaitSecondaryNodes();
|
|
donorRst.awaitReplication();
|
|
|
|
fpOnDonor.off();
|
|
|
|
jsTestLog("Ensure that the new node's documents match up with the primary's.");
|
|
|
|
// reenable tenantMigrationDonorAllowsNonTimestampedReads to allow us to read from both nodes
|
|
// without hitting reroute errors
|
|
configureFailPoint(
|
|
originalDonorPrimary, "tenantMigrationDonorAllowsNonTimestampedReads", {mode: 'alwaysOn'});
|
|
configureFailPoint(
|
|
initialSyncNode, "tenantMigrationDonorAllowsNonTimestampedReads", {mode: 'alwaysOn'});
|
|
|
|
// Make sure the documents on the new node matches that on the donor primary
|
|
let donorDocOnPrimary = undefined;
|
|
let donorDocOnNewNode = undefined;
|
|
assert.soon(() => {
|
|
donorDocOnPrimary = originalDonorPrimary.getDB(dbName).getCollection(testCollName).findOne();
|
|
donorDocOnNewNode = initialSyncNode.getDB(dbName).getCollection(testCollName).findOne();
|
|
|
|
return donorDocOnPrimary.state == donorDocOnNewNode.state;
|
|
}, `Documents never matched, primary: ${donorDocOnPrimary}, on new node: ${donorDocOnNewNode}`);
|
|
|
|
// Allow the migration to run to completion.
|
|
jsTestLog("Allowing migration to run to completion.");
|
|
TenantMigrationTest.assertCommitted(tenantMigrationTest.waitForMigrationToComplete(migrationOpts));
|
|
|
|
assert.commandWorked(tenantMigrationTest.forgetMigration(migrationOpts.migrationIdString));
|
|
|
|
tenantMigrationTest.stop();
|
|
})();
|