summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeert Bosch <geert@mongodb.com>2017-12-08 09:50:02 -0500
committerGeert Bosch <geert@mongodb.com>2017-12-21 14:35:48 -0500
commit826e020e3a23582c93c42a2986504ae567ff027f (patch)
tree9ed1c76dd99d4d42c548b465f171c032832b3689
parentfb8046d813af032d6d51327affbab9b6199fe654 (diff)
downloadmongo-826e020e3a23582c93c42a2986504ae567ff027f.tar.gz
SERVER-29602 New OpObserverRegistry to allow multiple observers
Also removes sharding specific interfaces for OpObserver.
-rw-r--r--src/mongo/db/SConscript23
-rw-r--r--src/mongo/db/catalog/SConscript1
-rw-r--r--src/mongo/db/catalog/collection_impl.cpp6
-rw-r--r--src/mongo/db/catalog/rename_collection_test.cpp9
-rw-r--r--src/mongo/db/catalog/uuid_catalog.cpp58
-rw-r--r--src/mongo/db/catalog/uuid_catalog.h66
-rw-r--r--src/mongo/db/db.cpp7
-rw-r--r--src/mongo/db/op_observer.h13
-rw-r--r--src/mongo/db/op_observer_impl.cpp52
-rw-r--r--src/mongo/db/op_observer_impl.h7
-rw-r--r--src/mongo/db/op_observer_impl_test.cpp35
-rw-r--r--src/mongo/db/op_observer_noop.cpp152
-rw-r--r--src/mongo/db/op_observer_noop.h42
-rw-r--r--src/mongo/db/op_observer_registry.h190
-rw-r--r--src/mongo/db/op_observer_registry_test.cpp175
-rw-r--r--src/mongo/db/repl/SConscript2
-rw-r--r--src/mongo/db/repl/oplog.cpp2
-rw-r--r--src/mongo/db/repl/rs_rollback_test.cpp6
-rw-r--r--src/mongo/db/s/collection_sharding_state.cpp3
-rw-r--r--src/mongo/db/s/collection_sharding_state.h4
-rw-r--r--src/mongo/db/service_context.h3
-rw-r--r--src/mongo/dbtests/SConscript1
-rw-r--r--src/mongo/dbtests/framework.cpp7
23 files changed, 612 insertions, 252 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();
diff --git a/src/mongo/dbtests/SConscript b/src/mongo/dbtests/SConscript
index f40bf0a4bba..d86bdcb1095 100644
--- a/src/mongo/dbtests/SConscript
+++ b/src/mongo/dbtests/SConscript
@@ -25,7 +25,6 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/db/dbdirectclient',
- '$BUILD_DIR/mongo/db/op_observer_noop',
'$BUILD_DIR/mongo/db/service_context_d',
'$BUILD_DIR/mongo/db/s/sharding',
'$BUILD_DIR/mongo/s/coreshard',
diff --git a/src/mongo/dbtests/framework.cpp b/src/mongo/dbtests/framework.cpp
index 5a6e6099150..9ea6d7c9add 100644
--- a/src/mongo/dbtests/framework.cpp
+++ b/src/mongo/dbtests/framework.cpp
@@ -36,10 +36,11 @@
#include "mongo/base/checked_cast.h"
#include "mongo/base/status.h"
+#include "mongo/db/catalog/uuid_catalog.h"
#include "mongo/db/client.h"
#include "mongo/db/concurrency/lock_state.h"
#include "mongo/db/dbdirectclient.h"
-#include "mongo/db/op_observer_noop.h"
+#include "mongo/db/op_observer_registry.h"
#include "mongo/db/s/sharding_state.h"
#include "mongo/db/service_context.h"
#include "mongo/db/service_context_d.h"
@@ -90,7 +91,9 @@ int runDbTests(int argc, char** argv) {
checked_cast<ServiceContextMongoD*>(globalServiceContext)->createLockFile();
globalServiceContext->initializeGlobalStorageEngine();
- globalServiceContext->setOpObserver(stdx::make_unique<OpObserverNoop>());
+ auto registry = stdx::make_unique<OpObserverRegistry>();
+ registry->addObserver(stdx::make_unique<UUIDCatalogObserver>());
+ globalServiceContext->setOpObserver(std::move(registry));
int ret = unittest::Suite::run(frameworkGlobalParams.suites,
frameworkGlobalParams.filter,