TEST_F(SyncTailTest, MultiInitialSyncApplyPassesThroughSyncApplyErrorAfterFailingToRetryBadOp) { SyncTailWithLocalDocumentFetcher syncTail(BSON("_id" << 0 << "x" << 1)); NamespaceString nss("local." + _agent.getSuiteName() + "_" + _agent.getTestName()); OplogEntry op(BSON("op" << "x" << "ns" << nss.ns())); MultiApplier::OperationPtrs ops = {&op}; ASSERT_EQUALS(ErrorCodes::BadValue, multiInitialSyncApply_noAbort(_txn.get(), &ops, &syncTail)); }
Status ReplicationCoordinatorExternalStateImpl::multiInitialSyncApply( MultiApplier::OperationPtrs* ops, const HostAndPort& source, AtomicUInt32* fetchCount) { // repl::multiInitialSyncApply uses SyncTail::shouldRetry() (and implicitly getMissingDoc()) // to fetch missing documents during initial sync. Therefore, it is fine to construct SyncTail // with invalid BackgroundSync, MultiSyncApplyFunc and writerPool arguments because we will not // be accessing any SyncTail functionality that require these constructor parameters. SyncTail syncTail(nullptr, SyncTail::MultiSyncApplyFunc(), nullptr); syncTail.setHostname(source.toString()); return repl::multiInitialSyncApply(ops, &syncTail, fetchCount); }
TEST_F(SyncTailTest, MultiInitialSyncApplyPassesThroughShouldSyncTailRetryError) { SyncTail syncTail(nullptr, SyncTail::MultiSyncApplyFunc(), nullptr); NamespaceString nss("local." + _agent.getSuiteName() + "_" + _agent.getTestName()); auto op = makeUpdateDocumentOplogEntry( {Timestamp(Seconds(1), 0), 1LL}, nss, BSON("_id" << 0), BSON("_id" << 0 << "x" << 2)); ASSERT_THROWS_CODE( syncTail.shouldRetry(_txn.get(), op.raw), mongo::UserException, ErrorCodes::FailedToParse); MultiApplier::OperationPtrs ops = {&op}; ASSERT_EQUALS(ErrorCodes::FailedToParse, multiInitialSyncApply_noAbort(_txn.get(), &ops, &syncTail)); }
TEST_F(SyncTailTest, MultiInitialSyncApplyDoesNotRetryFailedUpdateIfDocumentIsMissingFromSyncSource) { BSONObj emptyDoc; SyncTailWithLocalDocumentFetcher syncTail(emptyDoc); NamespaceString nss("local." + _agent.getSuiteName() + "_" + _agent.getTestName()); auto op = makeUpdateDocumentOplogEntry( {Timestamp(Seconds(1), 0), 1LL}, nss, BSON("_id" << 0), BSON("_id" << 0 << "x" << 2)); MultiApplier::OperationPtrs ops = {&op}; ASSERT_OK(multiInitialSyncApply_noAbort(_txn.get(), &ops, &syncTail)); // Since the missing document is not found on the sync source, the collection referenced by // the failed operation should not be automatically created. ASSERT_FALSE(AutoGetCollectionForRead(_txn.get(), nss).getCollection()); }
TEST_F(SyncTailTest, MultiInitialSyncApplyRetriesFailedUpdateIfDocumentIsAvailableFromSyncSource) { SyncTailWithLocalDocumentFetcher syncTail(BSON("_id" << 0 << "x" << 1)); NamespaceString nss("local." + _agent.getSuiteName() + "_" + _agent.getTestName()); auto updatedDocument = BSON("_id" << 0 << "x" << 2); auto op = makeUpdateDocumentOplogEntry( {Timestamp(Seconds(1), 0), 1LL}, nss, BSON("_id" << 0), updatedDocument); MultiApplier::OperationPtrs ops = {&op}; ASSERT_OK(multiInitialSyncApply_noAbort(_txn.get(), &ops, &syncTail)); // The collection referenced by "ns" in the failed operation is automatically created to hold // the missing document fetched from the sync source. We verify the contents of the collection // with the OplogInterfaceLocal class. OplogInterfaceLocal collectionReader(_txn.get(), nss.ns()); auto iter = collectionReader.makeIterator(); ASSERT_EQUALS(updatedDocument, unittest::assertGet(iter->next()).first); ASSERT_EQUALS(ErrorCodes::CollectionIsEmpty, iter->next().getStatus()); }
TEST_F(SyncTailTest, MultiInitialSyncApplySkipsDocumentOnNamespaceNotFound) { BSONObj emptyDoc; SyncTailWithLocalDocumentFetcher syncTail(emptyDoc); NamespaceString nss("local." + _agent.getSuiteName() + "_" + _agent.getTestName()); NamespaceString badNss("local." + _agent.getSuiteName() + "_" + _agent.getTestName() + "bad"); auto doc1 = BSON("_id" << 1); auto doc2 = BSON("_id" << 2); auto doc3 = BSON("_id" << 3); auto op0 = makeCreateCollectionOplogEntry({Timestamp(Seconds(1), 0), 1LL}, nss); auto op1 = makeInsertDocumentOplogEntry({Timestamp(Seconds(2), 0), 1LL}, nss, doc1); auto op2 = makeInsertDocumentOplogEntry({Timestamp(Seconds(3), 0), 1LL}, badNss, doc2); auto op3 = makeInsertDocumentOplogEntry({Timestamp(Seconds(4), 0), 1LL}, nss, doc3); MultiApplier::OperationPtrs ops = {&op0, &op1, &op2, &op3}; ASSERT_OK(multiInitialSyncApply_noAbort(_txn.get(), &ops, &syncTail)); OplogInterfaceLocal collectionReader(_txn.get(), nss.ns()); auto iter = collectionReader.makeIterator(); ASSERT_EQUALS(doc3, unittest::assertGet(iter->next()).first); ASSERT_EQUALS(doc1, unittest::assertGet(iter->next()).first); ASSERT_EQUALS(ErrorCodes::CollectionIsEmpty, iter->next().getStatus()); }