TEST_F(WiredTigerRecoveryUnitTestFixture, CommitTimestampAfterSetTimestampOnAbort) {
    boost::optional<Timestamp> commitTs = boost::none;
    auto opCtx = clientAndCtx1.second.get();
    Timestamp ts1(5, 5);
    Timestamp ts2(6, 6);

    {
        WriteUnitOfWork wuow(opCtx);
        opCtx->recoveryUnit()->onCommit(
            [&](boost::optional<Timestamp> commitTime) { commitTs = commitTime; });
        ASSERT(!commitTs);
        ASSERT_OK(opCtx->recoveryUnit()->setTimestamp(ts2));
        ASSERT(!commitTs);
    }
    ASSERT(!commitTs);

    opCtx->recoveryUnit()->setCommitTimestamp(ts1);
    ASSERT(!commitTs);

    {
        WriteUnitOfWork wuow(opCtx);
        opCtx->recoveryUnit()->onCommit(
            [&](boost::optional<Timestamp> commitTime) { commitTs = commitTime; });
        ASSERT(!commitTs);
    }
    ASSERT(!commitTs);
}
TEST_F(WiredTigerRecoveryUnitTestFixture, CommitWithDurableTimestamp) {
    auto opCtx = clientAndCtx1.second.get();
    Timestamp ts1(3, 3);
    Timestamp ts2(5, 5);

    opCtx->recoveryUnit()->setCommitTimestamp(ts1);
    opCtx->recoveryUnit()->setDurableTimestamp(ts2);
    auto durableTs = opCtx->recoveryUnit()->getDurableTimestamp();
    ASSERT_EQ(ts2, durableTs);

    {
        WriteUnitOfWork wuow(opCtx);
        wuow.commit();
    }
}
TEST_F(SnapshotManagerTests, FailsWithNoCommittedSnapshot) {
    if (!snapshotManager)
        return;  // This test is only for engines that DO support SnapshotMangers.

    auto op = makeOperation();
    auto ru = op->recoveryUnit();

    // Before first snapshot is created.
    ASSERT_EQ(ru->setReadFromMajorityCommittedSnapshot(),
              ErrorCodes::ReadConcernMajorityNotAvailableYet);

    // There is a snapshot but it isn't committed.
    auto name = prepareAndCreateSnapshot();
    ASSERT_EQ(ru->setReadFromMajorityCommittedSnapshot(),
              ErrorCodes::ReadConcernMajorityNotAvailableYet);

    // Now there is a committed snapshot.
    snapshotManager->setCommittedSnapshot(name);
    ASSERT_OK(ru->setReadFromMajorityCommittedSnapshot());

    // Not anymore!
    snapshotManager->dropAllSnapshots();
    ASSERT_EQ(ru->setReadFromMajorityCommittedSnapshot(),
              ErrorCodes::ReadConcernMajorityNotAvailableYet);
}
Esempio n. 4
0
TEST_F(SnapshotManagerTests, ConsistentIfNotSupported) {
    if (snapshotManager)
        return;  // This test is only for engines that DON'T support SnapshotMangers.

    auto op = makeOperation();
    auto ru = op->recoveryUnit();
    ASSERT(!ru->isReadingFromMajorityCommittedSnapshot());
    ASSERT(!ru->getMajorityCommittedSnapshot());
}
TEST_F(WiredTigerRecoveryUnitTestFixture, ChangeIsPassedCommitTimestamp) {
    boost::optional<Timestamp> commitTs = boost::none;
    auto opCtx = clientAndCtx1.second.get();
    Timestamp ts1(5, 5);

    opCtx->recoveryUnit()->setCommitTimestamp(ts1);
    ASSERT(!commitTs);

    {
        WriteUnitOfWork wuow(opCtx);
        opCtx->recoveryUnit()->onCommit(
            [&](boost::optional<Timestamp> commitTime) { commitTs = commitTime; });
        ASSERT(!commitTs);
        wuow.commit();
        ASSERT_EQ(*commitTs, ts1);
    }
    ASSERT_EQ(*commitTs, ts1);
}
TEST_F(WiredTigerRecoveryUnitTestFixture, CommitWithoutDurableTimestamp) {
    auto opCtx = clientAndCtx1.second.get();
    Timestamp ts1(5, 5);
    opCtx->recoveryUnit()->setCommitTimestamp(ts1);

    {
        WriteUnitOfWork wuow(opCtx);
        wuow.commit();
    }
}
TEST_F(SnapshotManagerTests, FailsAfterDropAllSnapshotsWhileYielded) {
    if (!snapshotManager)
        return;  // This test is only for engines that DO support SnapshotMangers.

    auto op = makeOperation();

    // Start an operation using a committed snapshot.
    auto name = prepareAndCreateSnapshot();
    snapshotManager->setCommittedSnapshot(name);
    ASSERT_OK(op->recoveryUnit()->setReadFromMajorityCommittedSnapshot());
    ASSERT_EQ(itCountOn(op), 0);  // acquires a snapshot.

    // Everything still works until we abandon our snapshot.
    snapshotManager->dropAllSnapshots();
    ASSERT_EQ(itCountOn(op), 0);

    // Now it doesn't.
    op->recoveryUnit()->abandonSnapshot();
    ASSERT_THROWS_CODE(
        itCountOn(op), UserException, ErrorCodes::ReadConcernMajorityNotAvailableYet);
}
TEST_F(WiredTigerRecoveryUnitTestFixture,
       ChangeIsPassedEmptyLastTimestampSetOnCommitWithNoTimestamp) {
    boost::optional<Timestamp> commitTs = boost::none;
    auto opCtx = clientAndCtx1.second.get();
    {
        WriteUnitOfWork wuow(opCtx);
        opCtx->recoveryUnit()->onCommit(
            [&](boost::optional<Timestamp> commitTime) { commitTs = commitTime; });
        wuow.commit();
    }
    ASSERT(!commitTs);
}
ServiceContext::UniqueOperationContext ServiceContext::makeOperationContext(Client* client) {
    auto opCtx = std::make_unique<OperationContext>(client, _nextOpId.fetchAndAdd(1));
    onCreate(opCtx.get(), _clientObservers);
    if (!opCtx->lockState()) {
        opCtx->setLockState(std::make_unique<LockerNoop>());
    }
    if (!opCtx->recoveryUnit()) {
        opCtx->setRecoveryUnit(std::make_unique<RecoveryUnitNoop>(),
                               WriteUnitOfWork::RecoveryUnitState::kNotInUnitOfWork);
    }
    {
        stdx::lock_guard<Client> lk(*client);
        client->setOperationContext(opCtx.get());
    }
    return UniqueOperationContext(opCtx.release());
};
Esempio n. 10
0
void ReplicationRecoveryImpl::_reconstructPreparedTransactions(OperationContext* opCtx) {
    DBDirectClient client(opCtx);
    const auto cursor = client.query(NamespaceString::kSessionTransactionsTableNamespace,
                                     {BSON("state"
                                           << "prepared")});

    // Iterate over each entry in the transactions table that has a prepared transaction.
    while (cursor->more()) {
        const auto txnRecordObj = cursor->next();
        const auto txnRecord = SessionTxnRecord::parse(
            IDLParserErrorContext("recovering prepared transaction"), txnRecordObj);

        invariant(txnRecord.getState() == DurableTxnStateEnum::kPrepared);

        // Get the prepareTransaction oplog entry corresponding to this transactions table entry.
        invariant(!opCtx->recoveryUnit()->getPointInTimeReadTimestamp());
        const auto prepareOpTime = txnRecord.getLastWriteOpTime();
        invariant(!prepareOpTime.isNull());
        TransactionHistoryIterator iter(prepareOpTime);
        invariant(iter.hasNext());
        const auto prepareOplogEntry = iter.next(opCtx);

        {
            // Make a new opCtx so that we can set the lsid when applying the prepare transaction
            // oplog entry.
            auto newClient =
                opCtx->getServiceContext()->makeClient("reconstruct-prepared-transactions");
            AlternativeClientRegion acr(newClient);
            const auto newOpCtx = cc().makeOperationContext();
            repl::UnreplicatedWritesBlock uwb(newOpCtx.get());

            // Snapshot transaction can never conflict with the PBWM lock.
            newOpCtx->lockState()->setShouldConflictWithSecondaryBatchApplication(false);

            // TODO: SERVER-40177 This should be removed once it is guaranteed operations applied on
            // recovering nodes cannot encounter unnecessary prepare conflicts.
            newOpCtx->recoveryUnit()->setIgnorePrepared(true);

            // Checks out the session, applies the operations and prepares the transactions.
            uassertStatusOK(applyRecoveredPrepareTransaction(newOpCtx.get(), prepareOplogEntry));
        }
    }
}
TEST_F(SnapshotManagerTests, BasicFunctionality) {
    if (!snapshotManager)
        return;  // This test is only for engines that DO support SnapshotMangers.

    // Snapshot variables are named according to the size of the RecordStore at the time of the
    // snapshot.
    auto snap0 = prepareAndCreateSnapshot();

    insertRecordAndCommit();
    auto snap1 = prepareAndCreateSnapshot();

    insertRecordAndCommit();
    prepareSnapshot();
    insertRecordAndCommit();
    auto snap2 = createSnapshot();

    {
        auto op = makeOperation();
        WriteUnitOfWork wuow(op);
        insertRecord(op);

        prepareSnapshot();  // insert should still be invisible.
        ASSERT_EQ(itCountOn(snapshotOperation), 3);

        wuow.commit();
    }
    auto snap3 = createSnapshot();

    {
        auto op = makeOperation();
        WriteUnitOfWork wuow(op);
        insertRecord(op);
        // rolling back wuow
    }
    auto snap4 = prepareAndCreateSnapshot();

    // If these fail, everything is busted.
    snapshotManager->setCommittedSnapshot(snap0);
    ASSERT_EQ(itCountCommitted(), 0);
    snapshotManager->setCommittedSnapshot(snap1);
    ASSERT_EQ(itCountCommitted(), 1);

    // If this fails, the snapshot is from the 'create' time rather than the 'prepare' time.
    snapshotManager->setCommittedSnapshot(snap2);
    ASSERT_EQ(itCountCommitted(), 2);

    // If this fails, the snapshot contains writes that weren't yet committed.
    snapshotManager->setCommittedSnapshot(snap3);
    ASSERT_EQ(itCountCommitted(), 3);

    // This op should keep its original snapshot until abandoned.
    auto longOp = makeOperation();
    ASSERT_OK(longOp->recoveryUnit()->setReadFromMajorityCommittedSnapshot());
    ASSERT_EQ(itCountOn(longOp), 3);

    // If this fails, the snapshot contains writes that were rolled back.
    snapshotManager->setCommittedSnapshot(snap4);
    ASSERT_EQ(itCountCommitted(), 4);

    // If this fails, longOp changed snapshots at an illegal time.
    ASSERT_EQ(itCountOn(longOp), 3);

    // If this fails, snapshots aren't preserved while in use.
    snapshotManager->cleanupUnneededSnapshots();
    ASSERT_EQ(itCountOn(longOp), 3);

    // If this fails, longOp didn't get a new snapshot when it should have.
    longOp->recoveryUnit()->abandonSnapshot();
    ASSERT_EQ(itCountOn(longOp), 4);
}