/** * Copyright (C) 2022-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 * . * * 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/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/db/repl/replica_set_aware_service.h" #include "mongo/db/service_context.h" namespace mongo { namespace user_writes_recoverable_critical_section_util { bool inRecoveryMode(OperationContext* opCtx); } /** * Represents the 'user writes blocking' critical section. The critical section status is persisted * on disk and it's in-memory representation is kept in sync with the persisted state through an * OpObserver that reacts to the inserts/updates/deletes on the persisted document. * * On replicaSets, enable blocking is depicted by transition (1); and disable blocking is depicted * by transition (2): * * (1) acquireRecoverableCriticalSectionBlockingUserWrites() * --------------------------------------------------------- * | | * | v * + --------------------+ + --------------------+ * | User writes allowed | | User writes blocked | * + --------------------+ + --------------------+ * ^ ^ * | | * --------------------------------------------------------- * (2) releaseRecoverableCriticalSection() * * On sharded clusters, blocking/unblocking happens as a two-phase protocol. Enable blocking is * depicted by transitions (1) and (2); and disable blocking is depicted by (3) and (4): * * (2) promoteRecoverableCriticalSectionToBlockUserWrites() * ------------------------ * | | * (1) acquireRecoverableCriticalSectionBlockNewShardedDDL() | | * --------------------------------- | | * | | | | * | V | v * + -------------------------+ + -------------------------+ + -------------------------+ * | User writes allowed, | | User writes allowed, | | User writes blocked | * | User sharded DDL allowed | | User sharded DDL blocked | | User sharded DDL blocked | * + -------------------------+ + -------------------------+ + -------------------------+ * ^ ^ | * | | | | * -------------------------------- | | * (4) releaseRecoverableCriticalSection() | | * | | * ----------------------------- * (3) demoteRecoverableCriticalSectionToNoLongerBlockUserWrites() * */ class UserWritesRecoverableCriticalSectionService : public ReplicaSetAwareService { public: static const NamespaceString kGlobalUserWritesNamespace; UserWritesRecoverableCriticalSectionService() = default; static UserWritesRecoverableCriticalSectionService* get(ServiceContext* serviceContext); static UserWritesRecoverableCriticalSectionService* get(OperationContext* opCtx); bool shouldRegisterReplicaSetAwareService() const override; /** * Acquires the user writes critical section blocking user writes. This should be used only on * replica sets. */ void acquireRecoverableCriticalSectionBlockingUserWrites(OperationContext* opCtx, const NamespaceString& nss); /** * Acquires the user writes critical section blocking only new sharded DDL operations, but not * user writes nor local DDL. This is a 'prepare' state before user writes and local DDL can be * blocked on sharded clusters. * Note: This method does not wait for ongoing DDL coordinators to finish. If the caller wishes * to ensure that any ongoing DDL operation has seen the new blocking state, the caller should * wait for the ShardingDDLCoordinators to drain itself. */ void acquireRecoverableCriticalSectionBlockNewShardedDDL(OperationContext* opCtx, const NamespaceString& nss); /** * Promotes a user writes critical section that is in the 'prepare' state (i.e. only blocking * sharded DDL) to start blocking also user writes. This should be run only after all shards in * the cluster have entered the 'prepare' state. */ void promoteRecoverableCriticalSectionToBlockUserWrites(OperationContext* opCtx, const NamespaceString& nss); /** * Demotes a user writes critical section that is blocking both sharded DDL and user writes to * only block sharded DDL. This is a preparation step before allowing user writes again on * sharded clusters. */ void demoteRecoverableCriticalSectionToNoLongerBlockUserWrites(OperationContext* opCtx, const NamespaceString& nss); /** * Releases the user writes critical section, allowing user writes again. On sharded clusters, * before this method is called all shards must have first demoted their critical sections to no * longer block user writes. */ void releaseRecoverableCriticalSection(OperationContext* opCtx, const NamespaceString& nss); /** * This method is called when we have to mirror the state on disk of the recoverable critical * section to memory (on startUp or on rollback). */ void recoverRecoverableCriticalSections(OperationContext* opCtx); private: void onInitialDataAvailable(OperationContext* opCtx, bool isMajorityDataAvailable) override final { recoverRecoverableCriticalSections(opCtx); } void onStartup(OperationContext* opCtx) override final {} void onShutdown() override final {} void onStepUpBegin(OperationContext* opCtx, long long term) override final {} void onStepUpComplete(OperationContext* opCtx, long long term) override final {} void onStepDown() override final {} void onBecomeArbiter() override final {} }; } // namespace mongo