diff options
Diffstat (limited to 'src/mongo/db')
65 files changed, 408 insertions, 510 deletions
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index 23967c70d60..3533eb35173 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -67,6 +67,8 @@ #include "mongo/util/invariant.h" #include "mongo/util/str.h" +using namespace fmt::literals; + namespace mongo { using logv2::LogComponent; @@ -456,26 +458,21 @@ BSONObj CommandHelpers::appendGenericReplyFields(const BSONObj& replyObjWithGene BSONObj CommandHelpers::appendMajorityWriteConcern(const BSONObj& cmdObj, WriteConcernOptions defaultWC) { WriteConcernOptions newWC = kMajorityWriteConcern; - if (cmdObj.hasField(kWriteConcernField)) { - auto wc = cmdObj.getField(kWriteConcernField); - // The command has a writeConcern field and it's majority, so we can - // return it as-is. - if (wc["w"].ok() && wc["w"].str() == "majority") { + auto wc = uassertStatusOK(WriteConcernOptions::extractWCFromCommand(cmdObj)); + + // The command has a writeConcern field and it's majority, so we can return it as-is. + if (wc.isMajority()) { return cmdObj; } - if (wc["wtimeout"].ok()) { - // They set a timeout, but aren't using majority WC. We want to use their - // timeout along with majority WC. - newWC = WriteConcernOptions(WriteConcernOptions::kMajority, - WriteConcernOptions::SyncMode::UNSET, - wc["wtimeout"].Number()); - } + newWC = WriteConcernOptions{ + WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, wc.wTimeout}; } else if (!defaultWC.usedDefaultConstructedWC) { auto minimumAcceptableWTimeout = newWC.wTimeout; newWC = defaultWC; - newWC.wMode = "majority"; + newWC.w = "majority"; + if (defaultWC.wTimeout < minimumAcceptableWTimeout) { newWC.wTimeout = minimumAcceptableWTimeout; } @@ -550,6 +547,13 @@ bool CommandHelpers::uassertShouldAttemptParse(OperationContext* opCtx, } } +void CommandHelpers::uassertCommandRunWithMajority(StringData commandName, + const WriteConcernOptions& writeConcern) { + uassert(ErrorCodes::InvalidOptions, + "\"{}\" must be called with majority writeConcern, got: {} "_format( + commandName, writeConcern.toBSON().toString()), + writeConcern.isMajority()); +} void CommandHelpers::canUseTransactions(const NamespaceString& nss, StringData cmdName, diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 778c62cb2b6..cd7ff64b371 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -307,6 +307,12 @@ struct CommandHelpers { const OpMsgRequest& request); /** + * Asserts that a majority write concern was used for a command. + */ + static void uassertCommandRunWithMajority(StringData commandName, + const WriteConcernOptions& wc); + + /** * Verifies that command is allowed to run under a transaction in the given database or * namespace, and throws if that verification doesn't pass. */ diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp index 8bef7d417fe..58c37b80edf 100644 --- a/src/mongo/db/commands/getmore_cmd.cpp +++ b/src/mongo/db/commands/getmore_cmd.cpp @@ -82,7 +82,7 @@ MONGO_FAIL_POINT_DEFINE(rsStopGetMoreCmd); MONGO_FAIL_POINT_DEFINE(getMoreHangAfterPinCursor); // The timeout when waiting for linearizable read concern on a getMore command. -static constexpr int kLinearizableReadConcernTimeout = 15000; +static constexpr Milliseconds kLinearizableReadConcernTimeout{15000}; // getMore can run with any readConcern, because cursor-creating commands like find can run with any // readConcern. However, since getMore automatically uses the readConcern of the command that diff --git a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp index 540d8657f6f..207ddd86709 100644 --- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp +++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp @@ -242,17 +242,15 @@ public: // TODO SERVER-25778: replace this with the general mechanism for specifying a default // writeConcern. ON_BLOCK_EXIT([&] { - // Propagate the user's wTimeout if one was given. - auto timeout = opCtx->getWriteConcern().isImplicitDefaultWriteConcern() - ? INT_MAX - : opCtx->getWriteConcern().wTimeout; WriteConcernResult res; auto waitForWCStatus = waitForWriteConcern( opCtx, repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp(), - WriteConcernOptions(repl::ReplSetConfig::kMajorityWriteConcernModeName, - WriteConcernOptions::SyncMode::UNSET, - timeout), + WriteConcernOptions( + repl::ReplSetConfig::kMajorityWriteConcernModeName, + WriteConcernOptions::SyncMode::UNSET, + // Propagate the user's wTimeout if one was given. Default is kNoTimeout. + opCtx->getWriteConcern().wTimeout), &res); CommandHelpers::appendCommandWCStatus(result, waitForWCStatus, res); }); diff --git a/src/mongo/db/commands/write_commands.cpp b/src/mongo/db/commands/write_commands.cpp index 6eb1009bc86..e90d4ac25ce 100644 --- a/src/mongo/db/commands/write_commands.cpp +++ b/src/mongo/db/commands/write_commands.cpp @@ -105,7 +105,7 @@ void redactTooLongLog(mutablebson::Document* cmdObj, StringData fieldName) { bool shouldSkipOutput(OperationContext* opCtx) { const WriteConcernOptions& writeConcern = opCtx->getWriteConcern(); - return writeConcern.wMode.empty() && writeConcern.wNumNodes == 0 && + return writeConcern.isUnacknowledged() && (writeConcern.syncMode == WriteConcernOptions::SyncMode::NONE || writeConcern.syncMode == WriteConcernOptions::SyncMode::UNSET); } diff --git a/src/mongo/db/read_concern.cpp b/src/mongo/db/read_concern.cpp index fe9fd208c04..85f33dd149e 100644 --- a/src/mongo/db/read_concern.cpp +++ b/src/mongo/db/read_concern.cpp @@ -48,7 +48,7 @@ Status waitForReadConcern(OperationContext* opCtx, return w(opCtx, readConcernArgs, dbName, allowAfterClusterTime); } -Status waitForLinearizableReadConcern(OperationContext* opCtx, int readConcernTimeout) { +Status waitForLinearizableReadConcern(OperationContext* opCtx, Milliseconds readConcernTimeout) { static auto w = MONGO_WEAK_FUNCTION_DEFINITION(waitForLinearizableReadConcern); return w(opCtx, readConcernTimeout); } diff --git a/src/mongo/db/read_concern.h b/src/mongo/db/read_concern.h index e12915870cf..5767631864e 100644 --- a/src/mongo/db/read_concern.h +++ b/src/mongo/db/read_concern.h @@ -29,6 +29,8 @@ #pragma once +#include "mongo/util/duration.h" + namespace mongo { class BSONObj; @@ -75,7 +77,7 @@ Status waitForReadConcern(OperationContext* opCtx, * A readConcernTimeout of 0 indicates that the operation will block indefinitely waiting for read * concern. */ -Status waitForLinearizableReadConcern(OperationContext* opCtx, int readConcernTimeout); +Status waitForLinearizableReadConcern(OperationContext* opCtx, Milliseconds readConcernTimeout); /** * Waits to satisfy a "speculative" majority read. diff --git a/src/mongo/db/read_concern_mongod.cpp b/src/mongo/db/read_concern_mongod.cpp index 055b2378e4c..ea339471f8e 100644 --- a/src/mongo/db/read_concern_mongod.cpp +++ b/src/mongo/db/read_concern_mongod.cpp @@ -452,7 +452,8 @@ Status waitForReadConcernImpl(OperationContext* opCtx, return Status::OK(); } -Status waitForLinearizableReadConcernImpl(OperationContext* opCtx, const int readConcernTimeout) { +Status waitForLinearizableReadConcernImpl(OperationContext* opCtx, + const Milliseconds readConcernTimeout) { CurOpFailpointHelpers::waitWhileFailPointEnabled( &hangBeforeLinearizableReadConcern, opCtx, "hangBeforeLinearizableReadConcern", [opCtx]() { LOGV2(20994, @@ -493,9 +494,8 @@ Status waitForLinearizableReadConcernImpl(OperationContext* opCtx, const int rea uow.commit(); }); } - WriteConcernOptions wc = WriteConcernOptions( - WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, readConcernTimeout); - + WriteConcernOptions wc = WriteConcernOptions{ + WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, readConcernTimeout}; repl::OpTime lastOpApplied = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp(); auto awaitReplResult = replCoord->awaitReplication(opCtx, lastOpApplied, wc); diff --git a/src/mongo/db/read_write_concern_defaults.cpp b/src/mongo/db/read_write_concern_defaults.cpp index 50e6ada1832..68dd5fb9477 100644 --- a/src/mongo/db/read_write_concern_defaults.cpp +++ b/src/mongo/db/read_write_concern_defaults.cpp @@ -91,7 +91,7 @@ void ReadWriteConcernDefaults::checkSuitabilityAsDefault(const ReadConcern& rc) void ReadWriteConcernDefaults::checkSuitabilityAsDefault(const WriteConcern& wc) { uassert(ErrorCodes::BadValue, "Unacknowledged write concern is not suitable for the default write concern", - !(wc.wMode.empty() && wc.wNumNodes < 1)); + !wc.isUnacknowledged()); uassert(ErrorCodes::BadValue, str::stream() << "'" << ReadWriteConcernProvenance::kSourceFieldName << "' must be unset in default write concern", diff --git a/src/mongo/db/read_write_concern_defaults_test.cpp b/src/mongo/db/read_write_concern_defaults_test.cpp index cde73217abd..9ff5cb9b9cb 100644 --- a/src/mongo/db/read_write_concern_defaults_test.cpp +++ b/src/mongo/db/read_write_concern_defaults_test.cpp @@ -100,7 +100,9 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithAbsentCWRWCWithImplicitWC ASSERT(defaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kImplicit); ASSERT(defaults.getDefaultWriteConcern()); - ASSERT_EQ(WriteConcernOptions::kMajority, defaults.getDefaultWriteConcern().get().wMode); + ASSERT(stdx::holds_alternative<std::string>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(WriteConcernOptions::kMajority, + stdx::get<std::string>(defaults.getDefaultWriteConcern()->w)); ASSERT(!defaults.getUpdateOpTime()); ASSERT(!defaults.getUpdateWallClockTime()); ASSERT_EQ(Date_t(), defaults.localUpdateWallClockTime()); @@ -144,7 +146,9 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithCWRWCNeverSetWithImplicit ASSERT(defaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kImplicit); ASSERT(defaults.getDefaultWriteConcern()); - ASSERT_EQ(WriteConcernOptions::kMajority, defaults.getDefaultWriteConcern().get().wMode); + ASSERT(stdx::holds_alternative<std::string>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(WriteConcernOptions::kMajority, + stdx::get<std::string>(defaults.getDefaultWriteConcern()->w)); ASSERT(!defaults.getUpdateOpTime()); ASSERT(!defaults.getUpdateWallClockTime()); ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); @@ -193,7 +197,9 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithUnsetCWRWCWithImplicitWCM ASSERT(defaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kImplicit); ASSERT(defaults.getDefaultWriteConcern()); - ASSERT_EQ(WriteConcernOptions::kMajority, defaults.getDefaultWriteConcern().get().wMode); + ASSERT(stdx::holds_alternative<std::string>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(WriteConcernOptions::kMajority, + stdx::get<std::string>(defaults.getDefaultWriteConcern()->w)); ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); @@ -222,10 +228,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithCWRWCNotSetThenSetWithImp RWConcernDefault newDefaults; newDefaults.setDefaultReadConcern( repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern)); - WriteConcernOptions wc; - wc.wNumNodes = 4; - wc.usedDefaultConstructedWC = false; - wc.notExplicitWValue = false; + WriteConcernOptions wc( + 4, WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoTimeout); newDefaults.setDefaultWriteConcern(wc); newDefaults.setUpdateOpTime(Timestamp(1, 2)); newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); @@ -239,7 +243,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithCWRWCNotSetThenSetWithImp ASSERT(defaults.getDefaultReadConcernSource()); ASSERT(defaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kGlobal); - ASSERT_EQ(4, defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(4, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); @@ -260,7 +265,9 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithCWRWCNotSetThenSetWithImp ASSERT(oldDefaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kImplicit); ASSERT(oldDefaults.getDefaultWriteConcern()); - ASSERT_EQ(WriteConcernOptions::kMajority, oldDefaults.getDefaultWriteConcern().get().wMode); + ASSERT(stdx::holds_alternative<std::string>(oldDefaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(WriteConcernOptions::kMajority, + stdx::get<std::string>(oldDefaults.getDefaultWriteConcern()->w)); ASSERT(!oldDefaults.getUpdateOpTime()); ASSERT(!oldDefaults.getUpdateWallClockTime()); ASSERT_GT(oldDefaults.localUpdateWallClockTime(), Date_t()); @@ -269,10 +276,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithCWRWCNotSetThenSetWithImp RWConcernDefault newDefaults; newDefaults.setDefaultReadConcern( repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern)); - WriteConcernOptions wc; - wc.wNumNodes = 4; - wc.usedDefaultConstructedWC = false; - wc.notExplicitWValue = false; + WriteConcernOptions wc( + 4, WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoTimeout); newDefaults.setDefaultWriteConcern(wc); newDefaults.setUpdateOpTime(Timestamp(1, 2)); newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); @@ -286,7 +291,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithCWRWCNotSetThenSetWithImp ASSERT(defaults.getDefaultReadConcernSource()); ASSERT(defaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kGlobal); - ASSERT_EQ(4, defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(4, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); @@ -300,10 +306,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetCWRWCWithImplicitWCW1) RWConcernDefault newDefaults; newDefaults.setDefaultReadConcern( repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern)); - WriteConcernOptions wc; - wc.wNumNodes = 4; - wc.usedDefaultConstructedWC = false; - wc.notExplicitWValue = false; + WriteConcernOptions wc( + 4, WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoTimeout); newDefaults.setDefaultWriteConcern(wc); newDefaults.setUpdateOpTime(Timestamp(1, 2)); newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); @@ -315,7 +319,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetCWRWCWithImplicitWCW1) ASSERT(defaults.getDefaultReadConcernSource()); ASSERT(defaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kGlobal); - ASSERT_EQ(4, defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(4, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); @@ -328,10 +333,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetCWRWCWithImplicitWCMaj RWConcernDefault newDefaults; newDefaults.setDefaultReadConcern( repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern)); - WriteConcernOptions wc; - wc.wNumNodes = 4; - wc.usedDefaultConstructedWC = false; - wc.notExplicitWValue = false; + WriteConcernOptions wc( + 4, WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoTimeout); newDefaults.setDefaultWriteConcern(wc); newDefaults.setUpdateOpTime(Timestamp(1, 2)); newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); @@ -344,7 +347,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetCWRWCWithImplicitWCMaj ASSERT(defaults.getDefaultReadConcernSource()); ASSERT(defaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kGlobal); - ASSERT_EQ(4, defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(4, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); @@ -372,8 +376,9 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWriteConcernSourceImplicitWit // The default write concern source should be set to implicit if wc.usedDefaultConstructedWC is // true - ASSERT_EQ(0, defaults.getDefaultWriteConcern()->wNumNodes); - ASSERT_EQ(WriteConcernOptions::kMajority, defaults.getDefaultWriteConcern().get().wMode); + ASSERT(stdx::holds_alternative<std::string>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(WriteConcernOptions::kMajority, + stdx::get<std::string>(defaults.getDefaultWriteConcern()->w)); ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); @@ -447,7 +452,9 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetAndUnSetCWRCWithImplic ASSERT(defaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kGlobal); ASSERT(defaults.getDefaultWriteConcern()); - ASSERT_EQ(WriteConcernOptions::kMajority, defaults.getDefaultWriteConcern().get().wMode); + ASSERT(stdx::holds_alternative<std::string>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(WriteConcernOptions::kMajority, + stdx::get<std::string>(defaults.getDefaultWriteConcern()->w)); ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); @@ -470,7 +477,9 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetAndUnSetCWRCWithImplic ASSERT(defaults.getDefaultReadConcernSource()); ASSERT(defaults.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kImplicit); - ASSERT_EQ(WriteConcernOptions::kMajority, defaults.getDefaultWriteConcern().get().wMode); + ASSERT(stdx::holds_alternative<std::string>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(WriteConcernOptions::kMajority, + stdx::get<std::string>(defaults.getDefaultWriteConcern()->w)); ASSERT_EQ(Timestamp(1, 3), *defaults.getUpdateOpTime()); ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); @@ -511,10 +520,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithoutInvalidateDoesNotCallL RWConcernDefault newDefaults2; newDefaults2.setDefaultReadConcern( repl::ReadConcernArgs(repl::ReadConcernLevel::kAvailableReadConcern)); - WriteConcernOptions wc; - wc.wNumNodes = 4; - wc.usedDefaultConstructedWC = false; - wc.notExplicitWValue = false; + WriteConcernOptions wc( + 4, WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoTimeout); newDefaults2.setDefaultWriteConcern(wc); newDefaults2.setUpdateOpTime(Timestamp(3, 4)); newDefaults2.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678)); @@ -563,10 +570,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestInvalidate) { RWConcernDefault newDefaults2; newDefaults2.setDefaultReadConcern( repl::ReadConcernArgs(repl::ReadConcernLevel::kAvailableReadConcern)); - WriteConcernOptions wc; - wc.wNumNodes = 4; - wc.usedDefaultConstructedWC = false; - wc.notExplicitWValue = false; + WriteConcernOptions wc( + 4, WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoTimeout); newDefaults2.setDefaultWriteConcern(wc); newDefaults2.setUpdateOpTime(Timestamp(3, 4)); newDefaults2.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678)); @@ -580,7 +585,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestInvalidate) { ASSERT(defaults2.getDefaultReadConcernSource()); ASSERT(defaults2.getDefaultReadConcernSource() == DefaultReadConcernSourceEnum::kGlobal); - ASSERT_EQ(4, defaults2.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(defaults2.getDefaultWriteConcern()->w)); + ASSERT_EQ(4, stdx::get<int64_t>(defaults2.getDefaultWriteConcern()->w)); ASSERT_EQ(Timestamp(3, 4), *defaults2.getUpdateOpTime()); ASSERT_EQ(5678, defaults2.getUpdateWallClockTime()->toMillisSinceEpoch()); ASSERT_GT(defaults2.localUpdateWallClockTime(), Date_t()); @@ -734,7 +740,8 @@ protected: // default read concern source is not saved on disk. ASSERT(!defaults.getDefaultReadConcernSource()); - ASSERT_EQ(4, defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(4, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); ASSERT(defaults.getUpdateOpTime()); ASSERT(defaults.getUpdateWallClockTime()); // Default write concern source is not saved on disk. @@ -833,7 +840,8 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, repl::ReadConcernLevel::kMajorityReadConcern); ASSERT(!defaults.getDefaultReadConcernSource()); - ASSERT_EQ(5, defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(5, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); // Default write concern source is not saved on disk. @@ -858,8 +866,7 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, repl::ReadConcernLevel::kMajorityReadConcern); ASSERT(!defaults.getDefaultReadConcernSource()); - ASSERT_EQ(oldDefaults.getDefaultWriteConcern()->wNumNodes, - defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(oldDefaults.getDefaultWriteConcern()->w == defaults.getDefaultWriteConcern()->w); ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); // Default write concern source is not saved on disk. @@ -903,7 +910,8 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, defaults.getDefaultReadConcern()->getLevel()); ASSERT(!defaults.getDefaultReadConcernSource()); - ASSERT_EQ(5, defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(5, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); // Default write concern source is not saved on disk. @@ -944,7 +952,8 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, uassertStatusOK(WriteConcernOptions::parse(BSON("w" << 5)))); ASSERT(newDefaults.getDefaultReadConcern()->getLevel() == defaults.getDefaultReadConcern()->getLevel()); - ASSERT_EQ(5, defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(5, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); // Default write concern source is not saved on disk. ASSERT(!defaults.getDefaultWriteConcernSource()); @@ -952,7 +961,8 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, _rwcd.refreshIfNecessary(operationContext()); newDefaults = _rwcd.getDefault(operationContext()); ASSERT(newDefaults.getDefaultWriteConcern()); - ASSERT_EQ(5, newDefaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(stdx::holds_alternative<int64_t>(newDefaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(5, stdx::get<int64_t>(newDefaults.getDefaultWriteConcern()->w)); // Default write concern source is calculated through 'getDefault'. ASSERT(newDefaults.getDefaultWriteConcernSource() == DefaultWriteConcernSourceEnum::kGlobal); } @@ -967,8 +977,7 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, ASSERT(!defaults.getDefaultReadConcernSource()); ASSERT(defaults.getDefaultWriteConcern()); - ASSERT_EQ(oldDefaults.getDefaultWriteConcern()->wNumNodes, - defaults.getDefaultWriteConcern()->wNumNodes); + ASSERT(oldDefaults.getDefaultWriteConcern()->w == defaults.getDefaultWriteConcern()->w); ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); // Default write concern source is not saved on disk. @@ -1033,8 +1042,9 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, ASSERT(oldDefaults.getDefaultReadConcern()->getLevel() == defaults.getDefaultReadConcern()->getLevel()); ASSERT(!defaults.getDefaultReadConcernSource()); - ASSERT_EQ(1, defaults.getDefaultWriteConcern()->wNumNodes); - ASSERT_EQ(0, defaults.getDefaultWriteConcern()->wTimeout); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(1, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(WriteConcernOptions::kNoTimeout, defaults.getDefaultWriteConcern()->wTimeout); ASSERT(WriteConcernOptions::SyncMode::JOURNAL == defaults.getDefaultWriteConcern()->syncMode); ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); @@ -1059,8 +1069,9 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, ASSERT(oldDefaults.getDefaultReadConcern()->getLevel() == defaults.getDefaultReadConcern()->getLevel()); ASSERT(!defaults.getDefaultReadConcernSource()); - ASSERT_EQ(1, defaults.getDefaultWriteConcern()->wNumNodes); - ASSERT_EQ(12345, defaults.getDefaultWriteConcern()->wTimeout); + ASSERT(stdx::holds_alternative<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(1, stdx::get<int64_t>(defaults.getDefaultWriteConcern()->w)); + ASSERT_EQ(Milliseconds(12345), defaults.getDefaultWriteConcern()->wTimeout); ASSERT(WriteConcernOptions::SyncMode::UNSET == defaults.getDefaultWriteConcern()->syncMode); ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); diff --git a/src/mongo/db/repl/repl_set_config.cpp b/src/mongo/db/repl/repl_set_config.cpp index c2d355833de..9ed67568c77 100644 --- a/src/mongo/db/repl/repl_set_config.cpp +++ b/src/mongo/db/repl/repl_set_config.cpp @@ -35,6 +35,7 @@ #include <algorithm> #include <fmt/format.h> +#include <fmt/ranges.h> #include <functional> #include "mongo/bson/util/bson_check.h" @@ -427,40 +428,46 @@ Status ReplSetConfig::_validate(bool allowSplitHorizonIP) const { Status ReplSetConfig::checkIfWriteConcernCanBeSatisfied( const WriteConcernOptions& writeConcern) const { - if (writeConcern.hasCustomWriteMode()) { - StatusWith<ReplSetTagPattern> tagPatternStatus = findCustomWriteMode(writeConcern.wMode); - if (!tagPatternStatus.isOK()) { - return tagPatternStatus.getStatus(); + if (auto wNumNodes = stdx::get_if<int64_t>(&writeConcern.w)) { + if (*wNumNodes > getNumDataBearingMembers()) { + return Status(ErrorCodes::UnsatisfiableWriteConcern, "Not enough data-bearing nodes"); } - ReplSetTagMatch matcher(tagPatternStatus.getValue()); - for (size_t j = 0; j < getMembers().size(); ++j) { - const MemberConfig& memberConfig = getMembers()[j]; - for (MemberConfig::TagIterator it = memberConfig.tagsBegin(); - it != memberConfig.tagsEnd(); - ++it) { - if (matcher.update(*it)) { - return Status::OK(); - } - } - } - // Even if all the nodes in the set had a given write it still would not satisfy this - // write concern mode. - return Status(ErrorCodes::UnsatisfiableWriteConcern, - str::stream() << "Not enough nodes match write concern mode \"" - << writeConcern.wMode << "\""); - } else { - int nodesRemaining = writeConcern.wNumNodes; - for (size_t j = 0; j < getMembers().size(); ++j) { - if (!getMembers()[j].isArbiter()) { // Only count data-bearing nodes - --nodesRemaining; - if (nodesRemaining <= 0) { - return Status::OK(); - } + return Status::OK(); + } + + StatusWith<ReplSetTagPattern> tagPatternStatus = [&]() { + auto wMode = stdx::get_if<std::string>(&writeConcern.w); + return wMode ? findCustomWriteMode(*wMode) + : makeCustomWriteMode(stdx::get<WTags>(writeConcern.w)); + }(); + + if (!tagPatternStatus.isOK()) { + return tagPatternStatus.getStatus(); + } + + ReplSetTagMatch matcher(tagPatternStatus.getValue()); + for (size_t j = 0; j < getMembers().size(); ++j) { + const MemberConfig& memberConfig = getMembers()[j]; + for (MemberConfig::TagIterator it = memberConfig.tagsBegin(); it != memberConfig.tagsEnd(); + ++it) { + if (matcher.update(*it)) { + return Status::OK(); } } - return Status(ErrorCodes::UnsatisfiableWriteConcern, "Not enough data-bearing nodes"); } + + // Even if all the nodes in the set had a given write it still would not satisfy this + // write concern mode. + auto wModeForError = [&]() { + auto wMode = stdx::get_if<std::string>(&writeConcern.w); + return wMode ? fmt::format("\"{}\"", *wMode) + : fmt::format("{}", stdx::get<WTags>(writeConcern.w)); + }(); + + return Status(ErrorCodes::UnsatisfiableWriteConcern, + str::stream() << "Not enough nodes match write concern mode \"" << wModeForError + << "\""); } int ReplSetConfig::getNumDataBearingMembers() const { @@ -534,31 +541,25 @@ ReplSetTag ReplSetConfig::findTag(StringData key, StringData value) const { } StatusWith<ReplSetTagPattern> ReplSetConfig::findCustomWriteMode(StringData patternName) const { + // The string "majority" corresponds to the internal "$majority" custom write mode + if (patternName == WriteConcernOptions::kMajority) { + patternName = kMajorityWriteConcernModeName; + } + const StringMap<ReplSetTagPattern>::const_iterator iter = _customWriteConcernModes.find(patternName); if (iter == _customWriteConcernModes.end()) { return StatusWith<ReplSetTagPattern>( ErrorCodes::UnknownReplWriteConcern, - str::stream() << "No write concern mode named '" << str::escape(patternName.toString()) - << "' found in replica set configuration"); + "No write concern mode named '{}' found in replica set configuration"_format( + str::escape(patternName.toString()))); } return StatusWith<ReplSetTagPattern>(iter->second); } -StatusWith<ReplSetTagPattern> ReplSetConfig::makeCustomWriteMode(const BSONObj& wTags) const { +StatusWith<ReplSetTagPattern> ReplSetConfig::makeCustomWriteMode(const WTags& wTags) const { ReplSetTagPattern pattern = _tagConfig.makePattern(); - for (auto e : wTags) { - const auto tagName = e.fieldNameStringData(); - if (!e.isNumber()) { - return { - ErrorCodes::BadValue, - fmt::format( - "Custom write mode only supports integer values, found: \"{}\" for tag: \"{}\"", - e.toString(), - tagName)}; - } - - const auto minNodesWithTag = e.safeNumberInt(); + for (const auto& [tagName, minNodesWithTag] : wTags) { auto status = _tagConfig.addTagCountConstraintToPattern(&pattern, tagName, minNodesWithTag); if (!status.isOK()) { return status; @@ -734,13 +735,19 @@ bool ReplSetConfig::containsCustomizedGetLastErrorDefaults() const { // Since the ReplSetConfig always has a WriteConcernOptions, the only way to know if it has been // customized through getLastErrorDefaults is if it's different from { w: 1, wtimeout: 0 }. const auto& getLastErrorDefaults = getDefaultWriteConcern(); - return !(getLastErrorDefaults.wNumNodes == 1 && getLastErrorDefaults.wTimeout == 0 && - getLastErrorDefaults.syncMode == WriteConcernOptions::SyncMode::UNSET); + if (auto wNumNodes = stdx::get_if<int64_t>(&getLastErrorDefaults.w); + !wNumNodes || *wNumNodes != 1) + return true; + if (getLastErrorDefaults.wTimeout != Milliseconds::zero()) + return true; + if (getLastErrorDefaults.syncMode != WriteConcernOptions::SyncMode::UNSET) + return true; + return false; } Status ReplSetConfig::validateWriteConcern(const WriteConcernOptions& writeConcern) const { if (writeConcern.hasCustomWriteMode()) { - return findCustomWriteMode(writeConcern.wMode).getStatus(); + return findCustomWriteMode(stdx::get<std::string>(writeConcern.w)).getStatus(); } return Status::OK(); } diff --git a/src/mongo/db/repl/repl_set_config.h b/src/mongo/db/repl/repl_set_config.h index 0ff4711f14d..5e410b65963 100644 --- a/src/mongo/db/repl/repl_set_config.h +++ b/src/mongo/db/repl/repl_set_config.h @@ -436,7 +436,7 @@ public: * @returns `ErrorCodes::NoSuchKey` if a tag was provided which is not found in * the local tag config. */ - StatusWith<ReplSetTagPattern> makeCustomWriteMode(const BSONObj& wTags) const; + StatusWith<ReplSetTagPattern> makeCustomWriteMode(const WTags& wTags) const; /** * Returns the "tags configuration" for this replicaset. diff --git a/src/mongo/db/repl/repl_set_config_test.cpp b/src/mongo/db/repl/repl_set_config_test.cpp index 5ebf3e6ac69..f93aa2e962a 100644 --- a/src/mongo/db/repl/repl_set_config_test.cpp +++ b/src/mongo/db/repl/repl_set_config_test.cpp @@ -93,8 +93,8 @@ TEST(ReplSetConfig, ParseMinimalConfigAndCheckDefaults) { ASSERT_EQUALS(1, config.getConfigTerm()); ASSERT_EQUALS(1, config.getNumMembers()); ASSERT_EQUALS(MemberId(0), config.membersBegin()->getId()); - ASSERT_EQUALS(1, config.getDefaultWriteConcern().wNumNodes); - ASSERT_EQUALS("", config.getDefaultWriteConcern().wMode); + ASSERT(stdx::holds_alternative<int64_t>(config.getDefaultWriteConcern().w)); + ASSERT_EQUALS(1, stdx::get<int64_t>(config.getDefaultWriteConcern().w)); ASSERT_EQUALS(ReplSetConfig::kDefaultHeartbeatInterval, config.getHeartbeatInterval()); ASSERT_EQUALS(ReplSetConfig::kDefaultHeartbeatTimeoutPeriod, config.getHeartbeatTimeoutPeriod()); @@ -1272,8 +1272,7 @@ bool operator==(const ReplSetConfig& a, const ReplSetConfig& b) { a.getElectionTimeoutPeriod() == b.getElectionTimeoutPeriod() && a.isChainingAllowed() == b.isChainingAllowed() && a.getConfigServer() == b.getConfigServer() && - a.getDefaultWriteConcern().wNumNodes == b.getDefaultWriteConcern().wNumNodes && - a.getDefaultWriteConcern().wMode == b.getDefaultWriteConcern().wMode && + a.getDefaultWriteConcern().w == b.getDefaultWriteConcern().w && a.getProtocolVersion() == b.getProtocolVersion() && a.getReplicaSetId() == b.getReplicaSetId(); } @@ -1476,34 +1475,34 @@ TEST(ReplSetConfig, CheckIfWriteConcernCanBeSatisfied) { << BSON("dc" << 3) << "invalidNotEnoughNodes" << BSON("rack" << 6))))); WriteConcernOptions validNumberWC; - validNumberWC.wNumNodes = 5; + validNumberWC.w = 5; ASSERT_OK(configA.checkIfWriteConcernCanBeSatisfied(validNumberWC)); WriteConcernOptions invalidNumberWC; - invalidNumberWC.wNumNodes = 6; + invalidNumberWC.w = 6; ASSERT_EQUALS(ErrorCodes::UnsatisfiableWriteConcern, configA.checkIfWriteConcernCanBeSatisfied(invalidNumberWC)); WriteConcernOptions majorityWC; - majorityWC.wMode = "majority"; + majorityWC.w = "majority"; ASSERT_OK(configA.checkIfWriteConcernCanBeSatisfied(majorityWC)); WriteConcernOptions validModeWC; - validModeWC.wMode = "valid"; + validModeWC.w = "valid"; ASSERT_OK(configA.checkIfWriteConcernCanBeSatisfied(validModeWC)); WriteConcernOptions fakeModeWC; - fakeModeWC.wMode = "fake"; + fakeModeWC.w = "fake"; ASSERT_EQUALS(ErrorCodes::UnknownReplWriteConcern, configA.checkIfWriteConcernCanBeSatisfied(fakeModeWC)); WriteConcernOptions invalidModeNotEnoughValuesWC; - invalidModeNotEnoughValuesWC.wMode = "invalidNotEnoughValues"; + invalidModeNotEnoughValuesWC.w = "invalidNotEnoughValues"; ASSERT_EQUALS(ErrorCodes::UnsatisfiableWriteConcern, configA.checkIfWriteConcernCanBeSatisfied(invalidModeNotEnoughValuesWC)); WriteConcernOptions invalidModeNotEnoughNodesWC; - invalidModeNotEnoughNodesWC.wMode = "invalidNotEnoughNodes"; + invalidModeNotEnoughNodesWC.w = "invalidNotEnoughNodes"; ASSERT_EQUALS(ErrorCodes::UnsatisfiableWriteConcern, configA.checkIfWriteConcernCanBeSatisfied(invalidModeNotEnoughNodesWC)); } @@ -2102,16 +2101,11 @@ TEST(ReplSetConfig, MakeCustomWriteMode) { << BSON("NYC" << "NY"))))); - auto swPattern = config.makeCustomWriteMode(BSON("NYC" - << "invalid value type")); - ASSERT_FALSE(swPattern.isOK()); - ASSERT_EQ(swPattern.getStatus().code(), ErrorCodes::BadValue); - - swPattern = config.makeCustomWriteMode(BSON("NonExistentTag" << 1)); + auto swPattern = config.makeCustomWriteMode({{"NonExistentTag", 1}}); ASSERT_FALSE(swPattern.isOK()); ASSERT_EQ(swPattern.getStatus().code(), ErrorCodes::NoSuchKey); - swPattern = config.makeCustomWriteMode(BSON("NYC" << 1)); + swPattern = config.makeCustomWriteMode({{"NYC", 1}}); ASSERT_TRUE(swPattern.isOK()); } diff --git a/src/mongo/db/repl/repl_set_config_validators.h b/src/mongo/db/repl/repl_set_config_validators.h index c02cfb1ee15..56eb9e80dd8 100644 --- a/src/mongo/db/repl/repl_set_config_validators.h +++ b/src/mongo/db/repl/repl_set_config_validators.h @@ -49,10 +49,11 @@ inline Status validateTrue(bool boolVal) { } inline Status validateDefaultWriteConcernHasMember(const WriteConcernOptions& defaultWriteConcern) { - if (defaultWriteConcern.wMode.empty() && defaultWriteConcern.wNumNodes == 0) { + if (defaultWriteConcern.isUnacknowledged()) { return Status(ErrorCodes::BadValue, "Default write concern mode must wait for at least 1 member"); } + return Status::OK(); } diff --git a/src/mongo/db/repl/replication_coordinator.h b/src/mongo/db/repl/replication_coordinator.h index 12dd736fdd0..cbd90683591 100644 --- a/src/mongo/db/repl/replication_coordinator.h +++ b/src/mongo/db/repl/replication_coordinator.h @@ -225,7 +225,7 @@ public: * ErrorCodes::ExceededTimeLimit if the opCtx->getMaxTimeMicrosRemaining is reached before * the data has been sufficiently replicated * ErrorCodes::NotWritablePrimary if the node is not a writable primary - * ErrorCodes::UnknownReplWriteConcern if the writeConcern.wMode contains a write concern + * ErrorCodes::UnknownReplWriteConcern if the writeConcern.w contains a write concern * mode that is not known * ErrorCodes::ShutdownInProgress if we are mid-shutdown * ErrorCodes::Interrupted if the operation was killed with killop() diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp index 0021211d17e..391011091c9 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -1895,17 +1895,19 @@ bool ReplicationCoordinatorImpl::_doneWaitingForReplication_inlock( invariant(writeConcern.syncMode != WriteConcernOptions::SyncMode::UNSET); const bool useDurableOpTime = writeConcern.syncMode == WriteConcernOptions::SyncMode::JOURNAL; - if (writeConcern.wMode.empty()) { - if (writeConcern.wTags()) { - auto tagPattern = uassertStatusOK(_rsConfig.makeCustomWriteMode(*writeConcern.wTags())); + if (!stdx::holds_alternative<std::string>(writeConcern.w)) { + if (auto wTags = stdx::get_if<WTags>(&writeConcern.w)) { + auto tagPattern = uassertStatusOK(_rsConfig.makeCustomWriteMode(*wTags)); return _topCoord->haveTaggedNodesReachedOpTime(opTime, tagPattern, useDurableOpTime); } return _topCoord->haveNumNodesReachedOpTime( - opTime, writeConcern.wNumNodes, useDurableOpTime); + opTime, stdx::get<int64_t>(writeConcern.w), useDurableOpTime); } + StringData patternName; - if (writeConcern.wMode == WriteConcernOptions::kMajority) { + auto wMode = stdx::get<std::string>(writeConcern.w); + if (wMode == WriteConcernOptions::kMajority) { if (_externalState->snapshotsEnabled() && !gTestingSnapshotBehaviorInIsolation) { // Make sure we have a valid "committed" snapshot up to the needed optime. if (!_currentCommittedSnapshot) { @@ -1955,8 +1957,9 @@ bool ReplicationCoordinatorImpl::_doneWaitingForReplication_inlock( // *** Needed for J:True, writeConcernMajorityShouldJournal:False (appliedOpTime snapshot). patternName = ReplSetConfig::kMajorityWriteConcernModeName; } else { - patternName = writeConcern.wMode; + patternName = wMode; } + auto tagPattern = uassertStatusOK(_rsConfig.findCustomWriteMode(patternName)); if (writeConcern.checkCondition == WriteConcernOptions::CheckCondition::OpTime) { return _topCoord->haveTaggedNodesReachedOpTime(opTime, tagPattern, useDurableOpTime); @@ -5921,8 +5924,7 @@ WriteConcernOptions ReplicationCoordinatorImpl::_populateUnsetWriteConcernOption WithLock lk, WriteConcernOptions wc) { WriteConcernOptions writeConcern(wc); if (writeConcern.syncMode == WriteConcernOptions::SyncMode::UNSET) { - if (writeConcern.wMode == WriteConcernOptions::kMajority && - getWriteConcernMajorityShouldJournal_inlock()) { + if (writeConcern.isMajority() && getWriteConcernMajorityShouldJournal_inlock()) { writeConcern.syncMode = WriteConcernOptions::SyncMode::JOURNAL; } else { writeConcern.syncMode = WriteConcernOptions::SyncMode::NONE; diff --git a/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp index f738cc855f0..781c10f0df0 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp @@ -881,10 +881,8 @@ TEST_F(ReplCoordTest, ReconfigThatChangesIDWCFromW1toWMajWithoutCWWCSetFails) { TEST_F(ReplCoordTest, ReconfigThatChangesIDWCWMajToW1WithCWWCSetPasses) { RWConcernDefault newDefaults; - WriteConcernOptions wc; - wc.wMode = "majority"; - wc.usedDefaultConstructedWC = false; - wc.notExplicitWValue = false; + WriteConcernOptions wc( + "majority", WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoTimeout); newDefaults.setDefaultWriteConcern(wc); lookupMock.setLookupCallReturnValue(std::move(newDefaults)); @@ -944,10 +942,8 @@ TEST_F(ReplCoordTest, ReconfigThatChangesIDWCWMajToW1WithCWWCSetPasses) { TEST_F(ReplCoordTest, ReconfigThatChangesIDWCW1ToWMajWithCWWCSetPasses) { RWConcernDefault newDefaults; - WriteConcernOptions wc; - wc.wMode = "majority"; - wc.usedDefaultConstructedWC = false; - wc.notExplicitWValue = false; + WriteConcernOptions wc( + "majority", WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoTimeout); newDefaults.setDefaultWriteConcern(wc); lookupMock.setLookupCallReturnValue(std::move(newDefaults)); @@ -2270,10 +2266,8 @@ TEST_F(ReplCoordReconfigTest, NodesWithNewlyAddedFieldSetHavePriorityZero) { TEST_F(ReplCoordReconfigTest, ArbiterNodesShouldNeverHaveNewlyAddedField) { RWConcernDefault newDefaults; - WriteConcernOptions wc; - wc.wMode = "majority"; - wc.usedDefaultConstructedWC = false; - wc.notExplicitWValue = false; + WriteConcernOptions wc( + "majority", WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoTimeout); newDefaults.setDefaultWriteConcern(wc); lookupMock.setLookupCallReturnValue(std::move(newDefaults)); diff --git a/src/mongo/db/repl/replication_coordinator_impl_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_test.cpp index 9fcd9adf9b8..cf9efee882b 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_test.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_test.cpp @@ -624,7 +624,7 @@ TEST_F(ReplCoordTest, NodeReturnsImmediatelyWhenAwaitReplicationIsRanAgainstASta WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern.wNumNodes = 2; + writeConcern.w = 2; // Because we didn't set ReplSettings.replSet, it will think we're a standalone so // awaitReplication will always work. @@ -652,10 +652,9 @@ TEST_F(ReplCoordTest, NodeReturnsNotPrimaryWhenRunningAwaitReplicationAgainstASe OpTimeWithTermOne time(100, 1); - WriteConcernOptions writeConcern; - writeConcern.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern.wNumNodes = 0; // Waiting for 0 nodes always works - writeConcern.wMode = ""; + // Waiting for 0 nodes always works + WriteConcernOptions writeConcern( + 0, WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoWaiting); // Node should fail to awaitReplication when not primary. ReplicationCoordinator::StatusAndDuration statusAndDur = @@ -680,10 +679,9 @@ TEST_F(ReplCoordTest, NodeReturnsOkWhenRunningAwaitReplicationAgainstPrimaryWith OpTimeWithTermOne time(100, 1); - WriteConcernOptions writeConcern; - writeConcern.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern.wNumNodes = 0; // Waiting for 0 nodes always works - writeConcern.wMode = ""; + // Waiting for 0 nodes always works + WriteConcernOptions writeConcern( + 0, WriteConcernOptions::SyncMode::UNSET, WriteConcernOptions::kNoWaiting); // Become primary. ASSERT_OK(getReplCoord()->setFollowerMode(MemberState::RS_SECONDARY)); @@ -727,7 +725,7 @@ TEST_F(ReplCoordTest, WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern.wNumNodes = 1; + writeConcern.w = 1; writeConcern.syncMode = WriteConcernOptions::SyncMode::JOURNAL; auto opCtx = makeOperationContext(); @@ -741,7 +739,7 @@ TEST_F(ReplCoordTest, ASSERT_OK(statusAndDur.status); // 2 nodes waiting for time1 - writeConcern.wNumNodes = 2; + writeConcern.w = 2; statusAndDur = getReplCoord()->awaitReplication(opCtx.get(), time1, writeConcern); ASSERT_EQUALS(ErrorCodes::WriteConcernFailed, statusAndDur.status); // Applied is not durable and will not satisfy WriteConcern with SyncMode JOURNAL. @@ -765,7 +763,7 @@ TEST_F(ReplCoordTest, ASSERT_OK(statusAndDur.status); // 3 nodes waiting for time2 - writeConcern.wNumNodes = 3; + writeConcern.w = 3; statusAndDur = getReplCoord()->awaitReplication(opCtx.get(), time2, writeConcern); ASSERT_EQUALS(ErrorCodes::WriteConcernFailed, statusAndDur.status); ASSERT_OK(getReplCoord()->setLastAppliedOptime_forTest(2, 3, time2)); @@ -801,7 +799,7 @@ TEST_F(ReplCoordTest, NodeReturnsWriteConcernFailedUntilASufficientNumberOfNodes WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern.wNumNodes = 1; + writeConcern.w = 1; auto opCtx = makeOperationContext(); @@ -816,7 +814,7 @@ TEST_F(ReplCoordTest, NodeReturnsWriteConcernFailedUntilASufficientNumberOfNodes ASSERT_OK(statusAndDur.status); // 2 nodes waiting for time1 - writeConcern.wNumNodes = 2; + writeConcern.w = 2; statusAndDur = getReplCoord()->awaitReplication(opCtx.get(), time1, writeConcern); ASSERT_EQUALS(ErrorCodes::WriteConcernFailed, statusAndDur.status); ASSERT_OK(getReplCoord()->setLastAppliedOptime_forTest(2, 1, time1)); @@ -836,7 +834,7 @@ TEST_F(ReplCoordTest, NodeReturnsWriteConcernFailedUntilASufficientNumberOfNodes ASSERT_OK(statusAndDur.status); // 3 nodes waiting for time2 - writeConcern.wNumNodes = 3; + writeConcern.w = 3; statusAndDur = getReplCoord()->awaitReplication(opCtx.get(), time2, writeConcern); ASSERT_EQUALS(ErrorCodes::WriteConcernFailed, statusAndDur.status); ASSERT_OK(getReplCoord()->setLastAppliedOptime_forTest(2, 3, time2)); @@ -871,7 +869,7 @@ TEST_F(ReplCoordTest, // Test invalid write concern WriteConcernOptions invalidWriteConcern; invalidWriteConcern.wTimeout = WriteConcernOptions::kNoWaiting; - invalidWriteConcern.wMode = "fakemode"; + invalidWriteConcern.w = "fakemode"; auto opCtx = makeOperationContext(); ReplicationCoordinator::StatusAndDuration statusAndDur = @@ -936,16 +934,16 @@ TEST_F( // Set up valid write concerns for the rest of the test WriteConcernOptions majorityWriteConcern; majorityWriteConcern.wTimeout = WriteConcernOptions::kNoWaiting; - majorityWriteConcern.wMode = WriteConcernOptions::kMajority; + majorityWriteConcern.w = WriteConcernOptions::kMajority; majorityWriteConcern.syncMode = WriteConcernOptions::SyncMode::JOURNAL; WriteConcernOptions multiDCWriteConcern; multiDCWriteConcern.wTimeout = WriteConcernOptions::kNoWaiting; - multiDCWriteConcern.wMode = "multiDC"; + multiDCWriteConcern.w = "multiDC"; WriteConcernOptions multiRackWriteConcern; multiRackWriteConcern.wTimeout = WriteConcernOptions::kNoWaiting; - multiRackWriteConcern.wMode = "multiDCAndRack"; + multiRackWriteConcern.w = "multiDCAndRack"; auto opCtx = makeOperationContext(); // Nothing satisfied @@ -1090,7 +1088,7 @@ TEST_F(ReplCoordTest, NodeReturnsOkWhenAWriteConcernWithNoTimeoutHasBeenSatisfie WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoTimeout; - writeConcern.wNumNodes = 2; + writeConcern.w = 2; // 2 nodes waiting for time1 awaiter.setOpTime(time1); @@ -1114,7 +1112,7 @@ TEST_F(ReplCoordTest, NodeReturnsOkWhenAWriteConcernWithNoTimeoutHasBeenSatisfie awaiter.reset(); // 3 nodes waiting for time2 - writeConcern.wNumNodes = 3; + writeConcern.w = 3; awaiter.setWriteConcern(writeConcern); awaiter.start(); ASSERT_OK(getReplCoord()->setLastAppliedOptime_forTest(2, 2, time2)); @@ -1311,7 +1309,7 @@ TEST_F(ReplCoordTest, NodeReturnsWriteConcernFailedWhenAWriteConcernTimesOutBefo WriteConcernOptions writeConcern; writeConcern.wDeadline = getNet()->now() + Milliseconds(50); - writeConcern.wNumNodes = 2; + writeConcern.w = 2; // 2 nodes waiting for time2 awaiter.setOpTime(time2); @@ -1357,7 +1355,7 @@ TEST_F(ReplCoordTest, WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoTimeout; - writeConcern.wNumNodes = 2; + writeConcern.w = 2; // 2 nodes waiting for time2 awaiter.setOpTime(time2); @@ -1453,7 +1451,7 @@ TEST_F(ReplCoordTest, NodeReturnsNotPrimaryWhenSteppingDownBeforeSatisfyingAWrit WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoTimeout; - writeConcern.wNumNodes = 2; + writeConcern.w = 2; // 2 nodes waiting for time2 awaiter.setOpTime(time2); @@ -1492,7 +1490,7 @@ TEST_F(ReplCoordTest, WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoTimeout; - writeConcern.wNumNodes = 2; + writeConcern.w = 2; // 2 nodes waiting for time2 @@ -5113,7 +5111,7 @@ TEST_F(ReplCoordTest, DoNotProcessSelfWhenUpdatePositionContainsInfoAboutSelf) { WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern.wNumNodes = 1; + writeConcern.w = 1; auto opCtx = makeOperationContext(); @@ -5163,7 +5161,7 @@ TEST_F(ReplCoordTest, ProcessUpdatePositionWhenItsConfigVersionIsDifferent) { WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern.wNumNodes = 1; + writeConcern.w = 1; // receive updatePosition with a different config version, 3 replCoordSetMyLastAppliedOpTime(time2, Date_t() + Seconds(100)); @@ -5214,7 +5212,7 @@ TEST_F(ReplCoordTest, DoNotProcessUpdatePositionOfMembersWhoseIdsAreNotInTheConf WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern.wNumNodes = 1; + writeConcern.w = 1; // receive updatePosition with nonexistent member id UpdatePositionArgs args; @@ -5266,7 +5264,7 @@ TEST_F(ReplCoordTest, WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern.wNumNodes = 1; + writeConcern.w = 1; // receive a good update position replCoordSetMyLastAppliedOpTime(time2, Date_t() + Seconds(100)); @@ -5299,7 +5297,7 @@ TEST_F(ReplCoordTest, ASSERT_OK(getReplCoord()->processReplSetUpdatePosition(args)); ASSERT_OK(getReplCoord()->awaitReplication(opCtx.get(), time2, writeConcern).status); - writeConcern.wNumNodes = 3; + writeConcern.w = 3; ASSERT_OK(getReplCoord()->awaitReplication(opCtx.get(), time2, writeConcern).status); } @@ -5414,7 +5412,7 @@ TEST_F(ReplCoordTest, AwaitReplicationShouldResolveAsNormalDuringAReconfig) { // 3 nodes waiting for time WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoTimeout; - writeConcern.wNumNodes = 3; + writeConcern.w = 3; writeConcern.syncMode = WriteConcernOptions::SyncMode::NONE; ReplicationAwaiter awaiter(getReplCoord(), getServiceContext()); @@ -5423,7 +5421,7 @@ TEST_F(ReplCoordTest, AwaitReplicationShouldResolveAsNormalDuringAReconfig) { awaiter.start(); ReplicationAwaiter awaiterJournaled(getReplCoord(), getServiceContext()); - writeConcern.wMode = WriteConcernOptions::kMajority; + writeConcern.w = WriteConcernOptions::kMajority; awaiterJournaled.setOpTime(time); awaiterJournaled.setWriteConcern(writeConcern); awaiterJournaled.start(); @@ -5563,19 +5561,13 @@ TEST_F( // 3 nodes waiting for time WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoTimeout; - writeConcern.wNumNodes = 3; + writeConcern.w = 3; ReplicationAwaiter awaiter(getReplCoord(), getServiceContext()); awaiter.setOpTime(time); awaiter.setWriteConcern(writeConcern); awaiter.start(); - ReplicationAwaiter awaiterJournaled(getReplCoord(), getServiceContext()); - writeConcern.wMode = WriteConcernOptions::kMajority; - awaiterJournaled.setOpTime(time); - awaiterJournaled.setWriteConcern(writeConcern); - awaiterJournaled.start(); - // reconfig to fewer nodes Status status(ErrorCodes::InternalError, "Not Set"); stdx::thread reconfigThread( @@ -5590,9 +5582,6 @@ TEST_F( ReplicationCoordinator::StatusAndDuration statusAndDur = awaiter.getResult(); ASSERT_EQUALS(ErrorCodes::UnsatisfiableWriteConcern, statusAndDur.status); awaiter.reset(); - ReplicationCoordinator::StatusAndDuration statusAndDurJournaled = awaiterJournaled.getResult(); - ASSERT_EQUALS(ErrorCodes::UnsatisfiableWriteConcern, statusAndDurJournaled.status); - awaiterJournaled.reset(); } TEST_F(ReplCoordTest, @@ -5637,7 +5626,7 @@ TEST_F(ReplCoordTest, // majority nodes waiting for time WriteConcernOptions writeConcern; writeConcern.wTimeout = WriteConcernOptions::kNoTimeout; - writeConcern.wMode = WriteConcernOptions::kMajority; + writeConcern.w = WriteConcernOptions::kMajority; writeConcern.syncMode = WriteConcernOptions::SyncMode::NONE; @@ -5649,7 +5638,7 @@ TEST_F(ReplCoordTest, // demonstrate that majority cannot currently be satisfied WriteConcernOptions writeConcern2; writeConcern2.wTimeout = WriteConcernOptions::kNoWaiting; - writeConcern2.wMode = WriteConcernOptions::kMajority; + writeConcern2.w = WriteConcernOptions::kMajority; writeConcern.syncMode = WriteConcernOptions::SyncMode::NONE; ASSERT_EQUALS(ErrorCodes::WriteConcernFailed, @@ -5702,7 +5691,7 @@ TEST_F(ReplCoordTest, WriteConcernOptions majorityWriteConcern; majorityWriteConcern.wTimeout = WriteConcernOptions::kNoWaiting; - majorityWriteConcern.wMode = WriteConcernOptions::kMajority; + majorityWriteConcern.w = WriteConcernOptions::kMajority; majorityWriteConcern.syncMode = WriteConcernOptions::SyncMode::JOURNAL; auto opCtx = makeOperationContext(); @@ -7414,7 +7403,7 @@ TEST_F( HostAndPort("test1", 1234)); WriteConcernOptions wc; - wc.wMode = WriteConcernOptions::kMajority; + wc.w = WriteConcernOptions::kMajority; wc.syncMode = WriteConcernOptions::SyncMode::UNSET; ASSERT(WriteConcernOptions::SyncMode::NONE == getReplCoord()->populateUnsetWriteConcernOptionsSyncMode(wc).syncMode); @@ -7434,7 +7423,7 @@ TEST_F( HostAndPort("test1", 1234)); WriteConcernOptions wc; - wc.wMode = WriteConcernOptions::kMajority; + wc.w = WriteConcernOptions::kMajority; wc.syncMode = WriteConcernOptions::SyncMode::UNSET; ASSERT(WriteConcernOptions::SyncMode::JOURNAL == getReplCoord()->populateUnsetWriteConcernOptionsSyncMode(wc).syncMode); @@ -7452,7 +7441,7 @@ TEST_F(ReplCoordTest, PopulateUnsetWriteConcernOptionsSyncModeReturnsInputIfSync HostAndPort("test1", 1234)); WriteConcernOptions wc; - wc.wMode = WriteConcernOptions::kMajority; + wc.w = WriteConcernOptions::kMajority; ASSERT(WriteConcernOptions::SyncMode::NONE == getReplCoord()->populateUnsetWriteConcernOptionsSyncMode(wc).syncMode); @@ -7478,11 +7467,11 @@ TEST_F(ReplCoordTest, PopulateUnsetWriteConcernOptionsSyncModeReturnsInputIfWMod WriteConcernOptions wc; wc.syncMode = WriteConcernOptions::SyncMode::UNSET; - wc.wMode = "not the value of kMajority"; + wc.w = "not the value of kMajority"; ASSERT(WriteConcernOptions::SyncMode::NONE == getReplCoord()->populateUnsetWriteConcernOptionsSyncMode(wc).syncMode); - wc.wMode = "like literally anythingelse"; + wc.w = "like literally anythingelse"; ASSERT(WriteConcernOptions::SyncMode::NONE == getReplCoord()->populateUnsetWriteConcernOptionsSyncMode(wc).syncMode); } diff --git a/src/mongo/db/repl/replication_coordinator_mock.cpp b/src/mongo/db/repl/replication_coordinator_mock.cpp index 85df34dcb91..43d59838487 100644 --- a/src/mongo/db/repl/replication_coordinator_mock.cpp +++ b/src/mongo/db/repl/replication_coordinator_mock.cpp @@ -645,7 +645,7 @@ void ReplicationCoordinatorMock::createWMajorityWriteAvailabilityDateWaiter(OpTi WriteConcernOptions ReplicationCoordinatorMock::populateUnsetWriteConcernOptionsSyncMode( WriteConcernOptions wc) { if (wc.syncMode == WriteConcernOptions::SyncMode::UNSET) { - if (wc.wMode == WriteConcernOptions::kMajority) { + if (wc.isMajority()) { wc.syncMode = WriteConcernOptions::SyncMode::JOURNAL; } else { wc.syncMode = WriteConcernOptions::SyncMode::NONE; diff --git a/src/mongo/db/s/chunk_move_write_concern_options.cpp b/src/mongo/db/s/chunk_move_write_concern_options.cpp index c41db7ad8ec..f7edb110c45 100644 --- a/src/mongo/db/s/chunk_move_write_concern_options.cpp +++ b/src/mongo/db/s/chunk_move_write_concern_options.cpp @@ -94,7 +94,7 @@ StatusWith<WriteConcernOptions> ChunkMoveWriteConcernOptions::getEffectiveWriteC if (writeConcern.needToWaitForOtherNodes() && writeConcern.wTimeout == WriteConcernOptions::kNoTimeout) { // Don't allow no timeout - writeConcern.wTimeout = durationCount<Milliseconds>(kDefaultWriteTimeoutForMigration); + writeConcern.wTimeout = duration_cast<Milliseconds>(kDefaultWriteTimeoutForMigration); } return writeConcern; diff --git a/src/mongo/db/s/clone_catalog_data_command.cpp b/src/mongo/db/s/clone_catalog_data_command.cpp index 1874b6e2376..cdfde40535b 100644 --- a/src/mongo/db/s/clone_catalog_data_command.cpp +++ b/src/mongo/db/s/clone_catalog_data_command.cpp @@ -95,11 +95,7 @@ public: str::stream() << "_shardsvrCloneCatalogData can only be run on shard servers", serverGlobalParams.clusterRole == ClusterRole::ShardServer); - uassert(ErrorCodes::InvalidOptions, - str::stream() - << "_shardsvrCloneCatalogData must be called with majority writeConcern, got " - << cmdObj, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(getName(), opCtx->getWriteConcern()); const auto cloneCatalogDataRequest = CloneCatalogData::parse(IDLParserErrorContext("_shardsvrCloneCatalogData"), cmdObj); diff --git a/src/mongo/db/s/config/configsvr_abort_reshard_collection_command.cpp b/src/mongo/db/s/config/configsvr_abort_reshard_collection_command.cpp index 7a5653bb594..9cb503a2979 100644 --- a/src/mongo/db/s/config/configsvr_abort_reshard_collection_command.cpp +++ b/src/mongo/db/s/config/configsvr_abort_reshard_collection_command.cpp @@ -107,9 +107,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrAbortReshardCollection can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - "_configsvrAbortReshardCollection must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); const auto reshardingUUID = retrieveReshardingUUID(opCtx, ns()); diff --git a/src/mongo/db/s/config/configsvr_add_shard_command.cpp b/src/mongo/db/s/config/configsvr_add_shard_command.cpp index 7504b149e50..7e45c082250 100644 --- a/src/mongo/db/s/config/configsvr_add_shard_command.cpp +++ b/src/mongo/db/s/config/configsvr_add_shard_command.cpp @@ -98,11 +98,7 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrAddShard can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert( - ErrorCodes::InvalidOptions, - str::stream() << "_configsvrAddShard must be called with majority writeConcern, got " - << cmdObj, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(getName(), opCtx->getWriteConcern()); // Set the operation context read concern level to local for reads into the config database. repl::ReadConcernArgs::get(opCtx) = diff --git a/src/mongo/db/s/config/configsvr_clear_jumbo_flag_command.cpp b/src/mongo/db/s/config/configsvr_clear_jumbo_flag_command.cpp index 9f4a3a07cc2..f97d164151b 100644 --- a/src/mongo/db/s/config/configsvr_clear_jumbo_flag_command.cpp +++ b/src/mongo/db/s/config/configsvr_clear_jumbo_flag_command.cpp @@ -57,9 +57,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrClearJumboFlag can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - "_configsvrClearJumboFlag must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); // Set the operation context read concern level to local for reads into the config // database. diff --git a/src/mongo/db/s/config/configsvr_commit_reshard_collection_command.cpp b/src/mongo/db/s/config/configsvr_commit_reshard_collection_command.cpp index 6cf6440a36f..fb67df820c4 100644 --- a/src/mongo/db/s/config/configsvr_commit_reshard_collection_command.cpp +++ b/src/mongo/db/s/config/configsvr_commit_reshard_collection_command.cpp @@ -79,10 +79,8 @@ public: ErrorCodes::IllegalOperation, format(FMT_STRING("{} can only be run on config servers"), definition()->getName()), serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - format(FMT_STRING("{} must be called with majority writeConcern"), - definition()->getName()), - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); UUID reshardingUUID = retrieveReshardingUUID(opCtx, ns()); diff --git a/src/mongo/db/s/config/configsvr_create_database_command.cpp b/src/mongo/db/s/config/configsvr_create_database_command.cpp index 86d2c81607d..83e9271954f 100644 --- a/src/mongo/db/s/config/configsvr_create_database_command.cpp +++ b/src/mongo/db/s/config/configsvr_create_database_command.cpp @@ -71,10 +71,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrCreateDatabase can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - str::stream() - << "_configsvrCreateDatabase must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); // Set the operation context read concern level to local for reads into the config // database. diff --git a/src/mongo/db/s/config/configsvr_ensure_chunk_version_is_greater_than_command.cpp b/src/mongo/db/s/config/configsvr_ensure_chunk_version_is_greater_than_command.cpp index 1b39374f9da..9acbfb2f107 100644 --- a/src/mongo/db/s/config/configsvr_ensure_chunk_version_is_greater_than_command.cpp +++ b/src/mongo/db/s/config/configsvr_ensure_chunk_version_is_greater_than_command.cpp @@ -52,10 +52,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrEnsureChunkVersionIsGreaterThan can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - "_configsvrEnsureChunkVersionIsGreaterThan must be called with majority " - "writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); ShardingCatalogManager::get(opCtx)->ensureChunkVersionIsGreaterThan( opCtx, request().getCollectionUUID(), diff --git a/src/mongo/db/s/config/configsvr_refine_collection_shard_key_command.cpp b/src/mongo/db/s/config/configsvr_refine_collection_shard_key_command.cpp index ef766dd1d68..a58168f06be 100644 --- a/src/mongo/db/s/config/configsvr_refine_collection_shard_key_command.cpp +++ b/src/mongo/db/s/config/configsvr_refine_collection_shard_key_command.cpp @@ -56,9 +56,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrRefineCollectionShardKey can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - "_configsvrRefineCollectionShardKey must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); _internalRun(opCtx); } diff --git a/src/mongo/db/s/config/configsvr_remove_chunks_command.cpp b/src/mongo/db/s/config/configsvr_remove_chunks_command.cpp index 77289aa3100..c08c93b9d8e 100644 --- a/src/mongo/db/s/config/configsvr_remove_chunks_command.cpp +++ b/src/mongo/db/s/config/configsvr_remove_chunks_command.cpp @@ -62,9 +62,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrRemoveChunks can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - "_configsvrRemoveChunks must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); // Set the operation context read concern level to local for reads into the config // database. diff --git a/src/mongo/db/s/config/configsvr_remove_shard_command.cpp b/src/mongo/db/s/config/configsvr_remove_shard_command.cpp index 16e74bb3dbf..73ad8dcbb21 100644 --- a/src/mongo/db/s/config/configsvr_remove_shard_command.cpp +++ b/src/mongo/db/s/config/configsvr_remove_shard_command.cpp @@ -99,11 +99,7 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrRemoveShard can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert( - ErrorCodes::InvalidOptions, - str::stream() << "_configsvrRemoveShard must be called with majority writeConcern, got " - << cmdObj, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(getName(), opCtx->getWriteConcern()); ON_BLOCK_EXIT([&opCtx] { repl::ReplClientInfo::forClient(opCtx->getClient()).setLastOpToSystemLastOpTime(opCtx); diff --git a/src/mongo/db/s/config/configsvr_remove_tags_command.cpp b/src/mongo/db/s/config/configsvr_remove_tags_command.cpp index 9bf9dbed174..ce1afe24e15 100644 --- a/src/mongo/db/s/config/configsvr_remove_tags_command.cpp +++ b/src/mongo/db/s/config/configsvr_remove_tags_command.cpp @@ -62,9 +62,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrRemoveTags can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - "_configsvrRemoveTags must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); // Set the operation context read concern level to local for reads into the config // database. diff --git a/src/mongo/db/s/config/configsvr_rename_collection_metadata_command.cpp b/src/mongo/db/s/config/configsvr_rename_collection_metadata_command.cpp index 854f6e359e4..87a069ec3f4 100644 --- a/src/mongo/db/s/config/configsvr_rename_collection_metadata_command.cpp +++ b/src/mongo/db/s/config/configsvr_rename_collection_metadata_command.cpp @@ -75,9 +75,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrRenameCollectionMetadata can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - "_configsvrRenameCollectionMetadata must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); opCtx->setAlwaysInterruptAtStepDownOrUp(); diff --git a/src/mongo/db/s/config/configsvr_repair_sharded_collection_chunks_history_command.cpp b/src/mongo/db/s/config/configsvr_repair_sharded_collection_chunks_history_command.cpp index 4586c03cc68..615e1098ccc 100644 --- a/src/mongo/db/s/config/configsvr_repair_sharded_collection_chunks_history_command.cpp +++ b/src/mongo/db/s/config/configsvr_repair_sharded_collection_chunks_history_command.cpp @@ -92,11 +92,7 @@ public: repl::ReadConcernArgs::get(opCtx) = repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern); - uassert(ErrorCodes::InvalidOptions, - str::stream() << "_configsvrRepairShardedCollectionChunksHistory must be called " - "with majority writeConcern, got " - << cmdObj, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(getName(), opCtx->getWriteConcern()); const NamespaceString nss{parseNs(unusedDbName, cmdObj)}; diff --git a/src/mongo/db/s/config/configsvr_reshard_collection_cmd.cpp b/src/mongo/db/s/config/configsvr_reshard_collection_cmd.cpp index c98393c64dd..55bcaa9bbd1 100644 --- a/src/mongo/db/s/config/configsvr_reshard_collection_cmd.cpp +++ b/src/mongo/db/s/config/configsvr_reshard_collection_cmd.cpp @@ -91,9 +91,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrReshardCollection can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - "_configsvrReshardCollection must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); repl::ReadConcernArgs::get(opCtx) = repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern); diff --git a/src/mongo/db/s/config/configsvr_set_allow_migrations_command.cpp b/src/mongo/db/s/config/configsvr_set_allow_migrations_command.cpp index f70df3d9d9f..01c3032aa40 100644 --- a/src/mongo/db/s/config/configsvr_set_allow_migrations_command.cpp +++ b/src/mongo/db/s/config/configsvr_set_allow_migrations_command.cpp @@ -56,9 +56,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_configsvrSetAllowMigrations can only be run on config servers", serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - uassert(ErrorCodes::InvalidOptions, - "_configsvrSetAllowMigrations must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); // Set the operation context read concern level to local for reads into the config // database. diff --git a/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp b/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp index de70ad64fd7..0a103392946 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp @@ -495,8 +495,9 @@ void ShardingCatalogManager::refineCollectionShardKey(OperationContext* opCtx, // documents than a normal operation, so we override the write concern to not use a // wTimeout, matching the behavior before the API was introduced. WriteConcernOptions originalWC = opCtx->getWriteConcern(); - opCtx->setWriteConcern(WriteConcernOptions( - WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, 0)); + opCtx->setWriteConcern(WriteConcernOptions{WriteConcernOptions::kMajority, + WriteConcernOptions::SyncMode::UNSET, + WriteConcernOptions::kNoTimeout}); ON_BLOCK_EXIT([opCtx, originalWC] { opCtx->setWriteConcern(originalWC); }); withTransactionAPI(opCtx, nss, std::move(updateCollectionAndChunksWithAPIFn)); diff --git a/src/mongo/db/s/migration_chunk_cloner_source_legacy_commands.cpp b/src/mongo/db/s/migration_chunk_cloner_source_legacy_commands.cpp index f1bd1269c86..713303693e5 100644 --- a/src/mongo/db/s/migration_chunk_cloner_source_legacy_commands.cpp +++ b/src/mongo/db/s/migration_chunk_cloner_source_legacy_commands.cpp @@ -316,8 +316,9 @@ public: // in this batch may have been rolled back. In this case, we abort the migration. if (opTime) { WriteConcernResult wcResult; - WriteConcernOptions majorityWC( - WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, 0); + WriteConcernOptions majorityWC{WriteConcernOptions::kMajority, + WriteConcernOptions::SyncMode::UNSET, + WriteConcernOptions::kNoTimeout}; uassertStatusOK(waitForWriteConcern(opCtx, opTime.get(), majorityWC, &wcResult)); auto rollbackIdAtMigrationInit = [&]() { diff --git a/src/mongo/db/s/migration_destination_manager.cpp b/src/mongo/db/s/migration_destination_manager.cpp index 562b689c2f7..364195ee50b 100644 --- a/src/mongo/db/s/migration_destination_manager.cpp +++ b/src/mongo/db/s/migration_destination_manager.cpp @@ -98,7 +98,7 @@ const WriteConcernOptions kMajorityWriteConcern(WriteConcernOptions::kMajority, // writeConcernMajorityJournalDefault is set to true // in the ReplSetConfig. WriteConcernOptions::SyncMode::UNSET, - -1); + WriteConcernOptions::kNoWaiting); BSONObj makeLocalReadConcernWithAfterClusterTime(Timestamp afterClusterTime) { return BSON(repl::ReadConcernArgs::kReadConcernFieldName @@ -230,7 +230,7 @@ bool opReplicatedEnough(OperationContext* opCtx, // Enforce the user specified write concern after "majority" so it covers the union of the 2 // write concerns in case the user's write concern is stronger than majority WriteConcernOptions userWriteConcern(writeConcern); - userWriteConcern.wTimeout = -1; + userWriteConcern.wTimeout = WriteConcernOptions::kNoWaiting; writeConcernResult.wTimedOut = false; Status userStatus = diff --git a/src/mongo/db/s/migration_destination_manager_legacy_commands.cpp b/src/mongo/db/s/migration_destination_manager_legacy_commands.cpp index 419f2144c8e..7f90fe3a6d6 100644 --- a/src/mongo/db/s/migration_destination_manager_legacy_commands.cpp +++ b/src/mongo/db/s/migration_destination_manager_legacy_commands.cpp @@ -341,11 +341,7 @@ public: BSONObjBuilder& result) override { opCtx->setAlwaysInterruptAtStepDownOrUp(); - uassert(ErrorCodes::InvalidOptions, - str::stream() << getName() << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); - + CommandHelpers::uassertCommandRunWithMajority(getName(), opCtx->getWriteConcern()); const auto sessionId = uassertStatusOK(MigrationSessionId::extractFromBSON(cmdObj)); LOGV2_DEBUG(5899101, 2, "Received _recvChunkReleaseCritSec", "sessionId"_attr = sessionId); diff --git a/src/mongo/db/s/migration_util.cpp b/src/mongo/db/s/migration_util.cpp index dbbcafd6aac..aeab3994ee9 100644 --- a/src/mongo/db/s/migration_util.cpp +++ b/src/mongo/db/s/migration_util.cpp @@ -840,7 +840,7 @@ void deleteMigrationCoordinatorDocumentLocally(OperationContext* opCtx, const UU NamespaceString::kMigrationCoordinatorsNamespace); store.remove(opCtx, BSON(MigrationCoordinatorDocument::kIdFieldName << migrationId), - {1, WriteConcernOptions::SyncMode::UNSET, Seconds(0)}); + WriteConcernOptions{1, WriteConcernOptions::SyncMode::UNSET, Seconds(0)}); } void ensureChunkVersionIsGreaterThan(OperationContext* opCtx, diff --git a/src/mongo/db/s/resharding/resharding_txn_cloner.cpp b/src/mongo/db/s/resharding/resharding_txn_cloner.cpp index d4b2cc88bee..cfb088a7655 100644 --- a/src/mongo/db/s/resharding/resharding_txn_cloner.cpp +++ b/src/mongo/db/s/resharding/resharding_txn_cloner.cpp @@ -186,7 +186,7 @@ void ReshardingTxnCloner::_updateProgressDocument(OperationContext* opCtx, opCtx, BSON(ReshardingTxnClonerProgress::kSourceIdFieldName << _sourceId.toBSON()), BSON("$set" << BSON(ReshardingTxnClonerProgress::kProgressFieldName << progress.toBSON())), - {1, WriteConcernOptions::SyncMode::UNSET, Seconds(0)}); + WriteConcernOptions{1, WriteConcernOptions::SyncMode::UNSET, Seconds(0)}); } SemiFuture<void> ReshardingTxnCloner::run( diff --git a/src/mongo/db/s/session_catalog_migration_source.cpp b/src/mongo/db/s/session_catalog_migration_source.cpp index ce330604c81..3d378f3a8ed 100644 --- a/src/mongo/db/s/session_catalog_migration_source.cpp +++ b/src/mongo/db/s/session_catalog_migration_source.cpp @@ -239,8 +239,9 @@ SessionCatalogMigrationSource::SessionCatalogMigrationSource(OperationContext* o auto opTimeToWait = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp(); WriteConcernResult result; - WriteConcernOptions majority( - WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, 0); + WriteConcernOptions majority{WriteConcernOptions::kMajority, + WriteConcernOptions::SyncMode::UNSET, + WriteConcernOptions::kNoTimeout}; uassertStatusOK(waitForWriteConcern(opCtx, opTimeToWait, majority, &result)); AutoGetCollection collection( diff --git a/src/mongo/db/s/sharding_logging.cpp b/src/mongo/db/s/sharding_logging.cpp index e6c56ad0538..c5c8925026c 100644 --- a/src/mongo/db/s/sharding_logging.cpp +++ b/src/mongo/db/s/sharding_logging.cpp @@ -102,7 +102,7 @@ Status ShardingLogging::logChangeChecked(OperationContext* opCtx, const BSONObj& detail, const WriteConcernOptions& writeConcern) { invariant(serverGlobalParams.clusterRole == ClusterRole::ConfigServer || - writeConcern.wMode == WriteConcernOptions::kMajority); + writeConcern.isMajority()); if (_changeLogCollectionCreated.load() == 0) { Status result = _createCappedConfigCollection( opCtx, kChangeLogCollectionName, kChangeLogCollectionSizeMB, writeConcern); diff --git a/src/mongo/db/s/shardsvr_abort_reshard_collection_command.cpp b/src/mongo/db/s/shardsvr_abort_reshard_collection_command.cpp index 1b43a1f7e76..aa319814395 100644 --- a/src/mongo/db/s/shardsvr_abort_reshard_collection_command.cpp +++ b/src/mongo/db/s/shardsvr_abort_reshard_collection_command.cpp @@ -61,9 +61,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_shardsvrAbortReshardCollection can only be run on shard servers", serverGlobalParams.clusterRole == ClusterRole::ShardServer); - uassert(ErrorCodes::InvalidOptions, - "_shardsvrAbortReshardCollection must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); std::vector<SharedSemiFuture<void>> futuresToWait; diff --git a/src/mongo/db/s/shardsvr_collmod_command.cpp b/src/mongo/db/s/shardsvr_collmod_command.cpp index 5591b5c841b..ab5516c9f63 100644 --- a/src/mongo/db/s/shardsvr_collmod_command.cpp +++ b/src/mongo/db/s/shardsvr_collmod_command.cpp @@ -96,11 +96,8 @@ public: auto const shardingState = ShardingState::get(opCtx); uassertStatusOK(shardingState->canAcceptShardedCommands()); - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); opCtx->setAlwaysInterruptAtStepDownOrUp(); diff --git a/src/mongo/db/s/shardsvr_collmod_participant_command.cpp b/src/mongo/db/s/shardsvr_collmod_participant_command.cpp index 155d298f751..5b8a791ec54 100644 --- a/src/mongo/db/s/shardsvr_collmod_participant_command.cpp +++ b/src/mongo/db/s/shardsvr_collmod_participant_command.cpp @@ -70,11 +70,8 @@ public: Response typedRun(OperationContext* opCtx) { uassertStatusOK(ShardingState::get(opCtx)->canAcceptShardedCommands()); - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); opCtx->setAlwaysInterruptAtStepDownOrUp(); diff --git a/src/mongo/db/s/shardsvr_commit_reshard_collection_command.cpp b/src/mongo/db/s/shardsvr_commit_reshard_collection_command.cpp index 5cf16bb5a7d..5cf90ea6441 100644 --- a/src/mongo/db/s/shardsvr_commit_reshard_collection_command.cpp +++ b/src/mongo/db/s/shardsvr_commit_reshard_collection_command.cpp @@ -61,9 +61,8 @@ public: uassert(ErrorCodes::IllegalOperation, "_shardsvrCommitReshardCollection can only be run on shard servers", serverGlobalParams.clusterRole == ClusterRole::ShardServer); - uassert(ErrorCodes::InvalidOptions, - "_shardsvrCommitReshardCollection must be called with majority writeConcern", - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); std::vector<SharedSemiFuture<void>> futuresToWait; diff --git a/src/mongo/db/s/shardsvr_create_collection_command.cpp b/src/mongo/db/s/shardsvr_create_collection_command.cpp index 2cae74399f1..d1312c7b3db 100644 --- a/src/mongo/db/s/shardsvr_create_collection_command.cpp +++ b/src/mongo/db/s/shardsvr_create_collection_command.cpp @@ -73,12 +73,8 @@ public: opCtx->setAlwaysInterruptAtStepDownOrUp(); - uassert( - ErrorCodes::InvalidOptions, - str::stream() - << "_shardsvrCreateCollection must be called with majority writeConcern, got " - << request().toBSON(BSONObj()), - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); uassert(ErrorCodes::NotImplemented, "Create Collection path has not been implemented", diff --git a/src/mongo/db/s/shardsvr_create_collection_participant_command.cpp b/src/mongo/db/s/shardsvr_create_collection_participant_command.cpp index 77f4d8b4b69..4ef72fd2f72 100644 --- a/src/mongo/db/s/shardsvr_create_collection_participant_command.cpp +++ b/src/mongo/db/s/shardsvr_create_collection_participant_command.cpp @@ -71,11 +71,8 @@ public: auto const shardingState = ShardingState::get(opCtx); uassertStatusOK(shardingState->canAcceptShardedCommands()); - uassert(ErrorCodes::InvalidOptions, - str::stream() << "_shardsvrCreateCollectionParticipant must be called with " - "majority writeConcern, got " - << request().toBSON(BSONObj()), - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); opCtx->setAlwaysInterruptAtStepDownOrUp(); diff --git a/src/mongo/db/s/shardsvr_drop_collection_command.cpp b/src/mongo/db/s/shardsvr_drop_collection_command.cpp index a2ca5259363..18f2a06d024 100644 --- a/src/mongo/db/s/shardsvr_drop_collection_command.cpp +++ b/src/mongo/db/s/shardsvr_drop_collection_command.cpp @@ -68,11 +68,8 @@ public: void typedRun(OperationContext* opCtx) { uassertStatusOK(ShardingState::get(opCtx)->canAcceptShardedCommands()); - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); opCtx->setAlwaysInterruptAtStepDownOrUp(); diff --git a/src/mongo/db/s/shardsvr_drop_collection_participant_command.cpp b/src/mongo/db/s/shardsvr_drop_collection_participant_command.cpp index 2ee8de9ee70..a9b18cf3f61 100644 --- a/src/mongo/db/s/shardsvr_drop_collection_participant_command.cpp +++ b/src/mongo/db/s/shardsvr_drop_collection_participant_command.cpp @@ -70,11 +70,8 @@ public: void typedRun(OperationContext* opCtx) { uassertStatusOK(ShardingState::get(opCtx)->canAcceptShardedCommands()); - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); opCtx->setAlwaysInterruptAtStepDownOrUp(); diff --git a/src/mongo/db/s/shardsvr_drop_database_command.cpp b/src/mongo/db/s/shardsvr_drop_database_command.cpp index a8aaef5fbcf..2ae8fbb35ec 100644 --- a/src/mongo/db/s/shardsvr_drop_database_command.cpp +++ b/src/mongo/db/s/shardsvr_drop_database_command.cpp @@ -69,11 +69,8 @@ public: void typedRun(OperationContext* opCtx) { uassertStatusOK(ShardingState::get(opCtx)->canAcceptShardedCommands()); - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); opCtx->setAlwaysInterruptAtStepDownOrUp(); diff --git a/src/mongo/db/s/shardsvr_drop_database_participant_command.cpp b/src/mongo/db/s/shardsvr_drop_database_participant_command.cpp index 04fbdb2a779..a10c5114cdb 100644 --- a/src/mongo/db/s/shardsvr_drop_database_participant_command.cpp +++ b/src/mongo/db/s/shardsvr_drop_database_participant_command.cpp @@ -68,11 +68,8 @@ public: void typedRun(OperationContext* opCtx) { uassertStatusOK(ShardingState::get(opCtx)->canAcceptShardedCommands()); - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); const auto& dbName = request().getDbName(); diff --git a/src/mongo/db/s/shardsvr_move_primary_command.cpp b/src/mongo/db/s/shardsvr_move_primary_command.cpp index de96165a672..dbf579eabf6 100644 --- a/src/mongo/db/s/shardsvr_move_primary_command.cpp +++ b/src/mongo/db/s/shardsvr_move_primary_command.cpp @@ -110,11 +110,7 @@ public: str::stream() << "you have to specify where you want to move it", !toShard.empty()); - uassert( - ErrorCodes::InvalidOptions, - str::stream() << "_shardsvrMovePrimary must be called with majority writeConcern, got " - << cmdObj, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(getName(), opCtx->getWriteConcern()); ON_BLOCK_EXIT( [opCtx, dbNss] { Grid::get(opCtx)->catalogCache()->purgeDatabase(dbNss.db()); }); diff --git a/src/mongo/db/s/shardsvr_rename_collection_command.cpp b/src/mongo/db/s/shardsvr_rename_collection_command.cpp index 3cceae99c84..5a1918046f4 100644 --- a/src/mongo/db/s/shardsvr_rename_collection_command.cpp +++ b/src/mongo/db/s/shardsvr_rename_collection_command.cpp @@ -83,11 +83,8 @@ public: opCtx->setAlwaysInterruptAtStepDownOrUp(); - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); if (fromNss.db() != toNss.db()) { sharding_ddl_util::checkDbPrimariesOnTheSameShard(opCtx, fromNss, toNss); diff --git a/src/mongo/db/s/shardsvr_rename_collection_participant_command.cpp b/src/mongo/db/s/shardsvr_rename_collection_participant_command.cpp index 61c9718b4e1..aa2b2bf013c 100644 --- a/src/mongo/db/s/shardsvr_rename_collection_participant_command.cpp +++ b/src/mongo/db/s/shardsvr_rename_collection_participant_command.cpp @@ -71,11 +71,8 @@ public: using InvocationBase::InvocationBase; void typedRun(OperationContext* opCtx) { - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); auto const shardingState = ShardingState::get(opCtx); uassertStatusOK(shardingState->canAcceptShardedCommands()); @@ -167,11 +164,8 @@ public: using InvocationBase::InvocationBase; void typedRun(OperationContext* opCtx) { - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); auto const shardingState = ShardingState::get(opCtx); uassertStatusOK(shardingState->canAcceptShardedCommands()); diff --git a/src/mongo/db/s/shardsvr_reshard_collection_command.cpp b/src/mongo/db/s/shardsvr_reshard_collection_command.cpp index 46d068c12af..5a10de6a559 100644 --- a/src/mongo/db/s/shardsvr_reshard_collection_command.cpp +++ b/src/mongo/db/s/shardsvr_reshard_collection_command.cpp @@ -71,11 +71,8 @@ public: void typedRun(OperationContext* opCtx) { uassertStatusOK(ShardingState::get(opCtx)->canAcceptShardedCommands()); - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << opCtx->getWriteConcern().wMode, - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); // (Generic FCV reference): To run this command and ensure the consistency of the // metadata we need to make sure we are on a stable state. uassert( diff --git a/src/mongo/db/s/shardsvr_set_allow_migrations_command.cpp b/src/mongo/db/s/shardsvr_set_allow_migrations_command.cpp index 1703dd86829..401356eb0f9 100644 --- a/src/mongo/db/s/shardsvr_set_allow_migrations_command.cpp +++ b/src/mongo/db/s/shardsvr_set_allow_migrations_command.cpp @@ -72,11 +72,8 @@ public: opCtx->setAlwaysInterruptAtStepDownOrUp(); - uassert(ErrorCodes::InvalidOptions, - str::stream() << Request::kCommandName - << " must be called with majority writeConcern, got " - << request().toBSON(BSONObj()), - opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + CommandHelpers::uassertCommandRunWithMajority(Request::kCommandName, + opCtx->getWriteConcern()); SetAllowMigrationsRequest setAllowMigationsCmdRequest = request().getSetAllowMigrationsRequest(); diff --git a/src/mongo/db/service_entry_point_mongod.cpp b/src/mongo/db/service_entry_point_mongod.cpp index 771f3b9c564..7d96332a67e 100644 --- a/src/mongo/db/service_entry_point_mongod.cpp +++ b/src/mongo/db/service_entry_point_mongod.cpp @@ -174,7 +174,7 @@ public: // from the primary. if (repl::ReadConcernArgs::get(opCtx).getLevel() == repl::ReadConcernLevel::kLinearizableReadConcern) { - uassertStatusOK(mongo::waitForLinearizableReadConcern(opCtx, 0)); + uassertStatusOK(mongo::waitForLinearizableReadConcern(opCtx, Milliseconds::zero())); } } diff --git a/src/mongo/db/stats/server_write_concern_metrics.cpp b/src/mongo/db/stats/server_write_concern_metrics.cpp index 1229541db85..7dcedc40ef6 100644 --- a/src/mongo/db/stats/server_write_concern_metrics.cpp +++ b/src/mongo/db/stats/server_write_concern_metrics.cpp @@ -108,17 +108,22 @@ BSONObj ServerWriteConcernMetrics::toBSON() const { void ServerWriteConcernMetrics::WriteConcernCounters::recordWriteConcern( const WriteConcernOptions& writeConcernOptions, size_t numOps) { - if (!writeConcernOptions.wMode.empty()) { - if (writeConcernOptions.wMode == WriteConcernOptions::kMajority) { + if (auto wMode = stdx::get_if<std::string>(&writeConcernOptions.w)) { + if (writeConcernOptions.isMajority()) { wMajorityCount += numOps; return; } - wTagCounts[writeConcernOptions.wMode] += numOps; + wTagCounts[*wMode] += numOps; return; } - wNumCounts[writeConcernOptions.wNumNodes] += numOps; + if (stdx::holds_alternative<WTags>(writeConcernOptions.w)) { + // wTags is an internal feature that we don't track metrics for + return; + } + + wNumCounts[stdx::get<int64_t>(writeConcernOptions.w)] += numOps; } void ServerWriteConcernMetrics::WriteConcernMetricsForOperationType::recordWriteConcern( diff --git a/src/mongo/db/transaction_api_test.cpp b/src/mongo/db/transaction_api_test.cpp index 266d0c61847..9a6d2f8d25f 100644 --- a/src/mongo/db/transaction_api_test.cpp +++ b/src/mongo/db/transaction_api_test.cpp @@ -257,9 +257,11 @@ TEST_F(TxnAPITest, OwnSession_AttachesTxnMetadata) { TEST_F(TxnAPITest, OwnSession_AttachesWriteConcernOnCommit) { const std::vector<WriteConcernOptions> writeConcernOptions = { - WriteConcernOptions(1, WriteConcernOptions::SyncMode::JOURNAL, 100), - WriteConcernOptions("majority", WriteConcernOptions::SyncMode::FSYNC, 0), - WriteConcernOptions(2, WriteConcernOptions::SyncMode::NONE, -1)}; + WriteConcernOptions{1, WriteConcernOptions::SyncMode::JOURNAL, Milliseconds{100}}, + WriteConcernOptions{ + "majority", WriteConcernOptions::SyncMode::FSYNC, WriteConcernOptions::kNoTimeout}, + WriteConcernOptions{ + 2, WriteConcernOptions::SyncMode::NONE, WriteConcernOptions::kNoWaiting}}; for (const auto& writeConcern : writeConcernOptions) { opCtx()->setWriteConcern(writeConcern); @@ -321,9 +323,11 @@ TEST_F(TxnAPITest, OwnSession_AttachesWriteConcernOnCommit) { TEST_F(TxnAPITest, OwnSession_AttachesWriteConcernOnAbort) { const std::vector<WriteConcernOptions> writeConcernOptions = { - WriteConcernOptions(1, WriteConcernOptions::SyncMode::JOURNAL, 100), - WriteConcernOptions("majority", WriteConcernOptions::SyncMode::FSYNC, 0), - WriteConcernOptions(2, WriteConcernOptions::SyncMode::NONE, -1)}; + WriteConcernOptions{1, WriteConcernOptions::SyncMode::JOURNAL, Milliseconds{100}}, + WriteConcernOptions{ + "majority", WriteConcernOptions::SyncMode::FSYNC, WriteConcernOptions::kNoTimeout}, + WriteConcernOptions{ + 2, WriteConcernOptions::SyncMode::NONE, WriteConcernOptions::kNoWaiting}}; for (const auto& writeConcern : writeConcernOptions) { opCtx()->setWriteConcern(writeConcern); diff --git a/src/mongo/db/write_concern.cpp b/src/mongo/db/write_concern.cpp index 906984b38a5..91a8cddac86 100644 --- a/src/mongo/db/write_concern.cpp +++ b/src/mongo/db/write_concern.cpp @@ -56,7 +56,6 @@ namespace mongo { using repl::OpTime; -using repl::OpTimeAndWallTime; using std::string; static TimerStats gleWtimeStats; @@ -132,8 +131,9 @@ StatusWith<WriteConcernOptions> extractWriteConcern(OperationContext* opCtx, } return writeConcern; })(); - if (writeConcern.wNumNodes == 0 && writeConcern.wMode.empty()) { - writeConcern.wNumNodes = 1; + + if (writeConcern.isUnacknowledged()) { + writeConcern.w = 1; } writeConcern.notExplicitWValue = true; @@ -162,7 +162,7 @@ StatusWith<WriteConcernOptions> extractWriteConcern(OperationContext* opCtx, // Upconvert the writeConcern of any incoming requests from internal connections (i.e., // from other nodes in the cluster) to "majority." This protects against internal code that // does not specify writeConcern when writing to the config server. - writeConcern = { + writeConcern = WriteConcernOptions{ WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, Seconds(30)}; writeConcern.getProvenance().setSource( ReadWriteConcernProvenance::Source::internalWriteDefault); @@ -185,15 +185,18 @@ Status validateWriteConcern(OperationContext* opCtx, const WriteConcernOptions& const auto replMode = repl::ReplicationCoordinator::get(opCtx)->getReplicationMode(); - if (replMode == repl::ReplicationCoordinator::modeNone && writeConcern.wNumNodes > 1) { + if (replMode == repl::ReplicationCoordinator::modeNone && + (stdx::holds_alternative<int64_t>(writeConcern.w) && + stdx::get<int64_t>(writeConcern.w) > 1)) { return Status(ErrorCodes::BadValue, "cannot use 'w' > 1 when a host is not replicated"); } if (replMode != repl::ReplicationCoordinator::modeReplSet && writeConcern.hasCustomWriteMode()) { return Status(ErrorCodes::BadValue, - string("cannot use custom 'w' mode ") + writeConcern.wMode + - " when a host is not a member of a replica set"); + fmt::format("cannot use non-majority 'w' mode \"{}\" when a host is not a " + "member of a replica set", + stdx::get<std::string>(writeConcern.w))); } return Status::OK(); diff --git a/src/mongo/db/write_concern_options.cpp b/src/mongo/db/write_concern_options.cpp index 5557cee8298..4f1822884fa 100644 --- a/src/mongo/db/write_concern_options.cpp +++ b/src/mongo/db/write_concern_options.cpp @@ -65,8 +65,8 @@ constexpr StringData kWElectionIdFieldName = "wElectionId"_sd; } // namespace -constexpr int WriteConcernOptions::kNoTimeout; -constexpr int WriteConcernOptions::kNoWaiting; +constexpr Milliseconds WriteConcernOptions::kNoTimeout; +constexpr Milliseconds WriteConcernOptions::kNoWaiting; constexpr StringData WriteConcernOptions::kWriteConcernField; const char WriteConcernOptions::kMajority[] = "majority"; @@ -86,55 +86,37 @@ constexpr Seconds WriteConcernOptions::kWriteConcernTimeoutMigration; constexpr Seconds WriteConcernOptions::kWriteConcernTimeoutSharding; constexpr Seconds WriteConcernOptions::kWriteConcernTimeoutUserCommand; - -WriteConcernOptions::WriteConcernOptions(int numNodes, SyncMode sync, int timeout) - : WriteConcernOptions(numNodes, sync, Milliseconds(timeout)) {} - -WriteConcernOptions::WriteConcernOptions(const std::string& mode, SyncMode sync, int timeout) - : WriteConcernOptions(mode, sync, Milliseconds(timeout)) {} - WriteConcernOptions::WriteConcernOptions(int numNodes, SyncMode sync, Milliseconds timeout) - : syncMode(sync), wNumNodes(numNodes), wTimeout(durationCount<Milliseconds>(timeout)) {} + : w{numNodes}, + syncMode{sync}, + wTimeout(durationCount<Milliseconds>(timeout)), + usedDefaultConstructedWC{false}, + notExplicitWValue{false} {} WriteConcernOptions::WriteConcernOptions(const std::string& mode, SyncMode sync, Milliseconds timeout) - : syncMode(sync), wNumNodes(0), wMode(mode), wTimeout(durationCount<Milliseconds>(timeout)) {} + : w{mode}, + syncMode{sync}, + wTimeout(durationCount<Milliseconds>(timeout)), + usedDefaultConstructedWC{false}, + notExplicitWValue{false} {} StatusWith<WriteConcernOptions> WriteConcernOptions::parse(const BSONObj& obj) try { if (obj.isEmpty()) { return Status(ErrorCodes::FailedToParse, "write concern object cannot be empty"); } - auto writeConcernIdl = WriteConcernIdl::parse(IDLParserErrorContext("writeConcern"), obj); + auto writeConcernIdl = WriteConcernIdl::parse({"WriteConcernOptions"}, obj); auto parsedW = writeConcernIdl.getWriteConcernW(); WriteConcernOptions writeConcern; - writeConcern.usedDefaultConstructedWC = parsedW.usedDefaultConstructedW1() && - !writeConcernIdl.getJ() && !writeConcernIdl.getFsync() && - writeConcernIdl.getWtimeout() == 0; + writeConcern.usedDefaultConstructedWC = !parsedW && !writeConcernIdl.getJ() && + !writeConcernIdl.getFsync() && writeConcernIdl.getWtimeout() == 0; - if (!parsedW.usedDefaultConstructedW1()) { + if (parsedW) { writeConcern.notExplicitWValue = false; - auto wVal = parsedW.getValue(); - if (auto wNum = stdx::get_if<std::int64_t>(&wVal)) { - if (*wNum < 0 || - *wNum > - static_cast<std::decay_t<decltype(*wNum)>>(repl::ReplSetConfig::kMaxMembers)) { - uasserted(ErrorCodes::FailedToParse, - str::stream() << "w has to be a non-negative number and not greater than " - << repl::ReplSetConfig::kMaxMembers << ", found: " << wNum); - } - writeConcern.wNumNodes = static_cast<decltype(writeConcern.wNumNodes)>(*wNum); - } else if (auto tags = stdx::get_if<BSONObj>(&wVal)) { - writeConcern.wNumNodes = 0; - writeConcern._tags = *tags; - } else { - auto wMode = stdx::get_if<std::string>(&wVal); - invariant(wMode); - writeConcern.wNumNodes = 0; // Have to reset from default 1. - writeConcern.wMode = std::move(*wMode); - } + writeConcern.w = *parsedW; } auto j = writeConcernIdl.getJ(); @@ -153,7 +135,7 @@ StatusWith<WriteConcernOptions> WriteConcernOptions::parse(const BSONObj& obj) t writeConcern.syncMode = SyncMode::NONE; } - writeConcern.wTimeout = writeConcernIdl.getWtimeout(); + writeConcern.wTimeout = Milliseconds{writeConcernIdl.getWtimeout()}; if (auto source = writeConcernIdl.getSource()) { writeConcern._provenance = ReadWriteConcernProvenance(*source); } @@ -196,14 +178,7 @@ StatusWith<WriteConcernOptions> WriteConcernOptions::extractWCFromCommand(const BSONObj WriteConcernOptions::toBSON() const { BSONObjBuilder builder; - - if (_tags) { - builder.append("w", *_tags); - } else if (wMode.empty()) { - builder.append("w", wNumNodes); - } else { - builder.append("w", wMode); - } + serializeWriteConcernW(w, "w", &builder); if (syncMode == SyncMode::FSYNC) { builder.append("fsync", true); @@ -213,7 +188,9 @@ BSONObj WriteConcernOptions::toBSON() const { builder.append("j", false); } - builder.append("wtimeout", wTimeout); + // Historically we have serialized this as a int32_t, even though it is defined as an + // int64_t in our IDL format. + builder.append("wtimeout", static_cast<int32_t>(durationCount<Milliseconds>(wTimeout))); _provenance.serialize(&builder); @@ -221,14 +198,13 @@ BSONObj WriteConcernOptions::toBSON() const { } bool WriteConcernOptions::needToWaitForOtherNodes() const { - return !wMode.empty() || wNumNodes > 1 || _tags; + return stdx::holds_alternative<std::string>(w) || stdx::holds_alternative<WTags>(w) || + (stdx::holds_alternative<std::int64_t>(w) && stdx::get<std::int64_t>(w) > 1); } bool WriteConcernOptions::operator==(const WriteConcernOptions& other) const { - return (_tags ? other._tags && _tags->woCompare(*other._tags) == 0 - : wMode == other.wMode && wNumNodes == other.wNumNodes) && - syncMode == other.syncMode && wDeadline == other.wDeadline && wTimeout == other.wTimeout && - _provenance == other._provenance; + return w == other.w && syncMode == other.syncMode && wDeadline == other.wDeadline && + wTimeout == other.wTimeout && _provenance == other._provenance; } } // namespace mongo diff --git a/src/mongo/db/write_concern_options.h b/src/mongo/db/write_concern_options.h index 910b859c857..4478aa173fe 100644 --- a/src/mongo/db/write_concern_options.h +++ b/src/mongo/db/write_concern_options.h @@ -47,8 +47,8 @@ public: // Users can only provide OpTime condition, the others are used internally. enum class CheckCondition { OpTime, Config }; - static constexpr int kNoTimeout = 0; - static constexpr int kNoWaiting = -1; + static constexpr Milliseconds kNoTimeout{0}; + static constexpr Milliseconds kNoWaiting{-1}; static const BSONObj Default; static const BSONObj Acknowledged; @@ -64,24 +64,9 @@ public: static constexpr Seconds kWriteConcernTimeoutSharding{60}; static constexpr Seconds kWriteConcernTimeoutUserCommand{60}; - // It is assumed that a default-constructed WriteConcernOptions will be populated with the - // default options. If it is subsequently populated with non-default options, it is the caller's - // responsibility to set the usedDefaultConstructedWC and notExplicitWValue flag correctly. - WriteConcernOptions() - : syncMode(SyncMode::UNSET), - wNumNodes(1), - wMode(""), - wTimeout(0), - usedDefaultConstructedWC(true), - notExplicitWValue(true) {} - - WriteConcernOptions(int numNodes, SyncMode sync, int timeout); - - WriteConcernOptions(int numNodes, SyncMode sync, Milliseconds timeout); - - WriteConcernOptions(const std::string& mode, SyncMode sync, int timeout); - - WriteConcernOptions(const std::string& mode, SyncMode sync, Milliseconds timeout); + WriteConcernOptions() = default; + explicit WriteConcernOptions(int numNodes, SyncMode sync, Milliseconds timeout); + explicit WriteConcernOptions(const std::string& mode, SyncMode sync, Milliseconds timeout); static StatusWith<WriteConcernOptions> parse(const BSONObj& obj); @@ -131,21 +116,33 @@ public: } bool hasCustomWriteMode() const { - return !wMode.empty() && wMode != WriteConcernOptions::kMajority; + return stdx::holds_alternative<std::string>(w) && + stdx::get<std::string>(w) != WriteConcernOptions::kMajority; } - SyncMode syncMode; + /** + * Returns whether this write concern's w parameter is the number 0. + */ + bool isUnacknowledged() const { + return stdx::holds_alternative<int64_t>(w) && stdx::get<int64_t>(w) < 1; + } - // The w parameter for this write concern. The wMode represents the string format and - // takes precedence over the numeric format wNumNodes. - int wNumNodes; - std::string wMode; + /** + * Returns whether this write concern's w parameter is the string "majority". + */ + bool isMajority() const { + return stdx::holds_alternative<std::string>(w) && stdx::get<std::string>(w) == kMajority; + } + // The w parameter for this write concern. + WriteConcernW w{1}; + // Corresponds to the `j` or `fsync` parameters for write concern. + SyncMode syncMode{SyncMode::UNSET}; // Timeout in milliseconds. - int wTimeout; + Milliseconds wTimeout{kNoTimeout}; // Deadline. If this is set to something other than Date_t::max(), this takes precedence over // wTimeout. - Date_t wDeadline = Date_t::max(); + Date_t wDeadline{Date_t::max()}; // True if the default constructed WC ({w:1}) was used. // - Implicit default WC when value of w is {w:1}. @@ -159,7 +156,7 @@ public: // - Client-supplied WC. // - with (w) value set, for example ({writeConcern: {w:1}}). // - without (w) value set, for example ({writeConcern: {j: true}}). - bool usedDefaultConstructedWC = false; + bool usedDefaultConstructedWC{true}; // Used only for tracking opWriteConcernCounters metric. // True if the (w) value of the write concern used is not set explicitly by client: @@ -170,17 +167,12 @@ public: // - without (w) value set, for example ({writeConcern: {j: true}}). // - Client-supplied WC without (w) value set, for example ({writeConcern: {j: true}}). // - Internal commands set empty WC ({writeConcern: {}}). - bool notExplicitWValue = false; - - CheckCondition checkCondition = CheckCondition::OpTime; + bool notExplicitWValue{true}; - boost::optional<BSONObj> wTags() const { - return _tags; - } + CheckCondition checkCondition{CheckCondition::OpTime}; private: ReadWriteConcernProvenance _provenance; - boost::optional<BSONObj> _tags; }; } // namespace mongo diff --git a/src/mongo/db/write_concern_options_test.cpp b/src/mongo/db/write_concern_options_test.cpp index 4f2295feb7f..e9a3d111a08 100644 --- a/src/mongo/db/write_concern_options_test.cpp +++ b/src/mongo/db/write_concern_options_test.cpp @@ -67,9 +67,8 @@ TEST(WriteConcernOptionsTest, ParseSetsSyncModeToJournelIfJIsTrue) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::JOURNAL == options.syncMode); - ASSERT_EQUALS(1, options.wNumNodes); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(0, options.wTimeout); + ASSERT_EQUALS(1, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(WriteConcernOptions::kNoTimeout, options.wTimeout); } TEST(WriteConcernOptionsTest, ParseSetsSyncModeToFSyncIfFSyncIsTrue) { @@ -77,9 +76,8 @@ TEST(WriteConcernOptionsTest, ParseSetsSyncModeToFSyncIfFSyncIsTrue) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::FSYNC == options.syncMode); - ASSERT_EQUALS(1, options.wNumNodes); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(0, options.wTimeout); + ASSERT_EQUALS(1, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(WriteConcernOptions::kNoTimeout, options.wTimeout); } TEST(WriteConcernOptionsTest, ParseSetsSyncModeToNoneIfJIsFalse) { @@ -87,9 +85,8 @@ TEST(WriteConcernOptionsTest, ParseSetsSyncModeToNoneIfJIsFalse) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::NONE == options.syncMode); - ASSERT_EQUALS(1, options.wNumNodes); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(0, options.wTimeout); + ASSERT_EQUALS(1, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(WriteConcernOptions::kNoTimeout, options.wTimeout); } TEST(WriteConcernOptionsTest, ParseLeavesSyncModeAsUnsetIfFSyncIsFalse) { @@ -97,15 +94,14 @@ TEST(WriteConcernOptionsTest, ParseLeavesSyncModeAsUnsetIfFSyncIsFalse) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::UNSET == options.syncMode); - ASSERT_EQUALS(1, options.wNumNodes); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(0, options.wTimeout); + ASSERT_EQUALS(1, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(WriteConcernOptions::kNoTimeout, options.wTimeout); } TEST(WriteConcernOptionsTest, ParseReturnsFailedToParseIfWIsNotNumberOrStringOrObject) { auto status = WriteConcernOptions::parse(BSON("w" << true)).getStatus(); ASSERT_EQUALS(ErrorCodes::FailedToParse, status); - ASSERT_EQUALS("w has to be a number, string, or object", status.reason()); + ASSERT_STRING_CONTAINS(status.reason(), "w has to be a number, string, or object"); } TEST(WriteConcernOptionsTest, ParseReturnsFailedToParseIfWIsNegativeOrExceedsMaxMembers) { @@ -114,7 +110,6 @@ TEST(WriteConcernOptionsTest, ParseReturnsFailedToParseIfWIsNegativeOrExceedsMax auto st2 = WriteConcernOptions::parse(BSON("w" << -1)).getStatus(); ASSERT_EQUALS(ErrorCodes::FailedToParse, st2); - ASSERT_EQUALS(st1.reason(), st2.reason()); } TEST(WriteConcernOptionsTest, ParseSetsWNumNodesIfWIsANumber) { @@ -122,9 +117,8 @@ TEST(WriteConcernOptionsTest, ParseSetsWNumNodesIfWIsANumber) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::UNSET == options.syncMode); - ASSERT_EQUALS(3, options.wNumNodes); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(0, options.wTimeout); + ASSERT_EQUALS(3, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(WriteConcernOptions::kNoTimeout, options.wTimeout); } TEST(WriteConcernOptionsTest, ParseSetsWTimeoutToZeroIfWTimeoutIsNotANumber) { @@ -133,9 +127,8 @@ TEST(WriteConcernOptionsTest, ParseSetsWTimeoutToZeroIfWTimeoutIsNotANumber) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::UNSET == options.syncMode); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(1, options.wNumNodes); - ASSERT_EQUALS(0, options.wTimeout); + ASSERT_EQUALS(1, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(WriteConcernOptions::kNoTimeout, options.wTimeout); } TEST(WriteConcernOptionsTest, ParseWTimeoutAsNumber) { @@ -143,9 +136,8 @@ TEST(WriteConcernOptionsTest, ParseWTimeoutAsNumber) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::UNSET == options.syncMode); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(1, options.wNumNodes); - ASSERT_EQUALS(123, options.wTimeout); + ASSERT_EQUALS(1, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(Milliseconds{123}, options.wTimeout); } TEST(WriteConcernOptionsTest, ParseWTimeoutAsNaNDouble) { @@ -154,9 +146,8 @@ TEST(WriteConcernOptionsTest, ParseWTimeoutAsNaNDouble) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::UNSET == options.syncMode); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(1, options.wNumNodes); - ASSERT_EQUALS(0, options.wTimeout); + ASSERT_EQUALS(1, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(WriteConcernOptions::kNoTimeout, options.wTimeout); } TEST(WriteConcernOptionsTest, ParseWTimeoutAsDoubleLargerThanInt) { @@ -165,9 +156,8 @@ TEST(WriteConcernOptionsTest, ParseWTimeoutAsDoubleLargerThanInt) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::UNSET == options.syncMode); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(1, options.wNumNodes); - ASSERT_LESS_THAN(options.wTimeout, 0); + ASSERT_EQUALS(1, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(options.wTimeout, Milliseconds{2999999999}); } TEST(WriteConcernOptionsTest, ParseReturnsFailedToParseOnUnknownField) { @@ -180,9 +170,8 @@ void _testIgnoreWriteConcernField(const char* fieldName) { ASSERT_OK(sw.getStatus()); WriteConcernOptions options = sw.getValue(); ASSERT_TRUE(WriteConcernOptions::SyncMode::UNSET == options.syncMode); - ASSERT_EQUALS("", options.wMode); - ASSERT_EQUALS(1, options.wNumNodes); - ASSERT_EQUALS(0, options.wTimeout); + ASSERT_EQUALS(1, stdx::get<int64_t>(options.w)); + ASSERT_EQUALS(WriteConcernOptions::kNoTimeout, options.wTimeout); } TEST(WriteConcernOptionsTest, ParseIgnoresSpecialFields) { _testIgnoreWriteConcernField("wElectionId"); @@ -195,20 +184,18 @@ TEST(WriteConcernOptionsTest, ParseWithTags) { << "def"))) .getStatus(); ASSERT_EQUALS(ErrorCodes::FailedToParse, status); - ASSERT_EQUALS("tags must be a single level document with only number values", status.reason()); + ASSERT_STRING_CONTAINS(status.reason(), + "tags must be a single level document with only number values"); auto sw = WriteConcernOptions::parse(BSON("w" << BSON("abc" << 1))); ASSERT_OK(sw.getStatus()); WriteConcernOptions wc = sw.getValue(); - ASSERT_EQ(wc.wNumNodes, 0); - ASSERT_TRUE(wc.wMode.empty()); ASSERT_EQ(wc.wTimeout, WriteConcernOptions::kNoTimeout); ASSERT_TRUE(wc.needToWaitForOtherNodes()); - auto tags = wc.wTags(); - ASSERT(tags.has_value()); - ASSERT_BSONOBJ_EQ(*tags, BSON("abc" << 1)); + auto tags = stdx::get<WTags>(wc.w); + ASSERT(tags == (WTags{{"abc", 1}})); ASSERT_BSONOBJ_EQ(wc.toBSON(), BSON("w" << BSON("abc" << 1) << "wtimeout" << 0)); auto wc2 = uassertStatusOK(WriteConcernOptions::parse(BSON("w" << BSON("abc" << 1)))); |