diff options
author | Geert Bosch <geert@mongodb.com> | 2017-12-08 09:50:02 -0500 |
---|---|---|
committer | Geert Bosch <geert@mongodb.com> | 2017-12-21 14:35:48 -0500 |
commit | 826e020e3a23582c93c42a2986504ae567ff027f (patch) | |
tree | 9ed1c76dd99d4d42c548b465f171c032832b3689 /src/mongo/db | |
parent | fb8046d813af032d6d51327affbab9b6199fe654 (diff) | |
download | mongo-826e020e3a23582c93c42a2986504ae567ff027f.tar.gz |
SERVER-29602 New OpObserverRegistry to allow multiple observers
Also removes sharding specific interfaces for OpObserver.
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/SConscript | 23 | ||||
-rw-r--r-- | src/mongo/db/catalog/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_impl.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/catalog/rename_collection_test.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/catalog/uuid_catalog.cpp | 58 | ||||
-rw-r--r-- | src/mongo/db/catalog/uuid_catalog.h | 66 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/op_observer.h | 13 | ||||
-rw-r--r-- | src/mongo/db/op_observer_impl.cpp | 52 | ||||
-rw-r--r-- | src/mongo/db/op_observer_impl.h | 7 | ||||
-rw-r--r-- | src/mongo/db/op_observer_impl_test.cpp | 35 | ||||
-rw-r--r-- | src/mongo/db/op_observer_noop.cpp | 152 | ||||
-rw-r--r-- | src/mongo/db/op_observer_noop.h | 42 | ||||
-rw-r--r-- | src/mongo/db/op_observer_registry.h | 190 | ||||
-rw-r--r-- | src/mongo/db/op_observer_registry_test.cpp | 175 | ||||
-rw-r--r-- | src/mongo/db/repl/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/oplog.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/rs_rollback_test.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/s/collection_sharding_state.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/s/collection_sharding_state.h | 4 | ||||
-rw-r--r-- | src/mongo/db/service_context.h | 3 |
21 files changed, 607 insertions, 249 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 59f48c539e8..223d0a2c4bf 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -116,6 +116,14 @@ env.CppUnitTest( ) env.CppUnitTest( + target= 'op_observer_registry_test', + source= 'op_observer_registry_test.cpp', + LIBDEPS=[ + 'common', + ], +) + +env.CppUnitTest( target= 'op_observer_impl_test', source= 'op_observer_impl_test.cpp', LIBDEPS=[ @@ -609,9 +617,9 @@ env.Library( "op_observer_impl.cpp", ], LIBDEPS=[ - '$BUILD_DIR/mongo/base', 'repl/serveronly', 'views/views_mongod', + '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/catalog/uuid_catalog', ], ) @@ -1432,24 +1440,11 @@ env.CppUnitTest( ) env.Library( - target= 'op_observer_noop', - source= [ - 'op_observer_noop.cpp', - ], - LIBDEPS= [ - '$BUILD_DIR/mongo/db/repl/optime', - '$BUILD_DIR/mongo/db/catalog/uuid_catalog', - '$BUILD_DIR/mongo/db/catalog/database_holder', - ], -) - -env.Library( target= 'service_context_d_test_fixture', source= [ 'service_context_d_test_fixture.cpp', ], LIBDEPS= [ - 'op_observer_noop', '$BUILD_DIR/mongo/db/serveronly', '$BUILD_DIR/mongo/db/storage/storage_options', '$BUILD_DIR/mongo/unittest/unittest', diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript index 7dbceb9ea6d..7b0be6a4d5e 100644 --- a/src/mongo/db/catalog/SConscript +++ b/src/mongo/db/catalog/SConscript @@ -189,6 +189,7 @@ env.Library( LIBDEPS=[ 'collection', 'database', + 'database_holder', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/namespace_string', '$BUILD_DIR/mongo/db/storage/storage_options', diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp index 94e8e232620..84412ca2a29 100644 --- a/src/mongo/db/catalog/collection_impl.cpp +++ b/src/mongo/db/catalog/collection_impl.cpp @@ -546,9 +546,7 @@ void CollectionImpl::deleteDocument(OperationContext* opCtx, } Snapshotted<BSONObj> doc = docFor(opCtx, loc); - - auto deleteState = - getGlobalServiceContext()->getOpObserver()->aboutToDelete(opCtx, ns(), doc.value()); + getGlobalServiceContext()->getOpObserver()->aboutToDelete(opCtx, ns(), doc.value()); boost::optional<BSONObj> deletedDoc; if (storeDeletedDoc == Collection::StoreDeletedDoc::On) { @@ -567,7 +565,7 @@ void CollectionImpl::deleteDocument(OperationContext* opCtx, _recordStore->deleteRecord(opCtx, loc); getGlobalServiceContext()->getOpObserver()->onDelete( - opCtx, ns(), uuid(), stmtId, std::move(deleteState), fromMigrate, deletedDoc); + opCtx, ns(), uuid(), stmtId, fromMigrate, deletedDoc); } Counter64 moveCounter; diff --git a/src/mongo/db/catalog/rename_collection_test.cpp b/src/mongo/db/catalog/rename_collection_test.cpp index 5c021ca13ee..43c06682d10 100644 --- a/src/mongo/db/catalog/rename_collection_test.cpp +++ b/src/mongo/db/catalog/rename_collection_test.cpp @@ -36,6 +36,7 @@ #include "mongo/db/catalog/collection_options.h" #include "mongo/db/catalog/index_create.h" #include "mongo/db/catalog/rename_collection.h" +#include "mongo/db/catalog/uuid_catalog.h" #include "mongo/db/client.h" #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/db_raii.h" @@ -43,6 +44,7 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/op_observer.h" #include "mongo/db/op_observer_noop.h" +#include "mongo/db/op_observer_registry.h" #include "mongo/db/operation_context.h" #include "mongo/db/repl/drop_pending_collection_reaper.h" #include "mongo/db/repl/oplog.h" @@ -231,8 +233,11 @@ void RenameCollectionTest::setUp() { ASSERT_OK(_replCoord->setFollowerMode(repl::MemberState::RS_PRIMARY)); // Use OpObserverMock to track notifications for collection and database drops. - auto opObserver = stdx::make_unique<OpObserverMock>(); - _opObserver = opObserver.get(); + auto opObserver = stdx::make_unique<OpObserverRegistry>(); + auto mockObserver = stdx::make_unique<OpObserverMock>(); + _opObserver = mockObserver.get(); + opObserver->addObserver(std::move(mockObserver)); + opObserver->addObserver(stdx::make_unique<UUIDCatalogObserver>()); service->setOpObserver(std::move(opObserver)); _sourceNss = NamespaceString("test.foo"); diff --git a/src/mongo/db/catalog/uuid_catalog.cpp b/src/mongo/db/catalog/uuid_catalog.cpp index d9bf9812949..9c52ff8134a 100644 --- a/src/mongo/db/catalog/uuid_catalog.cpp +++ b/src/mongo/db/catalog/uuid_catalog.cpp @@ -32,7 +32,9 @@ #include "uuid_catalog.h" #include "mongo/db/catalog/database.h" +#include "mongo/db/catalog/database_holder.h" #include "mongo/db/storage/recovery_unit.h" +#include "mongo/util/assert_util.h" #include "mongo/util/log.h" #include "mongo/util/uuid.h" @@ -42,6 +44,62 @@ const ServiceContext::Decoration<UUIDCatalog> getCatalog = ServiceContext::declareDecoration<UUIDCatalog>(); } // namespace +void UUIDCatalogObserver::onCreateCollection(OperationContext* opCtx, + Collection* coll, + const NamespaceString& collectionName, + const CollectionOptions& options, + const BSONObj& idIndex) { + if (!options.uuid) + return; + UUIDCatalog& catalog = UUIDCatalog::get(opCtx); + catalog.onCreateCollection(opCtx, coll, options.uuid.get()); +} + +void UUIDCatalogObserver::onCollMod(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + const BSONObj& collModCmd, + const CollectionOptions& oldCollOptions, + boost::optional<TTLCollModInfo> ttlInfo) { + if (!uuid) + return; + UUIDCatalog& catalog = UUIDCatalog::get(opCtx); + Collection* catalogColl = catalog.lookupCollectionByUUID(uuid.get()); + invariant(catalogColl->uuid() == uuid, uuid + "," + catalogColl->uuid); +} + +repl::OpTime UUIDCatalogObserver::onDropCollection(OperationContext* opCtx, + const NamespaceString& collectionName, + OptionalCollectionUUID uuid) { + + if (!uuid) + return {}; + UUIDCatalog& catalog = UUIDCatalog::get(opCtx); + catalog.onDropCollection(opCtx, uuid.get()); + return {}; +} + +repl::OpTime UUIDCatalogObserver::onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + bool dropTarget, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { + + if (!uuid) + return {}; + auto getNewCollection = [opCtx, toCollection] { + auto db = dbHolder().get(opCtx, toCollection.db()); + auto newColl = db->getCollection(opCtx, toCollection); + invariant(newColl); + return newColl; + }; + UUIDCatalog& catalog = UUIDCatalog::get(opCtx); + catalog.onRenameCollection(opCtx, getNewCollection, uuid.get()); + return {}; +} + UUIDCatalog& UUIDCatalog::get(ServiceContext* svcCtx) { return getCatalog(svcCtx); } diff --git a/src/mongo/db/catalog/uuid_catalog.h b/src/mongo/db/catalog/uuid_catalog.h index ba09f04a998..b4486f89aa2 100644 --- a/src/mongo/db/catalog/uuid_catalog.h +++ b/src/mongo/db/catalog/uuid_catalog.h @@ -32,11 +32,77 @@ #include "mongo/base/disallow_copying.h" #include "mongo/db/catalog/collection.h" +#include "mongo/db/op_observer.h" #include "mongo/db/service_context.h" #include "mongo/stdx/functional.h" #include "mongo/util/uuid.h" namespace mongo { +/** + * Class used for updating the UUID catalog on metadata operations. + */ +class UUIDCatalogObserver : public OpObserver { +public: + void onCreateIndex(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + BSONObj indexDoc, + bool fromMigrate) override {} + void onInserts(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + std::vector<InsertStatement>::const_iterator begin, + std::vector<InsertStatement>::const_iterator end, + bool fromMigrate) override {} + void onUpdate(OperationContext* opCtx, const OplogUpdateEntryArgs& args) override {} + void aboutToDelete(OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& doc) override {} + void onDelete(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + StmtId stmtId, + bool fromMigrate, + const boost::optional<BSONObj>& deletedDoc) override {} + void onInternalOpMessage(OperationContext* opCtx, + const NamespaceString& nss, + const boost::optional<UUID> uuid, + const BSONObj& msgObj, + const boost::optional<BSONObj> o2MsgObj) override {} + void onCreateCollection(OperationContext* opCtx, + Collection* coll, + const NamespaceString& collectionName, + const CollectionOptions& options, + const BSONObj& idIndex) override; + void onCollMod(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + const BSONObj& collModCmd, + const CollectionOptions& oldCollOptions, + boost::optional<TTLCollModInfo> ttlInfo) override; + void onDropDatabase(OperationContext* opCtx, const std::string& dbName) override {} + repl::OpTime onDropCollection(OperationContext* opCtx, + const NamespaceString& collectionName, + OptionalCollectionUUID uuid) override; + void onDropIndex(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + const std::string& indexName, + const BSONObj& idxDescriptor) override {} + repl::OpTime onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + bool dropTarget, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; + void onApplyOps(OperationContext* opCtx, + const std::string& dbName, + const BSONObj& applyOpCmd) override {} + void onEmptyCapped(OperationContext* opCtx, + const NamespaceString& collectionName, + OptionalCollectionUUID uuid) override {} +}; /** * This class comprises a UUID to collection catalog, allowing for efficient diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 4fefe009b16..5d369cd38ba 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -58,6 +58,7 @@ #include "mongo/db/catalog/health_log.h" #include "mongo/db/catalog/index_catalog.h" #include "mongo/db/catalog/index_key_validate.h" +#include "mongo/db/catalog/uuid_catalog.h" #include "mongo/db/client.h" #include "mongo/db/clientcursor.h" #include "mongo/db/commands/feature_compatibility_version.h" @@ -89,6 +90,7 @@ #include "mongo/db/logical_time_validator.h" #include "mongo/db/mongod_options.h" #include "mongo/db/op_observer_impl.h" +#include "mongo/db/op_observer_registry.h" #include "mongo/db/operation_context.h" #include "mongo/db/query/internal_plans.h" #include "mongo/db/repair_database.h" @@ -689,7 +691,10 @@ ExitCode _initAndListen(int listenPort) { auto serviceContext = checked_cast<ServiceContextMongoD*>(getGlobalServiceContext()); serviceContext->setFastClockSource(FastClockSourceFactory::create(Milliseconds(10))); - serviceContext->setOpObserver(stdx::make_unique<OpObserverImpl>()); + auto opObserverRegistry = stdx::make_unique<OpObserverRegistry>(); + opObserverRegistry->addObserver(stdx::make_unique<OpObserverImpl>()); + opObserverRegistry->addObserver(stdx::make_unique<UUIDCatalogObserver>()); + serviceContext->setOpObserver(std::move(opObserverRegistry)); DBDirectClientFactory::get(serviceContext).registerImplementation([](OperationContext* opCtx) { return std::unique_ptr<DBClientBase>(new DBDirectClient(opCtx)); diff --git a/src/mongo/db/op_observer.h b/src/mongo/db/op_observer.h index 53c2a79f0e5..080e31d5782 100644 --- a/src/mongo/db/op_observer.h +++ b/src/mongo/db/op_observer.h @@ -84,12 +84,7 @@ struct TTLCollModInfo { }; class OpObserver { - MONGO_DISALLOW_COPYING(OpObserver); - public: - OpObserver() = default; - virtual ~OpObserver() = default; - virtual void onCreateIndex(OperationContext* opCtx, const NamespaceString& nss, OptionalCollectionUUID uuid, @@ -102,14 +97,13 @@ public: std::vector<InsertStatement>::const_iterator end, bool fromMigrate) = 0; virtual void onUpdate(OperationContext* opCtx, const OplogUpdateEntryArgs& args) = 0; - virtual CollectionShardingState::DeleteState aboutToDelete(OperationContext* opCtx, - const NamespaceString& nss, - const BSONObj& doc) = 0; + virtual void aboutToDelete(OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& doc) = 0; /** * Handles logging before document is deleted. * * "ns" name of the collection from which deleteState.idDoc will be deleted. - * "deleteState" holds information about the deleted document. * "fromMigrate" indicates whether the delete was induced by a chunk migration, and * so should be ignored by the user as an internal maintenance operation and not a * real delete. @@ -118,7 +112,6 @@ public: const NamespaceString& nss, OptionalCollectionUUID uuid, StmtId stmtId, - CollectionShardingState::DeleteState deleteState, bool fromMigrate, const boost::optional<BSONObj>& deletedDoc) = 0; /** diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp index 66445bb839a..fddf1b3dc45 100644 --- a/src/mongo/db/op_observer_impl.cpp +++ b/src/mongo/db/op_observer_impl.cpp @@ -37,7 +37,6 @@ #include "mongo/db/catalog/database.h" #include "mongo/db/catalog/database_holder.h" #include "mongo/db/catalog/namespace_uuid_cache.h" -#include "mongo/db/catalog/uuid_catalog.h" #include "mongo/db/commands/feature_compatibility_version.h" #include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/index/index_descriptor.h" @@ -58,6 +57,11 @@ namespace { MONGO_FP_DECLARE(failCollectionUpdates); +using DeleteState = CollectionShardingState::DeleteState; + +const OperationContext::Decoration<DeleteState> getDeleteState = + OperationContext::declareDecoration<DeleteState>(); + /** * Returns whether we're a master using master-slave replication. */ @@ -203,7 +207,6 @@ OpTimeBundle replLogDelete(OperationContext* opCtx, OptionalCollectionUUID uuid, Session* session, StmtId stmtId, - const CollectionShardingState::DeleteState& deleteState, bool fromMigrate, const boost::optional<BSONObj>& deletedDoc) { OperationSessionInfo sessionInfo; @@ -234,6 +237,7 @@ OpTimeBundle replLogDelete(OperationContext* opCtx, oplogLink.preImageOpTime = noteOplog; } + CollectionShardingState::DeleteState& deleteState = getDeleteState(opCtx); opTimes.writeOpTime = repl::logOp(opCtx, "d", nss, @@ -407,27 +411,31 @@ void OpObserverImpl::onUpdate(OperationContext* opCtx, const OplogUpdateEntryArg opTime.wallClockTime); } -auto OpObserverImpl::aboutToDelete(OperationContext* opCtx, +void OpObserverImpl::aboutToDelete(OperationContext* opCtx, NamespaceString const& nss, - BSONObj const& doc) -> CollectionShardingState::DeleteState { + BSONObj const& doc) { + auto& deleteState = getDeleteState(opCtx); auto* css = CollectionShardingState::get(opCtx, nss.ns()); - return css->makeDeleteState(doc); + deleteState = css->makeDeleteState(doc); + deleteState.aboutToDeleteCalled = true; } void OpObserverImpl::onDelete(OperationContext* opCtx, const NamespaceString& nss, OptionalCollectionUUID uuid, StmtId stmtId, - CollectionShardingState::DeleteState deleteState, bool fromMigrate, const boost::optional<BSONObj>& deletedDoc) { + auto& deleteState = getDeleteState(opCtx); + invariant(deleteState.aboutToDeleteCalled); + deleteState.aboutToDeleteCalled = false; + if (deleteState.documentKey.isEmpty()) { return; } Session* const session = opCtx->getTxnNumber() ? OperationContextSession::get(opCtx) : nullptr; - const auto opTime = - replLogDelete(opCtx, nss, uuid, session, stmtId, deleteState, fromMigrate, deletedDoc); + const auto opTime = replLogDelete(opCtx, nss, uuid, session, stmtId, fromMigrate, deletedDoc); AuthorizationManager::get(opCtx->getServiceContext()) ->logOp(opCtx, "d", nss, deleteState.documentKey, nullptr); @@ -523,8 +531,6 @@ void OpObserverImpl::onCreateCollection(OperationContext* opCtx, ->logOp(opCtx, "c", cmdNss, cmdObj, nullptr); if (options.uuid) { - UUIDCatalog& catalog = UUIDCatalog::get(opCtx); - catalog.onCreateCollection(opCtx, coll, options.uuid.get()); opCtx->recoveryUnit()->onRollback([opCtx, collectionName]() { NamespaceUUIDCache::get(opCtx).evictNamespace(collectionName); }); @@ -582,12 +588,6 @@ void OpObserverImpl::onCollMod(OperationContext* opCtx, invariant(coll->uuid() == uuid); CollectionCatalogEntry* entry = coll->getCatalogEntry(); invariant(entry->isEqualToMetadataUUID(opCtx, uuid)); - - if (uuid) { - UUIDCatalog& catalog = UUIDCatalog::get(opCtx->getServiceContext()); - Collection* catalogColl = catalog.lookupCollectionByUUID(uuid.get()); - invariant(catalogColl && catalogColl->uuid() == uuid); - } } void OpObserverImpl::onDropDatabase(OperationContext* opCtx, const std::string& dbName) { @@ -626,7 +626,7 @@ repl::OpTime OpObserverImpl::onDropCollection(OperationContext* opCtx, repl::OpTime dropOpTime; if (!collectionName.isSystemDotProfile()) { - // Do not replicate system.profile modifications + // Do not replicate system.profile modifications. dropOpTime = repl::logOp(opCtx, "c", cmdNss, @@ -657,12 +657,6 @@ repl::OpTime OpObserverImpl::onDropCollection(OperationContext* opCtx, // Evict namespace entry from the namespace/uuid cache if it exists. NamespaceUUIDCache::get(opCtx).evictNamespace(collectionName); - // Remove collection from the uuid catalog. - if (uuid) { - UUIDCatalog& catalog = UUIDCatalog::get(opCtx); - catalog.onDropCollection(opCtx, uuid.get()); - } - return dropOpTime; } @@ -738,18 +732,6 @@ repl::OpTime OpObserverImpl::onRenameCollection(OperationContext* opCtx, opCtx->recoveryUnit()->onRollback( [&cache, toCollection]() { cache.evictNamespace(toCollection); }); - // Finally update the UUID Catalog. - if (uuid) { - auto getNewCollection = [opCtx, toCollection] { - auto db = dbHolder().get(opCtx, toCollection.db()); - auto newColl = db->getCollection(opCtx, toCollection); - invariant(newColl); - return newColl; - }; - UUIDCatalog& catalog = UUIDCatalog::get(opCtx); - catalog.onRenameCollection(opCtx, getNewCollection, uuid.get()); - } - return renameOpTime; } diff --git a/src/mongo/db/op_observer_impl.h b/src/mongo/db/op_observer_impl.h index e5395abdd22..1d26d54a411 100644 --- a/src/mongo/db/op_observer_impl.h +++ b/src/mongo/db/op_observer_impl.h @@ -51,14 +51,13 @@ public: std::vector<InsertStatement>::const_iterator end, bool fromMigrate) override; void onUpdate(OperationContext* opCtx, const OplogUpdateEntryArgs& args) override; - CollectionShardingState::DeleteState aboutToDelete(OperationContext* opCtx, - const NamespaceString& nss, - const BSONObj& doc) override; + void aboutToDelete(OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& doc) override; void onDelete(OperationContext* opCtx, const NamespaceString& nss, OptionalCollectionUUID uuid, StmtId stmtId, - CollectionShardingState::DeleteState deleteState, bool fromMigrate, const boost::optional<BSONObj>& deletedDoc) override; void onInternalOpMessage(OperationContext* opCtx, diff --git a/src/mongo/db/op_observer_impl_test.cpp b/src/mongo/db/op_observer_impl_test.cpp index f9e3f8079f0..ac5c2d15902 100644 --- a/src/mongo/db/op_observer_impl_test.cpp +++ b/src/mongo/db/op_observer_impl_test.cpp @@ -29,6 +29,7 @@ #include "mongo/db/op_observer_impl.h" #include "mongo/db/client.h" +#include "mongo/db/concurrency/locker_noop.h" #include "mongo/db/db_raii.h" #include "mongo/db/field_parser.h" #include "mongo/db/repl/oplog.h" @@ -36,7 +37,7 @@ #include "mongo/db/repl/repl_client_info.h" #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/service_context_d_test_fixture.h" - +#include "mongo/unittest/death_test.h" namespace mongo { namespace { @@ -238,5 +239,37 @@ TEST_F(OpObserverTest, OnRenameCollectionReturnsRenameOpTime) { ASSERT_EQUALS(repl::ReplClientInfo::forClient(&cc()).getLastOp(), renameOpTime); } +TEST_F(OpObserverTest, MultipleAboutToDeleteAndOnDelete) { + OpObserverImpl opObserver; + auto opCtx = cc().makeOperationContext(); + opCtx->releaseLockState(); + opCtx->setLockState(stdx::make_unique<LockerNoop>()); + NamespaceString nss = {"test", "coll"}; + opObserver.aboutToDelete(opCtx.get(), nss, {}); + opObserver.onDelete(opCtx.get(), nss, {}, {}, false, {}); + opObserver.aboutToDelete(opCtx.get(), nss, {}); + opObserver.onDelete(opCtx.get(), nss, {}, {}, false, {}); +} + +DEATH_TEST_F(OpObserverTest, AboutToDeleteMustPreceedOnDelete, "invariant") { + OpObserverImpl opObserver; + auto opCtx = cc().makeOperationContext(); + opCtx->releaseLockState(); + opCtx->setLockState(stdx::make_unique<LockerNoop>()); + NamespaceString nss = {"test", "coll"}; + opObserver.onDelete(opCtx.get(), nss, {}, {}, false, {}); +} + +DEATH_TEST_F(OpObserverTest, EachOnDeleteRequiresAboutToDelete, "invariant") { + OpObserverImpl opObserver; + auto opCtx = cc().makeOperationContext(); + opCtx->releaseLockState(); + opCtx->setLockState(stdx::make_unique<LockerNoop>()); + NamespaceString nss = {"test", "coll"}; + opObserver.aboutToDelete(opCtx.get(), nss, {}); + opObserver.onDelete(opCtx.get(), nss, {}, {}, false, {}); + opObserver.onDelete(opCtx.get(), nss, {}, {}, false, {}); +} + } // namespace } // namespace mongo diff --git a/src/mongo/db/op_observer_noop.cpp b/src/mongo/db/op_observer_noop.cpp deleted file mode 100644 index 66c3eae95bd..00000000000 --- a/src/mongo/db/op_observer_noop.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/** -* Copyright (C) 2016 MongoDB Inc. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License, version 3, -* as published by the Free Software Foundation. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see <http://www.gnu.org/licenses/>. -* -* As a special exception, the copyright holders give permission to link the -* code of portions of this program with the OpenSSL library under certain -* conditions as described in each individual source file and distribute -* linked combinations including the program with the OpenSSL library. You -* must comply with the GNU Affero General Public License in all respects for -* all of the code used other than as permitted herein. If you modify file(s) -* with this exception, you may extend this exception to your version of the -* file(s), but you are not obligated to do so. If you do not wish to do so, -* delete this exception statement from your version. If you delete this -* exception statement from all source files in the program, then also delete -* it in the license file. -*/ - -#include "mongo/platform/basic.h" - -#include "mongo/db/catalog/collection.h" -#include "mongo/db/catalog/database.h" -#include "mongo/db/catalog/database_holder.h" -#include "mongo/db/catalog/namespace_uuid_cache.h" -#include "mongo/db/catalog/uuid_catalog.h" -#include "mongo/db/op_observer_noop.h" - -namespace mongo { - -void OpObserverNoop::onCreateIndex( - OperationContext*, const NamespaceString&, OptionalCollectionUUID, BSONObj, bool) {} - -void OpObserverNoop::onInserts(OperationContext*, - const NamespaceString&, - OptionalCollectionUUID, - std::vector<InsertStatement>::const_iterator, - std::vector<InsertStatement>::const_iterator, - bool) {} - -void OpObserverNoop::onUpdate(OperationContext*, const OplogUpdateEntryArgs&) {} - -CollectionShardingState::DeleteState OpObserverNoop::aboutToDelete(OperationContext*, - const NamespaceString&, - const BSONObj&) { - return {}; -} - -void OpObserverNoop::onDelete(OperationContext*, - const NamespaceString&, - OptionalCollectionUUID, - StmtId stmtId, - CollectionShardingState::DeleteState, - bool fromMigrate, - const boost::optional<BSONObj>& deletedDoc) {} - -void OpObserverNoop::onInternalOpMessage(OperationContext*, - const NamespaceString&, - const boost::optional<UUID>, - const BSONObj&, - const boost::optional<BSONObj>) {} - -void OpObserverNoop::onCreateCollection(OperationContext* opCtx, - Collection* coll, - const NamespaceString& collectionName, - const CollectionOptions& options, - const BSONObj& idIndex) { - if (options.uuid) { - UUIDCatalog& catalog = UUIDCatalog::get(opCtx); - catalog.onCreateCollection(opCtx, coll, options.uuid.get()); - NamespaceUUIDCache& cache = NamespaceUUIDCache::get(opCtx); - opCtx->recoveryUnit()->onRollback( - [&cache, collectionName]() { cache.evictNamespace(collectionName); }); - } -} - -void OpObserverNoop::onCollMod(OperationContext*, - const NamespaceString&, - OptionalCollectionUUID, - const BSONObj&, - const CollectionOptions& oldCollOptions, - boost::optional<TTLCollModInfo> ttlInfo) {} - -void OpObserverNoop::onDropDatabase(OperationContext*, const std::string&) {} - -repl::OpTime OpObserverNoop::onDropCollection(OperationContext* opCtx, - const NamespaceString& collectionName, - OptionalCollectionUUID uuid) { - // Evict namespace entry from the namespace/uuid cache if it exists. - NamespaceUUIDCache& cache = NamespaceUUIDCache::get(opCtx); - cache.evictNamespace(collectionName); - - // Remove collection from the uuid catalog. - if (uuid) { - UUIDCatalog& catalog = UUIDCatalog::get(opCtx); - catalog.onDropCollection(opCtx, uuid.get()); - } - - return {}; -} - -void OpObserverNoop::onDropIndex(OperationContext*, - const NamespaceString&, - OptionalCollectionUUID, - const std::string&, - const BSONObj&) {} - -repl::OpTime OpObserverNoop::onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - bool dropTarget, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) { - // Evict namespace entry from the namespace/uuid cache if it exists. - NamespaceUUIDCache& cache = NamespaceUUIDCache::get(opCtx); - cache.evictNamespace(fromCollection); - cache.evictNamespace(toCollection); - opCtx->recoveryUnit()->onRollback( - [&cache, toCollection]() { cache.evictNamespace(toCollection); }); - - // Finally update the UUID Catalog. - if (uuid) { - auto getNewCollection = [opCtx, toCollection] { - auto db = dbHolder().get(opCtx, toCollection.db()); - auto newColl = db->getCollection(opCtx, toCollection); - invariant(newColl); - return newColl; - }; - UUIDCatalog& catalog = UUIDCatalog::get(opCtx); - catalog.onRenameCollection(opCtx, getNewCollection, uuid.get()); - } - - return {}; -} - -void OpObserverNoop::onApplyOps(OperationContext*, const std::string&, const BSONObj&) {} - -void OpObserverNoop::onEmptyCapped(OperationContext*, - const NamespaceString&, - OptionalCollectionUUID) {} - -} // namespace mongo diff --git a/src/mongo/db/op_observer_noop.h b/src/mongo/db/op_observer_noop.h index 66f64fe69e3..12aa572c231 100644 --- a/src/mongo/db/op_observer_noop.h +++ b/src/mongo/db/op_observer_noop.h @@ -33,72 +33,70 @@ namespace mongo { class OpObserverNoop : public OpObserver { - MONGO_DISALLOW_COPYING(OpObserverNoop); - public: - OpObserverNoop() = default; - virtual ~OpObserverNoop() = default; - void onCreateIndex(OperationContext* opCtx, const NamespaceString& nss, OptionalCollectionUUID uuid, BSONObj indexDoc, - bool fromMigrate) override; + bool fromMigrate) override {} void onInserts(OperationContext* opCtx, const NamespaceString& nss, OptionalCollectionUUID uuid, std::vector<InsertStatement>::const_iterator begin, std::vector<InsertStatement>::const_iterator end, - bool fromMigrate) override; - void onUpdate(OperationContext* opCtx, const OplogUpdateEntryArgs& args) override; - CollectionShardingState::DeleteState aboutToDelete(OperationContext* opCtx, - const NamespaceString& nss, - const BSONObj& doc) override; + bool fromMigrate) override {} + void onUpdate(OperationContext* opCtx, const OplogUpdateEntryArgs& args) override{}; + void aboutToDelete(OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& doc) override {} void onDelete(OperationContext* opCtx, const NamespaceString& nss, OptionalCollectionUUID uuid, StmtId stmtId, - CollectionShardingState::DeleteState deleteState, bool fromMigrate, - const boost::optional<BSONObj>& deletedDoc) override; + const boost::optional<BSONObj>& deletedDoc) override {} void onInternalOpMessage(OperationContext* opCtx, const NamespaceString& nss, const boost::optional<UUID> uuid, const BSONObj& msgObj, - const boost::optional<BSONObj> o2MsgObj) override; + const boost::optional<BSONObj> o2MsgObj) override {} void onCreateCollection(OperationContext* opCtx, Collection* coll, const NamespaceString& collectionName, const CollectionOptions& options, - const BSONObj& idIndex) override; + const BSONObj& idIndex) override {} void onCollMod(OperationContext* opCtx, const NamespaceString& nss, OptionalCollectionUUID uuid, const BSONObj& collModCmd, const CollectionOptions& oldCollOptions, - boost::optional<TTLCollModInfo> ttlInfo) override; - void onDropDatabase(OperationContext* opCtx, const std::string& dbName) override; + boost::optional<TTLCollModInfo> ttlInfo) override {} + void onDropDatabase(OperationContext* opCtx, const std::string& dbName) override {} repl::OpTime onDropCollection(OperationContext* opCtx, const NamespaceString& collectionName, - OptionalCollectionUUID uuid) override; + OptionalCollectionUUID uuid) override { + return {}; + } void onDropIndex(OperationContext* opCtx, const NamespaceString& nss, OptionalCollectionUUID uuid, const std::string& indexName, - const BSONObj& idxDescriptor) override; + const BSONObj& idxDescriptor) override {} repl::OpTime onRenameCollection(OperationContext* opCtx, const NamespaceString& fromCollection, const NamespaceString& toCollection, OptionalCollectionUUID uuid, bool dropTarget, OptionalCollectionUUID dropTargetUUID, - bool stayTemp) override; + bool stayTemp) override { + return {}; + } void onApplyOps(OperationContext* opCtx, const std::string& dbName, - const BSONObj& applyOpCmd) override; + const BSONObj& applyOpCmd) override {} void onEmptyCapped(OperationContext* opCtx, const NamespaceString& collectionName, - OptionalCollectionUUID uuid); + OptionalCollectionUUID uuid) override {} }; } // namespace mongo diff --git a/src/mongo/db/op_observer_registry.h b/src/mongo/db/op_observer_registry.h new file mode 100644 index 00000000000..f9d5c9bb860 --- /dev/null +++ b/src/mongo/db/op_observer_registry.h @@ -0,0 +1,190 @@ +/** + * Copyright 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include <algorithm> +#include <memory> +#include <vector> + +#include "mongo/db/op_observer.h" + +namespace mongo { + +/** + * Implementation of the OpObserver interface that allows multiple observers to be registered. + * All observers will be called in order of registration. Once an observer throws an exception, + * no further observers will receive notifications: typically the enclosing transaction will be + * aborted. If an observer needs to undo changes in such a case, it should register an onRollback + * handler with the recovery unit. + */ +class OpObserverRegistry final : public OpObserver { + MONGO_DISALLOW_COPYING(OpObserverRegistry); + +public: + OpObserverRegistry() = default; + virtual ~OpObserverRegistry() = default; + + // Add 'observer' to the list of observers to call. Observers are called in registration order. + // Registration must be done while no calls to observers are made. + void addObserver(std::unique_ptr<OpObserver> observer) { + _observers.emplace_back(std::move(observer)); + } + + void onCreateIndex(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + BSONObj indexDoc, + bool fromMigrate) override { + for (auto& o : _observers) + o->onCreateIndex(opCtx, nss, uuid, indexDoc, fromMigrate); + } + + void onInserts(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + std::vector<InsertStatement>::const_iterator begin, + std::vector<InsertStatement>::const_iterator end, + bool fromMigrate) override { + for (auto& o : _observers) + o->onInserts(opCtx, nss, uuid, begin, end, fromMigrate); + } + + void onUpdate(OperationContext* opCtx, const OplogUpdateEntryArgs& args) override { + for (auto& o : _observers) + o->onUpdate(opCtx, args); + } + + void aboutToDelete(OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& doc) override { + for (auto& o : _observers) + o->aboutToDelete(opCtx, nss, doc); + } + + void onDelete(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + StmtId stmtId, + bool fromMigrate, + const boost::optional<BSONObj>& deletedDoc) override { + for (auto& o : _observers) + o->onDelete(opCtx, nss, uuid, stmtId, fromMigrate, deletedDoc); + } + + void onInternalOpMessage(OperationContext* opCtx, + const NamespaceString& nss, + const boost::optional<UUID> uuid, + const BSONObj& msgObj, + const boost::optional<BSONObj> o2MsgObj) override { + for (auto& o : _observers) + o->onInternalOpMessage(opCtx, nss, uuid, msgObj, o2MsgObj); + } + + void onCreateCollection(OperationContext* opCtx, + Collection* coll, + const NamespaceString& collectionName, + const CollectionOptions& options, + const BSONObj& idIndex) override { + for (auto& o : _observers) + o->onCreateCollection(opCtx, coll, collectionName, options, idIndex); + } + + void onCollMod(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + const BSONObj& collModCmd, + const CollectionOptions& oldCollOptions, + boost::optional<TTLCollModInfo> ttlInfo) override { + for (auto& o : _observers) + o->onCollMod(opCtx, nss, uuid, collModCmd, oldCollOptions, ttlInfo); + } + + void onDropDatabase(OperationContext* opCtx, const std::string& dbName) override { + for (auto& o : _observers) + o->onDropDatabase(opCtx, dbName); + } + repl::OpTime onDropCollection(OperationContext* opCtx, + const NamespaceString& collectionName, + OptionalCollectionUUID uuid) override { + return _forEachObserver([&](auto& observer) -> repl::OpTime { + return observer.onDropCollection(opCtx, collectionName, uuid); + }); + } + + void onDropIndex(OperationContext* opCtx, + const NamespaceString& nss, + OptionalCollectionUUID uuid, + const std::string& indexName, + const BSONObj& idxDescriptor) override { + for (auto& o : _observers) + o->onDropIndex(opCtx, nss, uuid, indexName, idxDescriptor); + } + + repl::OpTime onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + bool dropTarget, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override { + return _forEachObserver([&](auto& observer) -> repl::OpTime { + return observer.onRenameCollection( + opCtx, fromCollection, toCollection, uuid, dropTarget, dropTargetUUID, stayTemp); + }); + } + + void onApplyOps(OperationContext* opCtx, + const std::string& dbName, + const BSONObj& applyOpCmd) override { + for (auto& o : _observers) + o->onApplyOps(opCtx, dbName, applyOpCmd); + } + + void onEmptyCapped(OperationContext* opCtx, + const NamespaceString& collectionName, + OptionalCollectionUUID uuid) { + for (auto& o : _observers) + o->onEmptyCapped(opCtx, collectionName, uuid); + } + +private: + repl::OpTime _forEachObserver(stdx::function<repl::OpTime(OpObserver&)> f) { + repl::OpTime opTime; + for (auto& observer : _observers) { + repl::OpTime newTime = f(*observer); + if (!newTime.isNull() && newTime != opTime) { + invariant(opTime.isNull()); + opTime = newTime; + } + } + return opTime; + } + std::vector<std::unique_ptr<OpObserver>> _observers; +}; +} // namespace mongo diff --git a/src/mongo/db/op_observer_registry_test.cpp b/src/mongo/db/op_observer_registry_test.cpp new file mode 100644 index 00000000000..6447962e323 --- /dev/null +++ b/src/mongo/db/op_observer_registry_test.cpp @@ -0,0 +1,175 @@ +/** + * Copyright (C) 2017 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/db/op_observer_registry.h" +#include "mongo/db/op_observer_noop.h" +#include "mongo/db/repl/optime.h" +#include "mongo/unittest/death_test.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { +int testObservers = 0; +struct TestObserver : public OpObserverNoop { + TestObserver() { + testObservers++; + } + virtual ~TestObserver() { + testObservers--; + } + int drops = 0; + repl::OpTime opTime; + + void onDropDatabase(OperationContext* opCtx, const std::string& dbName) { + drops++; + } + repl::OpTime onDropCollection(OperationContext* opCtx, + const NamespaceString& collectionName, + OptionalCollectionUUID uuid) { + drops++; + return opTime; + } + repl::OpTime onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + bool dropTarget, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { + return opTime; + } +}; + +struct ThrowingObserver : public TestObserver { + void onDropDatabase(OperationContext* opCtx, const std::string& dbName) { + drops++; + uasserted(ErrorCodes::InternalError, "throwing observer"); + } +}; + +struct OpObserverRegistryTest : public unittest::Test { + NamespaceString testNss = {"test", "coll"}; + std::unique_ptr<TestObserver> unique1 = stdx::make_unique<TestObserver>(); + std::unique_ptr<TestObserver> unique2 = stdx::make_unique<TestObserver>(); + TestObserver* observer1 = unique1.get(); + TestObserver* observer2 = unique2.get(); + OpObserverRegistry registry; + /** + * The 'op' function calls an observer method on the registry that returns an OpTime. + * The method checks that the registry correctly merges the results of the registered observers. + */ + void checkConsistentOpTime(stdx::function<repl::OpTime()> op) { + const repl::OpTime myTime(Timestamp(1, 1), 1); + ASSERT(op() == repl::OpTime()); + observer1->opTime = myTime; + ASSERT(op() == myTime); + observer2->opTime = myTime; + ASSERT(op() == myTime); + observer1->opTime = {}; + ASSERT(op() == myTime); + } + + /** + * The 'op' function calls an observer method on the registry that returns an OpTime. + * The method checks that the registry invariants if the observers return conflicting times. + */ + void checkInconsistentOpTime(stdx::function<repl::OpTime()> op) { + observer1->opTime = repl::OpTime(Timestamp(1, 1), 1); + observer2->opTime = repl::OpTime(Timestamp(2, 2), 2); + op(); // This will invariant because of inconsistent timestamps: for death test. + } +}; + +TEST_F(OpObserverRegistryTest, NoObservers) { + // Check that it's OK to call observer methods with no observers registered. + registry.onDropDatabase(nullptr, "test"); +} + +TEST_F(OpObserverRegistryTest, TwoObservers) { + ASSERT_EQUALS(testObservers, 2); + registry.addObserver(std::move(unique1)); + registry.addObserver(std::move(unique2)); + registry.onDropDatabase(nullptr, "test"); + ASSERT_EQUALS(observer1->drops, 1); + ASSERT_EQUALS(observer2->drops, 1); +} + +TEST_F(OpObserverRegistryTest, ThrowingObserver1) { + unique1 = stdx::make_unique<ThrowingObserver>(); + observer1 = unique1.get(); + registry.addObserver(std::move(unique1)); + registry.addObserver(std::move(unique2)); + ASSERT_THROWS(registry.onDropDatabase(nullptr, "test"), AssertionException); + ASSERT_EQUALS(observer1->drops, 1); + ASSERT_EQUALS(observer2->drops, 0); +} + +TEST_F(OpObserverRegistryTest, ThrowingObserver2) { + unique2 = stdx::make_unique<ThrowingObserver>(); + observer2 = unique1.get(); + registry.addObserver(std::move(unique1)); + registry.addObserver(std::move(unique2)); + ASSERT_THROWS(registry.onDropDatabase(nullptr, "test"), AssertionException); + ASSERT_EQUALS(observer1->drops, 1); + ASSERT_EQUALS(observer2->drops, 1); +} + +TEST_F(OpObserverRegistryTest, OnDropCollectionObserverResultReturnsRightTime) { + registry.addObserver(std::move(unique1)); + registry.addObserver(std::move(unique2)); + auto op = [&]() -> repl::OpTime { return registry.onDropCollection(nullptr, testNss, {}); }; + checkConsistentOpTime(op); +} + +TEST_F(OpObserverRegistryTest, OnRenameCollectionObserverResultReturnsRightTime) { + registry.addObserver(std::move(unique1)); + registry.addObserver(std::move(unique2)); + auto op = [&]() -> repl::OpTime { + return registry.onRenameCollection(nullptr, testNss, testNss, {}, false, {}, false); + }; + checkConsistentOpTime(op); +} + +DEATH_TEST_F(OpObserverRegistryTest, OnDropCollectionReturnsInconsistentTime, "invariant") { + registry.addObserver(std::move(unique1)); + registry.addObserver(std::move(unique2)); + auto op = [&]() -> repl::OpTime { return registry.onDropCollection(nullptr, testNss, {}); }; + checkInconsistentOpTime(op); +} + +DEATH_TEST_F(OpObserverRegistryTest, OnRenameCollectionReturnsInconsistentTime, "invariant") { + registry.addObserver(std::move(unique1)); + registry.addObserver(std::move(unique2)); + auto op = [&]() -> repl::OpTime { + return registry.onRenameCollection(nullptr, testNss, testNss, {}, false, {}, false); + }; + checkInconsistentOpTime(op); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index 0934da02f22..007584df837 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -49,7 +49,6 @@ env.CppUnitTest( 'oplog_entry', 'replmocks', 'storage_interface_impl', - '$BUILD_DIR/mongo/db/op_observer_noop', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', '$BUILD_DIR/mongo/rpc/command_status', ], @@ -80,7 +79,6 @@ env.CppUnitTest( 'oplog_entry', 'replmocks', 'storage_interface_impl', - '$BUILD_DIR/mongo/db/op_observer_noop', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', '$BUILD_DIR/mongo/rpc/command_status', ], diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index e5d5f222c64..7e3eb5bd2f3 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -417,6 +417,8 @@ OpTime logOp(OperationContext* opCtx, StmtId statementId, const OplogLink& oplogLink) { auto replCoord = ReplicationCoordinator::get(opCtx); + // For commands, the test below is on the command ns and therefore does not check for + // specific namespaces such as system.profile. This is the caller's responsibility. if (replCoord->isOplogDisabledFor(opCtx, nss)) { invariant(statementId == kUninitializedStmtId); return {}; diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp index fc5ec478aa3..5e310eaf9aa 100644 --- a/src/mongo/db/repl/rs_rollback_test.cpp +++ b/src/mongo/db/repl/rs_rollback_test.cpp @@ -46,6 +46,8 @@ #include "mongo/db/index/index_descriptor.h" #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" +#include "mongo/db/op_observer_noop.h" +#include "mongo/db/op_observer_registry.h" #include "mongo/db/repl/drop_pending_collection_reaper.h" #include "mongo/db/repl/oplog.h" #include "mongo/db/repl/oplog_interface.h" @@ -76,6 +78,10 @@ private: void RSRollbackTest::setUp() { RollbackTest::setUp(); enableCollectionUUIDs = true; + auto observerRegistry = stdx::make_unique<OpObserverRegistry>(); + observerRegistry->addObserver(stdx::make_unique<UUIDCatalogObserver>()); + _serviceContextMongoDTest.getServiceContext()->setOpObserver( + std::unique_ptr<OpObserver>(observerRegistry.release())); } void RSRollbackTest::tearDown() { diff --git a/src/mongo/db/s/collection_sharding_state.cpp b/src/mongo/db/s/collection_sharding_state.cpp index 2458d9861c5..0c045dc7111 100644 --- a/src/mongo/db/s/collection_sharding_state.cpp +++ b/src/mongo/db/s/collection_sharding_state.cpp @@ -340,7 +340,8 @@ void CollectionShardingState::onUpdateOp(OperationContext* opCtx, auto CollectionShardingState::makeDeleteState(BSONObj const& doc) -> DeleteState { return {getMetadata().extractDocumentKey(doc).getOwned(), - _sourceMgr && _sourceMgr->getCloner()->isDocumentInMigratingChunk(doc)}; + _sourceMgr && _sourceMgr->getCloner()->isDocumentInMigratingChunk(doc), + /*aboutToDeleteCalled*/ false}; } void CollectionShardingState::onDeleteOp(OperationContext* opCtx, diff --git a/src/mongo/db/s/collection_sharding_state.h b/src/mongo/db/s/collection_sharding_state.h index 7af282f3463..c3e6861b71f 100644 --- a/src/mongo/db/s/collection_sharding_state.h +++ b/src/mongo/db/s/collection_sharding_state.h @@ -79,6 +79,10 @@ public: // is being migrated out. (Not to be confused with "fromMigrate", which tags operations // that are steps in performing the migration.) bool isMigrating; + + // For verifying the protocol used by OpObserver that aboutToDelete must be called before + // onDelete. Set by the OpObserverImpl::aboutToDelete and cleared by onDelete. + bool aboutToDeleteCalled; }; DeleteState makeDeleteState(BSONObj const& doc); diff --git a/src/mongo/db/service_context.h b/src/mongo/db/service_context.h index 681bbdc6262..775fd60d0e4 100644 --- a/src/mongo/db/service_context.h +++ b/src/mongo/db/service_context.h @@ -380,7 +380,8 @@ public: void setOpObserver(std::unique_ptr<OpObserver> opObserver); /** - * Return the OpObserver instance we're using. + * Return the OpObserver instance we're using. This may be an OpObserverRegistry that in fact + * contains multiple observers. */ OpObserver* getOpObserver() const { return _opObserver.get(); |