summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Jesse Jiryu Davis <jesse@mongodb.com>2020-07-09 18:19:07 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-07-24 19:12:21 +0000
commitc57ab4974d550323c7f86d1de6cbc0499458138b (patch)
tree9d6c84732637dce5b6da3c8039b0f23c814abdd7
parentbc405c72dce4714da604810cdc90c132bd5fbaa1 (diff)
downloadmongo-c57ab4974d550323c7f86d1de6cbc0499458138b.tar.gz
SERVER-48967 Ban non-primary writes with empty namespace
(cherry picked from commit 1ce0d13e46f720b35ae01e37d16bcd7b3789a045) # Conflicts: # src/mongo/db/op_observer_impl_test.cpp # src/mongo/db/repl/replication_coordinator_external_state_impl.cpp # src/mongo/db/repl/replication_coordinator_impl.cpp
-rw-r--r--src/mongo/db/op_observer_impl_test.cpp15
-rw-r--r--src/mongo/db/repl/SConscript1
-rw-r--r--src/mongo/db/repl/always_allow_non_local_writes.cpp55
-rw-r--r--src/mongo/db/repl/always_allow_non_local_writes.h59
-rw-r--r--src/mongo/db/repl/oplog.cpp2
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_impl.cpp3
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp33
7 files changed, 137 insertions, 31 deletions
diff --git a/src/mongo/db/op_observer_impl_test.cpp b/src/mongo/db/op_observer_impl_test.cpp
index 2c97d4c5f3a..fb6c3508ea6 100644
--- a/src/mongo/db/op_observer_impl_test.cpp
+++ b/src/mongo/db/op_observer_impl_test.cpp
@@ -34,6 +34,7 @@
#include "mongo/db/db_raii.h"
#include "mongo/db/field_parser.h"
#include "mongo/db/repl/oplog.h"
+#include "mongo/db/repl/oplog_entry.h"
#include "mongo/db/repl/oplog_interface_local.h"
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator_mock.h"
@@ -240,5 +241,19 @@ TEST_F(OpObserverTest, OnRenameCollectionReturnsRenameOpTime) {
ASSERT_EQUALS(repl::ReplClientInfo::forClient(&cc()).getLastOp(), renameOpTime);
}
+TEST_F(OpObserverTest, MustBePrimaryToWriteOplogEntries) {
+ OpObserverImpl opObserver;
+ auto opCtx = cc().makeOperationContext();
+
+ ASSERT_OK(repl::ReplicationCoordinator::get(opCtx.get())
+ ->setFollowerMode(repl::MemberState::RS_SECONDARY));
+
+ Lock::GlobalWrite globalWrite(opCtx.get());
+ WriteUnitOfWork wunit(opCtx.get());
+
+ // No-op writes should be prohibited.
+ ASSERT_THROWS(opObserver.onOpMessage(opCtx.get(), {}), AssertionException);
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index 40f95182f39..43f4a65f5db 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -975,6 +975,7 @@ env.CppUnitTest('replication_coordinator_impl_reconfig_test',
env.Library(
target='repl_coordinator_interface',
source=[
+ 'always_allow_non_local_writes.cpp',
'repl_client_info.cpp',
'replication_coordinator.cpp',
],
diff --git a/src/mongo/db/repl/always_allow_non_local_writes.cpp b/src/mongo/db/repl/always_allow_non_local_writes.cpp
new file mode 100644
index 00000000000..0417debe798
--- /dev/null
+++ b/src/mongo/db/repl/always_allow_non_local_writes.cpp
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) 2020-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.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/repl/always_allow_non_local_writes.h"
+
+namespace mongo {
+namespace repl {
+
+// Overrides _canAcceptLocalWrites for the decorated OperationContext.
+const OperationContext::Decoration<bool> alwaysAllowNonLocalWritesOnOpCtx =
+ OperationContext::declareDecoration<bool>();
+
+AllowNonLocalWritesBlock::AllowNonLocalWritesBlock(OperationContext* opCtx)
+ : _opCtx(opCtx), _initialState(alwaysAllowNonLocalWritesOnOpCtx(_opCtx)) {
+ alwaysAllowNonLocalWritesOnOpCtx(_opCtx) = true;
+}
+
+AllowNonLocalWritesBlock::~AllowNonLocalWritesBlock() {
+ alwaysAllowNonLocalWritesOnOpCtx(_opCtx) = _initialState;
+}
+
+bool alwaysAllowNonLocalWrites(const OperationContext* opCtx) {
+ return alwaysAllowNonLocalWritesOnOpCtx(opCtx);
+}
+
+} // namespace repl
+} // namespace mongo
diff --git a/src/mongo/db/repl/always_allow_non_local_writes.h b/src/mongo/db/repl/always_allow_non_local_writes.h
new file mode 100644
index 00000000000..e5759ba418f
--- /dev/null
+++ b/src/mongo/db/repl/always_allow_non_local_writes.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2020-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/operation_context.h"
+
+namespace mongo {
+namespace repl {
+
+/**
+ * Allows non-local writes despite _canAcceptNonLocalWrites being false on a single OperationContext
+ * while in scope.
+ *
+ * Resets to original value when leaving scope so it is safe to nest.
+ */
+class AllowNonLocalWritesBlock {
+ AllowNonLocalWritesBlock(const AllowNonLocalWritesBlock&) = delete;
+ AllowNonLocalWritesBlock& operator=(const AllowNonLocalWritesBlock&) = delete;
+
+public:
+ AllowNonLocalWritesBlock(OperationContext* opCtx);
+ ~AllowNonLocalWritesBlock();
+
+private:
+ OperationContext* const _opCtx;
+ const bool _initialState;
+};
+
+bool alwaysAllowNonLocalWrites(const OperationContext* opCtx);
+
+} // namespace repl
+} // namespace mongo
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 5d49daa71ee..e811fe5d991 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -419,7 +419,7 @@ void _logOpsInner(OperationContext* opCtx,
Collection* oplogCollection,
OpTime finalOpTime) {
auto replCoord = ReplicationCoordinator::get(opCtx);
- if (nss.size() && replCoord->getReplicationMode() == ReplicationCoordinator::modeReplSet &&
+ if (replCoord->getReplicationMode() == ReplicationCoordinator::modeReplSet &&
!replCoord->canAcceptWritesFor(opCtx, nss)) {
uasserted(17405,
str::stream() << "logOp() but can't accept write to collection " << nss.ns());
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
index 6921d0e9839..47103721a65 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
@@ -57,6 +57,7 @@
#include "mongo/db/logical_time_validator.h"
#include "mongo/db/op_observer.h"
#include "mongo/db/repair_database.h"
+#include "mongo/db/repl/always_allow_non_local_writes.h"
#include "mongo/db/repl/bgsync.h"
#include "mongo/db/repl/drop_pending_collection_reaper.h"
#include "mongo/db/repl/isself.h"
@@ -396,6 +397,8 @@ Status ReplicationCoordinatorExternalStateImpl::initializeReplSetStorage(Operati
"initiate oplog entry",
kRsOplogNamespace.toString(),
[this, &opCtx, &config, &kRsOplogNamespace] {
+ // Permit writing to the oplog before we step up to primary.
+ AllowNonLocalWritesBlock allowNonLocalWrites(opCtx);
Lock::GlobalWrite globalWrite(opCtx);
WriteUnitOfWork wuow(opCtx);
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp
index c6fb608d76b..a6a4d0084bb 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -48,6 +48,7 @@
#include "mongo/db/logical_time.h"
#include "mongo/db/logical_time_validator.h"
#include "mongo/db/operation_context_noop.h"
+#include "mongo/db/repl/always_allow_non_local_writes.h"
#include "mongo/db/repl/check_quorum_for_config_change.h"
#include "mongo/db/repl/data_replicator_external_state_initial_sync.h"
#include "mongo/db/repl/elect_cmd_runner.h"
@@ -105,9 +106,6 @@ using NextAction = Fetcher::NextAction;
namespace {
const char kLocalDB[] = "local";
-// Overrides _canAcceptLocalWrites for the decorated OperationContext.
-const OperationContext::Decoration<bool> alwaysAllowNonLocalWrites =
- OperationContext::declareDecoration<bool>();
MONGO_EXPORT_SERVER_PARAMETER(numInitialSyncAttempts, int, 10);
@@ -130,31 +128,6 @@ MONGO_INITIALIZER(periodicNoopIntervalSecs)(InitializerContext*) {
return Status::OK();
}
-
-/**
- * Allows non-local writes despite _canAcceptNonlocalWrites being false on a single OperationContext
- * while in scope.
- *
- * Resets to original value when leaving scope so it is safe to nest.
- */
-class AllowNonLocalWritesBlock {
- MONGO_DISALLOW_COPYING(AllowNonLocalWritesBlock);
-
-public:
- AllowNonLocalWritesBlock(OperationContext* opCtx)
- : _opCtx(opCtx), _initialState(alwaysAllowNonLocalWrites(_opCtx)) {
- alwaysAllowNonLocalWrites(_opCtx) = true;
- }
-
- ~AllowNonLocalWritesBlock() {
- alwaysAllowNonLocalWrites(_opCtx) = _initialState;
- }
-
-private:
- OperationContext* const _opCtx;
- const bool _initialState;
-};
-
void lockAndCall(stdx::unique_lock<stdx::mutex>* lk, const stdx::function<void()>& fn) {
if (!lk->owns_lock()) {
lk->lock();
@@ -2009,7 +1982,7 @@ bool ReplicationCoordinatorImpl::canAcceptWritesForDatabase_UNSAFE(OperationCont
// accept writes. Similarly, writes are always permitted to the "local" database. Finally,
// in the event that a node is started with --slave and --master, we allow writes unless the
// master/slave system has set the replAllDead flag.
- if (_canAcceptNonLocalWrites.loadRelaxed() || alwaysAllowNonLocalWrites(*opCtx)) {
+ if (_canAcceptNonLocalWrites.loadRelaxed() || alwaysAllowNonLocalWrites(opCtx)) {
return true;
}
if (dbName == kLocalDB) {
@@ -2038,7 +2011,7 @@ bool ReplicationCoordinatorImpl::canAcceptWritesFor_UNSAFE(OperationContext* opC
// If we can accept non local writes (ie we're PRIMARY) then we must not be in ROLLBACK.
// This check is redundant of the check of _memberState below, but since this can be checked
// without locking, we do it as an optimization.
- if (_canAcceptNonLocalWrites.loadRelaxed() || alwaysAllowNonLocalWrites(*opCtx)) {
+ if (_canAcceptNonLocalWrites.loadRelaxed() || alwaysAllowNonLocalWrites(opCtx)) {
return true;
}