jstests/replsets/tenant_migration_concurrent_reads_on_recipient.js
/**
 * Tests if the migration aborts after rejectReadsBeforeTimestamp is set, the recipient keeps
 * rejecting reads with atClusterTime < rejectReadsBeforeTimestamp until the state doc is garbage
 * collected.
 */
Tests if the migration aborts after rejectReadsBeforeTimestamp is set, the recipient keeps
rejecting reads with atClusterTime < rejectReadsBeforeTimestamp until the state doc is garbage
collected.
Tests if the migration aborts after rejectReadsBeforeTimestamp is set the recipient keeps rejecting reads with atClusterTime < rejectReadsBeforeTimestamp until the state doc is garbage collected .
function testDoNotRejectReadsAfterMigrationAbortedAfterReachingRejectReadsBeforeTimestamp(
    {testCase, dbName, collName, tenantMigrationTest}) {
    const tenantId = dbName.split('_')[0];
    const migrationId = UUID();
    const migrationOpts = {
        migrationIdString: extractUUIDFromObject(migrationId),
        tenantId,
    };
    const donorRst = tenantMigrationTest.getDonorRst();
    const donorPrimary = donorRst.getPrimary();
    const recipientRst = tenantMigrationTest.getRecipientRst();
    const recipientPrimary = recipientRst.getPrimary();
    const setParametersCmd = {
        setParameter: 1,
        tenantMigrationGarbageCollectionDelayMS: 3 * 1000,
        ttlMonitorSleepSecs: 1,
    };
    donorRst.nodes.forEach(node => {
        assert.commandWorked(node.adminCommand(setParametersCmd));
    });
    recipientRst.nodes.forEach(node => {
        assert.commandWorked(node.adminCommand(setParametersCmd));
    });
    const preMigrationTimestamp = getLastOpTime(donorPrimary).ts;
    let abortFp =
        configureFailPoint(donorPrimary, "abortTenantMigrationBeforeLeavingBlockingState");
    TenantMigrationTest.assertAborted(
        tenantMigrationTest.runMigration(migrationOpts, {automaticForgetMigration: false}));
    abortFp.off();
    recipientRst.awaitLastOpCommitted();
    const recipientDoc =
        recipientPrimary.getCollection(TenantMigrationTest.kConfigRecipientsNS).findOne({
            tenantId: tenantId
        });
    const nodes = testCase.isSupportedOnSecondaries ? recipientRst.nodes : [recipientPrimary];
    nodes.forEach(node => {
        const db = node.getDB(dbName);
        if (testCase.requiresReadTimestamp) {
            runCommand(
                db, testCase.command(collName, preMigrationTimestamp), ErrorCodes.SnapshotTooOld);
            runCommand(
                db, testCase.command(collName, recipientDoc.rejectReadsBeforeTimestamp), null);
        } else {
            runCommand(db, testCase.command(collName), null);
        }
    });
    assert.commandWorked(tenantMigrationTest.forgetMigration(migrationOpts.migrationIdString));
    tenantMigrationTest.waitForMigrationGarbageCollection(migrationId, migrationOpts.tenantId);
    nodes.forEach(node => {
        const db = node.getDB(dbName);
        if (testCase.requiresReadTimestamp) {
            runCommand(db, testCase.command(collName, preMigrationTimestamp), null);
            runCommand(
                db, testCase.command(collName, recipientDoc.rejectReadsBeforeTimestamp), null);
        } else {
            runCommand(db, testCase.command(collName), null);
        }
    });
}
function testDoNotRejectReadsAfterMigrationAbortedAfterReachingRejectReadsBeforeTimestamp ( { testCase , dbName , collName , tenantMigrationTest } ) { const tenantId = dbName . split ( '_' ) [ 0 ] ; const migrationId = UUID ( ) ; const migrationOpts = { migrationIdString : extractUUIDFromObject ( migrationId ) , tenantId , } ; const donorRst = tenantMigrationTest . getDonorRst ( ) ; const donorPrimary = donorRst . getPrimary ( ) ; const recipientRst = tenantMigrationTest . getRecipientRst ( ) ; const recipientPrimary = recipientRst . getPrimary ( ) ; const setParametersCmd = { setParameter : 1 , tenantMigrationGarbageCollectionDelayMS : 3 * 1000 , ttlMonitorSleepSecs : 1 , } ; donorRst . nodes . forEach ( node => { assert . commandWorked ( node . adminCommand ( setParametersCmd ) ) ; } ) ; recipientRst . nodes . forEach ( node => { assert . commandWorked ( node . adminCommand ( setParametersCmd ) ) ; } ) ; const preMigrationTimestamp = getLastOpTime ( donorPrimary ) . ts ; let abortFp = configureFailPoint ( donorPrimary , "abortTenantMigrationBeforeLeavingBlockingState" ) ; TenantMigrationTest . assertAborted ( tenantMigrationTest . runMigration ( migrationOpts , { automaticForgetMigration : false } ) ) ; abortFp . off ( ) ; recipientRst . awaitLastOpCommitted ( ) ; const recipientDoc = recipientPrimary . getCollection ( TenantMigrationTest . kConfigRecipientsNS ) . findOne ( { tenantId : tenantId } ) ; const nodes = testCase . isSupportedOnSecondaries ? recipientRst . nodes : [ recipientPrimary ] ; nodes . forEach ( node => { const db = node . getDB ( dbName ) ; if ( testCase . requiresReadTimestamp ) { runCommand ( db , testCase . command ( collName , preMigrationTimestamp ) , ErrorCodes . SnapshotTooOld ) ; runCommand ( db , testCase . command ( collName , recipientDoc . rejectReadsBeforeTimestamp ) , null ) ; } else { runCommand ( db , testCase . command ( collName ) , null ) ; } } ) ; assert . commandWorked ( tenantMigrationTest . forgetMigration ( migrationOpts . migrationIdString ) ) ; tenantMigrationTest . waitForMigrationGarbageCollection ( migrationId , migrationOpts . tenantId ) ; nodes . forEach ( node => { const db = node . getDB ( dbName ) ; if ( testCase . requiresReadTimestamp ) { runCommand ( db , testCase . command ( collName , preMigrationTimestamp ) , null ) ; runCommand ( db , testCase . command ( collName , recipientDoc . rejectReadsBeforeTimestamp ) , null ) ; } else { runCommand ( db , testCase . command ( collName ) , null ) ; } } ) ; }
Tests if the migration aborts after rejectReadsBeforeTimestamp is set, the recipient keeps
 rejecting reads with atClusterTime < rejectReadsBeforeTimestamp until the state doc is garbage
 collected.
Tests if the migration aborts after rejectReadsBeforeTimestamp is set the recipient keeps rejecting reads with atClusterTime < rejectReadsBeforeTimestamp until the state doc is garbage collected .
// Set the delay before a state doc is garbage collected to be short to speed up the test. // Select a read timestamp < rejectReadsBeforeTimestamp. // Force the donor to abort the migration right after the recipient responds to the second // recipientSyncData (i.e. after it has reached the returnAfterReachingTimestamp). // Wait for the last oplog entry on the primary to be visible in the committed snapshot view of // the oplog on all the secondaries. This is to ensure that snapshot reads on secondaries with // unspecified atClusterTime have read timestamp >= rejectReadsBeforeTimestamp. // Untimestamped reads are not rejected after the recipient has applied data past the // rejectReadsBeforeTimestamp. Snapshot reads with unspecified atClusterTime should have // read timestamp >= rejectReadsBeforeTimestamp so are also not rejected.