diff options
Diffstat (limited to 'src/mongo/idl')
| -rw-r--r-- | src/mongo/idl/SConscript | 20 | ||||
| -rw-r--r-- | src/mongo/idl/cluster_parameter_synchronization_helpers.cpp | 147 | ||||
| -rw-r--r-- | src/mongo/idl/cluster_parameter_synchronization_helpers.h | 105 | ||||
| -rw-r--r-- | src/mongo/idl/cluster_server_parameter_initializer.cpp | 95 | ||||
| -rw-r--r-- | src/mongo/idl/cluster_server_parameter_initializer.h | 46 | ||||
| -rw-r--r-- | src/mongo/idl/cluster_server_parameter_initializer_test.cpp | 3 | ||||
| -rw-r--r-- | src/mongo/idl/cluster_server_parameter_op_observer.cpp | 63 | ||||
| -rw-r--r-- | src/mongo/idl/cluster_server_parameter_op_observer.h | 19 | ||||
| -rw-r--r-- | src/mongo/idl/cluster_server_parameter_op_observer_test.cpp | 173 |
9 files changed, 390 insertions, 281 deletions
diff --git a/src/mongo/idl/SConscript b/src/mongo/idl/SConscript index 2fcbf03c37a..a95eb570aa5 100644 --- a/src/mongo/idl/SConscript +++ b/src/mongo/idl/SConscript @@ -74,7 +74,23 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/dbdirectclient', '$BUILD_DIR/mongo/db/repl/replica_set_aware_service', - ] + 'cluster_parameter_synchronization_helpers', + ], +) + +env.Library( + target='cluster_parameter_synchronization_helpers', + source=[ + 'cluster_parameter_synchronization_helpers.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/audit', + '$BUILD_DIR/mongo/db/db_raii', + '$BUILD_DIR/mongo/db/logical_time', + ], ) env.Library( @@ -88,6 +104,7 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/dbdirectclient', '$BUILD_DIR/mongo/db/op_observer', + 'cluster_parameter_synchronization_helpers', 'cluster_server_parameter_initializer', ], ) @@ -136,6 +153,7 @@ env.CppUnitTest( '$BUILD_DIR/mongo/db/repl/storage_interface_impl', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', '$BUILD_DIR/mongo/util/signal_handlers', + 'cluster_parameter_synchronization_helpers', 'cluster_server_parameter_initializer', 'cluster_server_parameter_test_parameter', ], diff --git a/src/mongo/idl/cluster_parameter_synchronization_helpers.cpp b/src/mongo/idl/cluster_parameter_synchronization_helpers.cpp new file mode 100644 index 00000000000..1566c42fa8e --- /dev/null +++ b/src/mongo/idl/cluster_parameter_synchronization_helpers.cpp @@ -0,0 +1,147 @@ +/** + * Copyright (C) 2023-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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. + */ +#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kControl + +#include "mongo/idl/cluster_parameter_synchronization_helpers.h" + +#include "mongo/base/string_data.h" +#include "mongo/db/audit.h" +#include "mongo/db/catalog_raii.h" +#include "mongo/db/multitenancy_gen.h" +#include "mongo/logv2/log.h" + +namespace mongo::cluster_parameters { + +constexpr auto kIdField = "_id"_sd; +constexpr auto kCPTField = "clusterParameterTime"_sd; +constexpr auto kOplog = "oplog"_sd; + +void updateParameter(BSONObj doc, StringData mode) { + auto nameElem = doc[kIdField]; + if (nameElem.type() != String) { + LOGV2_DEBUG(6226301, + 1, + "Update with invalid cluster server parameter name", + "mode"_attr = mode, + "_id"_attr = nameElem); + return; + } + + auto name = nameElem.valueStringData(); + auto* sp = ServerParameterSet::getClusterParameterSet()->getIfExists(name); + if (!sp) { + LOGV2_DEBUG(6226300, + 3, + "Update to unknown cluster server parameter", + "mode"_attr = mode, + "name"_attr = name); + return; + } + + auto cptElem = doc[kCPTField]; + if ((cptElem.type() != mongo::Date) && (cptElem.type() != bsonTimestamp)) { + LOGV2_DEBUG(6226302, + 1, + "Update to cluster server parameter has invalid clusterParameterTime", + "mode"_attr = mode, + "name"_attr = name, + "clusterParameterTime"_attr = cptElem); + return; + } + + uassertStatusOK(sp->set(doc)); +} + +void clearParameter(ServerParameter* sp) { + if (sp->getClusterParameterTime() == LogicalTime::kUninitialized) { + // Nothing to clear. + return; + } + + uassertStatusOK(sp->reset()); +} + +void clearParameter(StringData id) { + auto* sp = ServerParameterSet::getClusterParameterSet()->getIfExists(id); + if (!sp) { + LOGV2_DEBUG(6226303, + 5, + "oplog event deletion of unknown cluster server parameter", + "name"_attr = id); + return; + } + + clearParameter(sp); +} + +void clearAllParameters() { + const auto& params = ServerParameterSet::getClusterParameterSet()->getMap(); + for (const auto& it : params) { + clearParameter(it.second); + } +} + +void initializeAllParametersFromDisk(OperationContext* opCtx) { + doLoadAllParametersFromDisk( + opCtx, "initializing"_sd, [](OperationContext* opCtx, BSONObj doc, StringData mode) { + updateParameter(doc, mode); + }); +} + +void resynchronizeAllParametersFromDisk(OperationContext* opCtx) { + const auto& allParams = ServerParameterSet::getClusterParameterSet()->getMap(); + std::set<std::string> unsetSettings; + for (const auto& it : allParams) { + unsetSettings.insert(it.second->name()); + } + + doLoadAllParametersFromDisk( + opCtx, + "resynchronizing"_sd, + [&unsetSettings](OperationContext* opCtx, BSONObj doc, StringData mode) { + unsetSettings.erase(doc[kIdField].str()); + updateParameter(doc, mode); + }); + + // For all known settings which were not present in this resync, + // explicitly clear any value which may be present in-memory. + for (const auto& setting : unsetSettings) { + clearParameter(setting); + } +} + +void maybeUpdateClusterParametersPostImportCollectionCommit(OperationContext* opCtx, + const NamespaceString& nss) { + if (nss == NamespaceString::kClusterParametersNamespace) { + // Something was imported, do a full collection scan to sync up. + cluster_parameters::initializeAllParametersFromDisk(opCtx); + } +} + +} // namespace mongo::cluster_parameters diff --git a/src/mongo/idl/cluster_parameter_synchronization_helpers.h b/src/mongo/idl/cluster_parameter_synchronization_helpers.h new file mode 100644 index 00000000000..d69271a7c03 --- /dev/null +++ b/src/mongo/idl/cluster_parameter_synchronization_helpers.h @@ -0,0 +1,105 @@ +/** + * Copyright (C) 2023-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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 "mongo/db/db_raii.h" + +namespace mongo { + +namespace cluster_parameters { + +void updateParameter(BSONObj doc, StringData mode); + +void clearParameter(ServerParameter* sp); + +void clearParameter(StringData id); + +void clearAllParameters(); + +/** + * Used to initialize in-memory cluster parameter state based on the on-disk contents after startup + * recovery or initial sync is complete. + */ +void initializeAllParametersFromDisk(OperationContext* opCtx); + +/** + * Used on rollback. Updates settings which are present and clears settings which are not. + */ +void resynchronizeAllParametersFromDisk(OperationContext* opCtx); + +template <typename OnEntry> +void doLoadAllParametersFromDisk(OperationContext* opCtx, StringData mode, OnEntry onEntry) try { + + // If the RecoveryUnit already had an open snapshot, keep the snapshot open. Otherwise + // abandon the snapshot when exiting the function. + ScopeGuard scopeGuard([&] { opCtx->recoveryUnit()->abandonSnapshot(); }); + if (opCtx->recoveryUnit()->isActive()) { + scopeGuard.dismiss(); + } + + AutoGetCollectionForRead coll(opCtx, NamespaceString::kClusterParametersNamespace); + if (!coll) { + return; + } + + std::vector<Status> failures; + + auto cursor = coll->getCursor(opCtx); + for (auto doc = cursor->next(); doc; doc = cursor->next()) { + try { + onEntry(opCtx, doc.get().data.toBson(), mode); + } catch (const DBException& ex) { + failures.push_back(ex.toStatus()); + } + } + + if (!failures.empty()) { + StringBuilder msg; + for (const auto& failure : failures) { + msg << failure.toString() << ", "; + } + msg.reset(msg.len() - 2); + uasserted(ErrorCodes::OperationFailed, msg.str()); + } +} catch (const DBException& ex) { + uassertStatusOK(ex.toStatus().withContext( + str::stream() << "Failed " << mode << " cluster server parameters from disk")); +} + +/** + * Used after an importCollection commits. Will update the in-memory cluster parameter state if the + * given namespace is a cluster parameters namespace. + */ +void maybeUpdateClusterParametersPostImportCollectionCommit(OperationContext* opCtx, + const NamespaceString& nss); + +} // namespace cluster_parameters + +} // namespace mongo diff --git a/src/mongo/idl/cluster_server_parameter_initializer.cpp b/src/mongo/idl/cluster_server_parameter_initializer.cpp index 3205f7a4fa7..e28e0542ecb 100644 --- a/src/mongo/idl/cluster_server_parameter_initializer.cpp +++ b/src/mongo/idl/cluster_server_parameter_initializer.cpp @@ -34,6 +34,7 @@ #include "mongo/base/string_data.h" #include "mongo/db/repl/replica_set_aware_service.h" #include "mongo/db/service_context.h" +#include "mongo/idl/cluster_parameter_synchronization_helpers.h" #include "mongo/logv2/log.h" namespace mongo { @@ -58,102 +59,10 @@ ClusterServerParameterInitializer* ClusterServerParameterInitializer::get( return &getInstance(serviceContext); } -void ClusterServerParameterInitializer::updateParameter(BSONObj doc, StringData mode) { - auto nameElem = doc[kIdField]; - if (nameElem.type() != String) { - LOGV2_DEBUG(6226301, - 1, - "Update with invalid cluster server parameter name", - "mode"_attr = mode, - "_id"_attr = nameElem); - return; - } - - auto name = nameElem.valueStringData(); - auto* sp = ServerParameterSet::getClusterParameterSet()->getIfExists(name); - if (!sp) { - LOGV2_DEBUG(6226300, - 3, - "Update to unknown cluster server parameter", - "mode"_attr = mode, - "name"_attr = name); - return; - } - - auto cptElem = doc[kCPTField]; - if ((cptElem.type() != mongo::Date) && (cptElem.type() != bsonTimestamp)) { - LOGV2_DEBUG(6226302, - 1, - "Update to cluster server parameter has invalid clusterParameterTime", - "mode"_attr = mode, - "name"_attr = name, - "clusterParameterTime"_attr = cptElem); - return; - } - - uassertStatusOK(sp->set(doc)); -} - -void ClusterServerParameterInitializer::clearParameter(ServerParameter* sp) { - if (sp->getClusterParameterTime() == LogicalTime::kUninitialized) { - // Nothing to clear. - return; - } - - uassertStatusOK(sp->reset()); -} - -void ClusterServerParameterInitializer::clearParameter(StringData id) { - auto* sp = ServerParameterSet::getClusterParameterSet()->getIfExists(id); - if (!sp) { - LOGV2_DEBUG(6226303, - 5, - "oplog event deletion of unknown cluster server parameter", - "name"_attr = id); - return; - } - - clearParameter(sp); -} - -void ClusterServerParameterInitializer::clearAllParameters() { - const auto& params = ServerParameterSet::getClusterParameterSet()->getMap(); - for (const auto& it : params) { - clearParameter(it.second); - } -} - -void ClusterServerParameterInitializer::initializeAllParametersFromDisk(OperationContext* opCtx) { - doLoadAllParametersFromDisk(opCtx, "initializing"_sd, [this](BSONObj doc, StringData mode) { - updateParameter(doc, mode); - }); -} - -void ClusterServerParameterInitializer::resynchronizeAllParametersFromDisk( - OperationContext* opCtx) { - const auto& allParams = ServerParameterSet::getClusterParameterSet()->getMap(); - std::set<std::string> unsetSettings; - for (const auto& it : allParams) { - unsetSettings.insert(it.second->name()); - } - - doLoadAllParametersFromDisk( - opCtx, "resynchronizing"_sd, [this, &unsetSettings](BSONObj doc, StringData mode) { - unsetSettings.erase(doc[kIdField].str()); - updateParameter(doc, mode); - }); - - // For all known settings which were not present in this resync, - // explicitly clear any value which may be present in-memory. - for (const auto& setting : unsetSettings) { - clearParameter(setting); - } -} - void ClusterServerParameterInitializer::onInitialDataAvailable(OperationContext* opCtx, bool isMajorityDataAvailable) { LOGV2_INFO(6608200, "Initializing cluster server parameters from disk"); - initializeAllParametersFromDisk(opCtx); + cluster_parameters::initializeAllParametersFromDisk(opCtx); } } // namespace mongo diff --git a/src/mongo/idl/cluster_server_parameter_initializer.h b/src/mongo/idl/cluster_server_parameter_initializer.h index 0eb02bb058c..49d501a7e6d 100644 --- a/src/mongo/idl/cluster_server_parameter_initializer.h +++ b/src/mongo/idl/cluster_server_parameter_initializer.h @@ -52,23 +52,6 @@ public: static ClusterServerParameterInitializer* get(OperationContext* opCtx); static ClusterServerParameterInitializer* get(ServiceContext* serviceContext); - void updateParameter(BSONObj doc, StringData mode); - void clearParameter(ServerParameter* sp); - void clearParameter(StringData id); - void clearAllParameters(); - - /** - * Used to initialize in-memory cluster parameter state based on the on-disk contents after - * startup recovery or initial sync is complete. - */ - void initializeAllParametersFromDisk(OperationContext* opCtx); - - /** - * Used on rollback and rename with drop. - * Updates settings which are present and clears settings which are not. - */ - void resynchronizeAllParametersFromDisk(OperationContext* opCtx); - // Virtual methods coming from the ReplicaSetAwareService void onStartup(OperationContext* opCtx) override final {} @@ -82,35 +65,6 @@ public: void onStepUpComplete(OperationContext* opCtx, long long term) override final {} void onStepDown() override final {} void onBecomeArbiter() override final {} - -private: - template <typename OnEntry> - void doLoadAllParametersFromDisk(OperationContext* opCtx, - StringData mode, - OnEntry onEntry) try { - std::vector<Status> failures; - - DBDirectClient client(opCtx); - FindCommandRequest findRequest{NamespaceString::kClusterParametersNamespace}; - client.find(std::move(findRequest), ReadPreferenceSetting{}, [&](BSONObj doc) { - try { - onEntry(doc, mode); - } catch (const DBException& ex) { - failures.push_back(ex.toStatus()); - } - }); - if (!failures.empty()) { - StringBuilder msg; - for (const auto& failure : failures) { - msg << failure.toString() << ", "; - } - msg.reset(msg.len() - 2); - uasserted(ErrorCodes::OperationFailed, msg.str()); - } - } catch (const DBException& ex) { - uassertStatusOK(ex.toStatus().withContext( - str::stream() << "Failed " << mode << " cluster server parameters from disk")); - } }; } // namespace mongo diff --git a/src/mongo/idl/cluster_server_parameter_initializer_test.cpp b/src/mongo/idl/cluster_server_parameter_initializer_test.cpp index e8a62b6a1c1..a2211ed0295 100644 --- a/src/mongo/idl/cluster_server_parameter_initializer_test.cpp +++ b/src/mongo/idl/cluster_server_parameter_initializer_test.cpp @@ -38,6 +38,7 @@ #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/storage_interface_mock.h" #include "mongo/db/service_context_d_test_fixture.h" +#include "mongo/idl/cluster_parameter_synchronization_helpers.h" #include "mongo/idl/cluster_server_parameter_gen.h" #include "mongo/idl/cluster_server_parameter_initializer.h" #include "mongo/idl/cluster_server_parameter_test_gen.h" @@ -68,7 +69,7 @@ public: // Delete all cluster server parameter documents written and refresh in-memory state. remove(); auto opCtx = cc().makeOperationContext(); - _initializer.resynchronizeAllParametersFromDisk(opCtx.get()); + cluster_parameters::resynchronizeAllParametersFromDisk(opCtx.get()); } /** * Simulates the call to the ClusterServerParameterInitializer at the end of initial sync, when diff --git a/src/mongo/idl/cluster_server_parameter_op_observer.cpp b/src/mongo/idl/cluster_server_parameter_op_observer.cpp index b8787dcfa12..ea9553d46fb 100644 --- a/src/mongo/idl/cluster_server_parameter_op_observer.cpp +++ b/src/mongo/idl/cluster_server_parameter_op_observer.cpp @@ -34,7 +34,7 @@ #include <memory> #include "mongo/db/dbdirectclient.h" -#include "mongo/idl/cluster_server_parameter_initializer.h" +#include "mongo/idl/cluster_parameter_synchronization_helpers.h" #include "mongo/logv2/log.h" namespace mongo { @@ -67,7 +67,9 @@ void ClusterServerParameterOpObserver::onInserts(OperationContext* opCtx, } for (auto it = first; it != last; ++it) { - ClusterServerParameterInitializer::get(opCtx)->updateParameter(it->doc, kOplog); + opCtx->recoveryUnit()->onCommit([doc = it->doc](boost::optional<Timestamp>) { + cluster_parameters::updateParameter(doc, kOplog); + }); } } @@ -78,7 +80,9 @@ void ClusterServerParameterOpObserver::onUpdate(OperationContext* opCtx, return; } - ClusterServerParameterInitializer::get(opCtx)->updateParameter(updatedDoc, kOplog); + opCtx->recoveryUnit()->onCommit([updatedDoc](boost::optional<Timestamp>) { + cluster_parameters::updateParameter(updatedDoc, kOplog); + }); } void ClusterServerParameterOpObserver::aboutToDelete(OperationContext* opCtx, @@ -115,7 +119,8 @@ void ClusterServerParameterOpObserver::onDelete(OperationContext* opCtx, const OplogDeleteEntryArgs& args) { const auto& docName = aboutToDeleteDoc(opCtx); if (!docName.empty()) { - ClusterServerParameterInitializer::get(opCtx)->clearParameter(docName); + opCtx->recoveryUnit()->onCommit( + [docName](boost::optional<Timestamp>) { cluster_parameters::clearParameter(docName); }); } } @@ -123,7 +128,8 @@ void ClusterServerParameterOpObserver::onDropDatabase(OperationContext* opCtx, const std::string& dbName) { if (dbName == NamespaceString::kConfigDb) { // Entire config DB deleted, reset to default state. - ClusterServerParameterInitializer::get(opCtx)->clearAllParameters(); + opCtx->recoveryUnit()->onCommit( + [](boost::optional<Timestamp>) { cluster_parameters::clearAllParameters(); }); } } @@ -135,58 +141,21 @@ repl::OpTime ClusterServerParameterOpObserver::onDropCollection( CollectionDropType dropType) { if (isConfigNamespace(collectionName)) { // Entire collection deleted, reset to default state. - ClusterServerParameterInitializer::get(opCtx)->clearAllParameters(); + opCtx->recoveryUnit()->onCommit( + [](boost::optional<Timestamp>) { cluster_parameters::clearAllParameters(); }); } return {}; } -void ClusterServerParameterOpObserver::postRenameCollection( - OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - const UUID& uuid, - const boost::optional<UUID>& dropTargetUUID, - bool stayTemp) { - if (isConfigNamespace(fromCollection)) { - // Same as collection dropped from a config point of view. - ClusterServerParameterInitializer::get(opCtx)->clearAllParameters(); - } - - if (isConfigNamespace(toCollection)) { - // Potentially many documents now set, perform full scan. - if (dropTargetUUID) { - // Possibly lost configurations in overwrite. - ClusterServerParameterInitializer::get(opCtx)->resynchronizeAllParametersFromDisk( - opCtx); - } else { - // Collection did not exist prior to rename. - ClusterServerParameterInitializer::get(opCtx)->initializeAllParametersFromDisk(opCtx); - } - } -} - -void ClusterServerParameterOpObserver::onImportCollection(OperationContext* opCtx, - const UUID& importUUID, - const NamespaceString& nss, - long long numRecords, - long long dataSize, - const BSONObj& catalogEntry, - const BSONObj& storageMetadata, - bool isDryRun) { - if (!isDryRun && (numRecords > 0) && isConfigNamespace(nss)) { - // Something was imported, do a full collection scan to sync up. - // No need to apply rollback rules since nothing will have been deleted. - ClusterServerParameterInitializer::get(opCtx)->initializeAllParametersFromDisk(opCtx); - } -} - void ClusterServerParameterOpObserver::_onReplicationRollback(OperationContext* opCtx, const RollbackObserverInfo& rbInfo) { if (rbInfo.rollbackNamespaces.count(NamespaceString::kClusterParametersNamespace)) { // Some kind of rollback happend in the settings collection. // Just reload from disk to be safe. - ClusterServerParameterInitializer::get(opCtx)->resynchronizeAllParametersFromDisk(opCtx); + // We can call resynchronize directly because onReplicationRollback is guaranteed to be + // called from a state with no active WUOW and no database locks. + cluster_parameters::resynchronizeAllParametersFromDisk(opCtx); } } diff --git a/src/mongo/idl/cluster_server_parameter_op_observer.h b/src/mongo/idl/cluster_server_parameter_op_observer.h index 2ef05729e39..86bec6b61da 100644 --- a/src/mongo/idl/cluster_server_parameter_op_observer.h +++ b/src/mongo/idl/cluster_server_parameter_op_observer.h @@ -72,12 +72,18 @@ public: const UUID& uuid, std::uint64_t numRecords, CollectionDropType dropType) final; + +private: + void _onReplicationRollback(OperationContext* opCtx, const RollbackObserverInfo& rbInfo) final; + +public: + // Remainder of operations are ignorable. void postRenameCollection(OperationContext* opCtx, const NamespaceString& fromCollection, const NamespaceString& toCollection, const UUID& uuid, const boost::optional<UUID>& dropTargetUUID, - bool stayTemp) final; + bool stayTemp) final {} void onImportCollection(OperationContext* opCtx, const UUID& importUUID, const NamespaceString& nss, @@ -85,12 +91,7 @@ public: long long dataSize, const BSONObj& catalogEntry, const BSONObj& storageMetadata, - bool isDryRun) final; - - void _onReplicationRollback(OperationContext* opCtx, const RollbackObserverInfo& rbInfo) final; - -public: - // Remainder of operations are ignorable. + bool isDryRun) final {} void onCreateIndex(OperationContext* opCtx, const NamespaceString& nss, @@ -212,6 +213,10 @@ public: size_t numberOfPrePostImagesToWrite, Date_t wallClockTime) final {} + void onTransactionPrepareNonPrimary(OperationContext* opCtx, + const std::vector<repl::OplogEntry>& statements, + const repl::OpTime& prepareOpTime) final {} + void onTransactionAbort(OperationContext* opCtx, boost::optional<OplogSlot> abortOplogEntryOpTime) final {} diff --git a/src/mongo/idl/cluster_server_parameter_op_observer_test.cpp b/src/mongo/idl/cluster_server_parameter_op_observer_test.cpp index 2ba82076bf2..7553bfc191a 100644 --- a/src/mongo/idl/cluster_server_parameter_op_observer_test.cpp +++ b/src/mongo/idl/cluster_server_parameter_op_observer_test.cpp @@ -31,9 +31,9 @@ #include "mongo/platform/basic.h" -#include "mongo/idl/cluster_server_parameter_test_util.h" - +#include "mongo/db/catalog_raii.h" #include "mongo/idl/cluster_server_parameter_op_observer.h" +#include "mongo/idl/cluster_server_parameter_test_util.h" #include "mongo/logv2/log.h" namespace mongo { @@ -47,60 +47,58 @@ const std::vector<NamespaceString> kIgnoredNamespaces = { class ClusterServerParameterOpObserverTest : public ClusterServerParameterTestBase { public: - void doInserts(const NamespaceString& nss, std::initializer_list<BSONObj> docs) { + void doInserts(const NamespaceString& nss, + std::initializer_list<BSONObj> docs, + bool commit = true) { std::vector<InsertStatement> stmts; std::transform(docs.begin(), docs.end(), std::back_inserter(stmts), [](auto doc) { return InsertStatement(doc); }); auto opCtx = cc().makeOperationContext(); + WriteUnitOfWork wuow(opCtx.get()); + + AutoGetCollection autoColl(opCtx.get(), nss, MODE_IX); observer.onInserts( opCtx.get(), nss, UUID::gen(), stmts.cbegin(), stmts.cend(), false /* fromMigrate */); + if (commit) + wuow.commit(); } - void doUpdate(const NamespaceString& nss, BSONObj updatedDoc) { + void doUpdate(const NamespaceString& nss, BSONObj updatedDoc, bool commit = true) { // Actual UUID doesn't matter, just use any... CollectionUpdateArgs updateArgs; updateArgs.update = BSON("$set" << updatedDoc); updateArgs.updatedDoc = updatedDoc; OplogUpdateEntryArgs entryArgs(&updateArgs, nss, UUID::gen()); auto opCtx = cc().makeOperationContext(); + WriteUnitOfWork wuow(opCtx.get()); + AutoGetCollection autoColl(opCtx.get(), nss, MODE_IX); observer.onUpdate(opCtx.get(), entryArgs); + if (commit) + wuow.commit(); } - void doDelete(const NamespaceString& nss, BSONObj deletedDoc, bool includeDeletedDoc = true) { + void doDelete(const NamespaceString& nss, + BSONObj deletedDoc, + bool includeDeletedDoc = true, + bool commit = true) { auto opCtx = cc().makeOperationContext(); - auto uuid = UUID::gen(); - observer.aboutToDelete(opCtx.get(), nss, uuid, deletedDoc); + WriteUnitOfWork wuow(opCtx.get()); + AutoGetCollection autoColl(opCtx.get(), nss, MODE_IX); + observer.aboutToDelete(opCtx.get(), nss, UUID::gen(), deletedDoc); OplogDeleteEntryArgs args; args.deletedDoc = includeDeletedDoc ? &deletedDoc : nullptr; - observer.onDelete(opCtx.get(), nss, uuid, 1 /* StmtId */, args); + observer.onDelete(opCtx.get(), nss, UUID::gen(), 1 /* StmtId */, args); + if (commit) + wuow.commit(); } - void doDropDatabase(StringData dbname) { + void doDropDatabase(const std::string& dbname, bool commit = true) { auto opCtx = cc().makeOperationContext(); - observer.onDropDatabase(opCtx.get(), dbname.toString()); - } - - void doRenameCollection(const NamespaceString& fromColl, const NamespaceString& toColl) { - auto opCtx = cc().makeOperationContext(); - observer.postRenameCollection(opCtx.get(), - fromColl, - toColl, - UUID::gen(), - boost::none /* targetUUID */, - false /* stayTemp */); - } - - void doImportCollection(const NamespaceString& nss) { - auto opCtx = cc().makeOperationContext(); - observer.onImportCollection(opCtx.get(), - UUID::gen(), - nss, - 10 /* num records */, - 1 << 20 /* data size */, - BSONObj() /* catalogEntry */, - BSONObj() /* storageMetadata */, - false /* isDryRun */); + WriteUnitOfWork wuow(opCtx.get()); + observer.onDropDatabase(opCtx.get(), dbname); + if (commit) + wuow.commit(); } void doReplicationRollback(const std::vector<NamespaceString>& namespaces) { @@ -165,6 +163,28 @@ public: assertIgnored(NamespaceString::kClusterParametersNamespace, fn); } + void assertParameterState(int line, + int intVal, + StringData strVal, + boost::optional<LogicalTime> cpt = boost::none) { + auto* sp = ServerParameterSet::getClusterParameterSet() + ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, + ClusterServerParameterTest>>(kCSPTest); + ; + try { + if (cpt) { + ASSERT_EQ(sp->getClusterParameterTime(), *cpt); + } + + ClusterServerParameterTest cspTest = sp->getValue(); + ASSERT_EQ(cspTest.getIntValue(), intVal); + ASSERT_EQ(cspTest.getStrValue(), strVal); + } catch (...) { + LOGV2_ERROR(6887700, "ASSERT_PARAMETER_STATE failed", "line"_attr = line); + throw; + } + } + protected: ClusterServerParameterOpObserver observer; }; @@ -308,12 +328,12 @@ TEST_F(ClusterServerParameterOpObserverTest, onDropDatabase) { assertIgnoredOtherNamespaces([this](const auto& nss) { const auto dbname = nss.db(); if (dbname != kConfigDB) { - doDropDatabase(dbname); + doDropDatabase(dbname.toString()); } }); // Actually drop the config DB. - doDropDatabase(kConfigDB); + doDropDatabase(kConfigDB.toString()); auto* sp = ServerParameterSet::getClusterParameterSet() ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, @@ -325,63 +345,12 @@ TEST_F(ClusterServerParameterOpObserverTest, onDropDatabase) { ASSERT_EQ(cspTest.getStrValue(), kDefaultStrValue); } -TEST_F(ClusterServerParameterOpObserverTest, onRenameCollection) { - initializeState(); - - const NamespaceString kTestFoo("test", "foo"); - // Rename ignorable collections. - assertIgnoredOtherNamespaces([&](const auto& nss) { doRenameCollection(nss, kTestFoo); }); - assertIgnoredOtherNamespaces([&](const auto& nss) { doRenameCollection(kTestFoo, nss); }); - - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); - ASSERT(sp != nullptr); - - // These renames "work" despite not mutating durable state - // since the rename away doesn't require a rescan. - - // Rename away (and reset to default) - doRenameCollection(NamespaceString::kClusterParametersNamespace, kTestFoo); - ClusterServerParameterTest cspTest = sp->getValue(); - ASSERT_EQ(cspTest.getIntValue(), kDefaultIntValue); - ASSERT_EQ(cspTest.getStrValue(), kDefaultStrValue); - - // Rename in (and restore to initialized state) - doRenameCollection(kTestFoo, NamespaceString::kClusterParametersNamespace); - cspTest = sp->getValue(); - ASSERT_EQ(cspTest.getIntValue(), kInitialIntValue); - ASSERT_EQ(cspTest.getStrValue(), kInitialStrValue); -} - -TEST_F(ClusterServerParameterOpObserverTest, onImportCollection) { - initializeState(); - - const NamespaceString kTestFoo("test", "foo"); - // Import ignorable collections. - assertIgnoredOtherNamespaces([&](const auto& nss) { doImportCollection(nss); }); - - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); - ASSERT(sp != nullptr); - - // Import the collection (rescan). - auto doc = - makeClusterParametersDoc(LogicalTime(Timestamp(time(nullptr))), 333, "onImportCollection"); - upsert(doc); - doImportCollection(NamespaceString::kClusterParametersNamespace); - ClusterServerParameterTest cspTest = sp->getValue(); - ASSERT_EQ(cspTest.getIntValue(), 333); - ASSERT_EQ(cspTest.getStrValue(), "onImportCollection"); -} - TEST_F(ClusterServerParameterOpObserverTest, onReplicationRollback) { initializeState(); const NamespaceString kTestFoo("test", "foo"); // Import ignorable collections. - assertIgnoredOtherNamespaces([&](const auto& nss) { doImportCollection(nss); }); + assertIgnoredOtherNamespaces([&](const auto& nss) { doReplicationRollback({nss}); }); auto* sp = ServerParameterSet::getClusterParameterSet() ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, @@ -412,5 +381,37 @@ TEST_F(ClusterServerParameterOpObserverTest, onReplicationRollback) { ASSERT_EQ(cspTest.getStrValue(), kDefaultStrValue); } +#define ASSERT_PARAMETER_STATE(...) assertParameterState(__LINE__, __VA_ARGS__) + +TEST_F(ClusterServerParameterOpObserverTest, abortsAfterObservation) { + + const auto initialDoc = initializeState(); + + doInserts(NamespaceString::kClusterParametersNamespace, + {makeClusterParametersDoc(LogicalTime(Timestamp(12345678)), 123, "abc")}, + false /* commit */); + + ASSERT_PARAMETER_STATE(kInitialIntValue, kInitialStrValue); + + doUpdate(NamespaceString::kClusterParametersNamespace, + {makeClusterParametersDoc(LogicalTime(Timestamp(87654321)), 321, "cba")}, + false /* commit */); + + ASSERT_PARAMETER_STATE(kInitialIntValue, kInitialStrValue); + + doDelete(NamespaceString::kClusterParametersNamespace, + initialDoc, + true /* includeDeletedDoc */, + false /* commit */); + + ASSERT_PARAMETER_STATE(kInitialIntValue, kInitialStrValue); + + doDropDatabase(kConfigDB.toString(), false /* commit */); + + ASSERT_PARAMETER_STATE(kInitialIntValue, kInitialStrValue); +} + +#undef ASSERT_PARAMETER_STATE + } // namespace } // namespace mongo |
