diff options
author | Randolph Tan <randolph@10gen.com> | 2014-06-17 17:45:53 -0400 |
---|---|---|
committer | Randolph Tan <randolph@10gen.com> | 2014-07-15 13:33:25 -0400 |
commit | fa1233fbe4a48ef0675820f381987f1df4f42f75 (patch) | |
tree | 2f16f64be178a8aa417414dabb5e23f72c13e276 /src/mongo/db | |
parent | a78c439ba94e08ab6079e5575f1959c39c9beb87 (diff) | |
download | mongo-fa1233fbe4a48ef0675820f381987f1df4f42f75.tar.gz |
SERVER-14041 enhance secondaryThrottle parameter
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/commands/cleanup_orphaned_cmd.cpp | 59 | ||||
-rw-r--r-- | src/mongo/db/dbhelpers.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/dbhelpers.h | 5 | ||||
-rw-r--r-- | src/mongo/db/field_parser.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/range_deleter.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/range_deleter.h | 9 | ||||
-rw-r--r-- | src/mongo/db/range_deleter_db_env.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/range_deleter_test.cpp | 101 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_coordinator.h | 11 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_coordinator_hybrid.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_coordinator_hybrid.h | 3 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_coordinator_impl.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_coordinator_impl.h | 3 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_coordinator_legacy.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_coordinator_legacy.h | 3 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_coordinator_mock.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_coordinator_mock.h | 3 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.cpp | 103 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.h | 47 |
19 files changed, 359 insertions, 69 deletions
diff --git a/src/mongo/db/commands/cleanup_orphaned_cmd.cpp b/src/mongo/db/commands/cleanup_orphaned_cmd.cpp index 89e7f49ab4b..3fd6dbcb9d2 100644 --- a/src/mongo/db/commands/cleanup_orphaned_cmd.cpp +++ b/src/mongo/db/commands/cleanup_orphaned_cmd.cpp @@ -41,11 +41,22 @@ #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" #include "mongo/db/range_deleter_service.h" +#include "mongo/db/repl/repl_coordinator_global.h" #include "mongo/s/collection_metadata.h" #include "mongo/s/d_logic.h" #include "mongo/s/range_arithmetic.h" +#include "mongo/s/type_settings.h" #include "mongo/util/log.h" +namespace { + using mongo::WriteConcernOptions; + + const int kDefaultWTimeoutMs = 60 * 1000; + const WriteConcernOptions DefaultWriteConcern("majority", + WriteConcernOptions::NONE, + kDefaultWTimeoutMs); +} + namespace mongo { MONGO_LOG_DEFAULT_COMPONENT_FILE(::mongo::logger::LogComponent::kCommands); @@ -69,7 +80,7 @@ namespace mongo { CleanupResult cleanupOrphanedData( OperationContext* txn, const NamespaceString& ns, const BSONObj& startingFromKeyConst, - bool secondaryThrottle, + const WriteConcernOptions& secondaryThrottle, BSONObj* stoppedAtKey, string* errMsg ) { @@ -153,6 +164,17 @@ namespace mongo { * the balancer is off. * * Safe to call with the balancer on. + * + * Format: + * + * { + * cleanupOrphaned: <ns>, + * // optional parameters: + * startingAtKey: { <shardKeyValue> }, // defaults to lowest value + * secondaryThrottle: <bool>, // defaults to true + * // defaults to { w: "majority", wtimeout: 60000 }. Applies to individual writes. + * writeConcern: { <writeConcern options> } + * } */ class CleanupOrphanedCommand : public Command { public: @@ -179,7 +201,6 @@ namespace mongo { // Input static BSONField<string> nsField; static BSONField<BSONObj> startingFromKeyField; - static BSONField<bool> secondaryThrottleField; // Output static BSONField<BSONObj> stoppedAtKeyField; @@ -210,12 +231,29 @@ namespace mongo { return false; } - bool secondaryThrottle = true; - if ( !FieldParser::extract( cmdObj, - secondaryThrottleField, - &secondaryThrottle, - &errmsg ) ) { - return false; + WriteConcernOptions writeConcern; + Status status = writeConcern.parseSecondaryThrottle(cmdObj, NULL); + + if (!status.isOK()){ + if (status.code() != ErrorCodes::WriteConcernNotDefined) { + return appendCommandStatus(result, status); + } + + writeConcern = DefaultWriteConcern; + } + else { + repl::ReplicationCoordinator* replCoordinator = + repl::getGlobalReplicationCoordinator(); + Status status = replCoordinator->checkIfWriteConcernCanBeSatisfied(writeConcern); + if (!status.isOK()) { + return appendCommandStatus(result, status); + } + } + + if (writeConcern.shouldWaitForOtherNodes() && + writeConcern.wTimeout == WriteConcernOptions::kNoTimeout) { + // Don't allow no timeout. + writeConcern.wTimeout = kDefaultWTimeoutMs; } if (!shardingState.enabled()) { @@ -225,7 +263,7 @@ namespace mongo { } ChunkVersion shardVersion; - Status status = shardingState.refreshMetadataNow( ns, &shardVersion ); + status = shardingState.refreshMetadataNow( ns, &shardVersion ); if ( !status.isOK() ) { if ( status.code() == ErrorCodes::RemoteChangeDetected ) { warning() << "Shard version in transition detected while refreshing " @@ -242,7 +280,7 @@ namespace mongo { CleanupResult cleanupResult = cleanupOrphanedData( txn, NamespaceString( ns ), startingFromKey, - secondaryThrottle, + writeConcern, &stoppedAtKey, &errmsg ); @@ -263,7 +301,6 @@ namespace mongo { BSONField<string> CleanupOrphanedCommand::nsField( "cleanupOrphaned" ); BSONField<BSONObj> CleanupOrphanedCommand::startingFromKeyField( "startingFromKey" ); - BSONField<bool> CleanupOrphanedCommand::secondaryThrottleField( "secondaryThrottle" ); BSONField<BSONObj> CleanupOrphanedCommand::stoppedAtKeyField( "stoppedAtKey" ); MONGO_INITIALIZER(RegisterCleanupOrphanedCommand)(InitializerContext* context) { diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp index 83b5b0b7ec5..8cc0eb0bd1c 100644 --- a/src/mongo/db/dbhelpers.cpp +++ b/src/mongo/db/dbhelpers.cpp @@ -52,6 +52,7 @@ #include "mongo/db/repl/oplog.h" #include "mongo/db/repl/repl_coordinator_global.h" #include "mongo/db/write_concern.h" +#include "mongo/db/write_concern_options.h" #include "mongo/db/operation_context_impl.h" #include "mongo/db/storage_options.h" #include "mongo/db/catalog/collection.h" @@ -305,7 +306,7 @@ namespace mongo { long long Helpers::removeRange( OperationContext* txn, const KeyRange& range, bool maxInclusive, - bool secondaryThrottle, + const WriteConcernOptions& writeConcern, RemoveSaver* callback, bool fromMigrate, bool onlyRemoveOrphanedDocs ) @@ -342,7 +343,7 @@ namespace mongo { Helpers::toKeyFormat( indexKeyPattern.extendRangeBound(range.maxKey,maxInclusive)); LOG(1) << "begin removal of " << min << " to " << max << " in " << ns - << (secondaryThrottle ? " (waiting for secondaries)" : "" ) << endl; + << " with write concern: " << writeConcern.toBSON() << endl; Client& c = cc(); @@ -435,10 +436,7 @@ namespace mongo { // TODO remove once the yielding below that references this timer has been removed Timer secondaryThrottleTime; - if ( secondaryThrottle && numDeleted > 0 ) { - WriteConcernOptions writeConcern; - writeConcern.wNumNodes = 2; - writeConcern.wTimeout = 60 * 1000; + if (writeConcern.shouldWaitForOtherNodes() && numDeleted > 0) { repl::ReplicationCoordinator::StatusAndDuration replStatus = repl::getGlobalReplicationCoordinator()->awaitReplication(txn, c.getLastOp(), @@ -454,7 +452,7 @@ namespace mongo { } } - if ( secondaryThrottle ) + if (writeConcern.shouldWaitForOtherNodes()) log() << "Helpers::removeRangeUnlocked time spent waiting for replication: " << millisWaitingForReplication << "ms" << endl; diff --git a/src/mongo/db/dbhelpers.h b/src/mongo/db/dbhelpers.h index be0ca859248..865543c74f9 100644 --- a/src/mongo/db/dbhelpers.h +++ b/src/mongo/db/dbhelpers.h @@ -41,6 +41,7 @@ namespace mongo { class Collection; class Cursor; class OperationContext; + struct WriteConcernOptions; /** * db helpers are helper functions and classes that let us easily manipulate the local @@ -164,8 +165,8 @@ namespace mongo { */ static long long removeRange( OperationContext* txn, const KeyRange& range, - bool maxInclusive = false, - bool secondaryThrottle = false, + bool maxInclusive, + const WriteConcernOptions& secondaryThrottle, RemoveSaver* callback = NULL, bool fromMigrate = false, bool onlyRemoveOrphanedDocs = false ); diff --git a/src/mongo/db/field_parser.cpp b/src/mongo/db/field_parser.cpp index 40c8c9664eb..47b5b8cf1c0 100644 --- a/src/mongo/db/field_parser.cpp +++ b/src/mongo/db/field_parser.cpp @@ -111,7 +111,7 @@ namespace mongo { { if (elem.eoo()) { if (field.hasDefault()) { - *out = field.getDefault(); + *out = field.getDefault().getOwned(); return FIELD_DEFAULT; } else { diff --git a/src/mongo/db/range_deleter.cpp b/src/mongo/db/range_deleter.cpp index d096cab1219..e75ce96f0bd 100644 --- a/src/mongo/db/range_deleter.cpp +++ b/src/mongo/db/range_deleter.cpp @@ -189,7 +189,7 @@ namespace mongo { const BSONObj& min, const BSONObj& max, const BSONObj& shardKeyPattern, - bool secondaryThrottle, + const WriteConcernOptions& writeConcern, Notification* notifyDone, std::string* errMsg) { string dummy; @@ -199,7 +199,7 @@ namespace mongo { min.getOwned(), max.getOwned(), shardKeyPattern.getOwned(), - secondaryThrottle)); + writeConcern)); toDelete->notifyDone = notifyDone; { @@ -243,10 +243,13 @@ namespace mongo { } namespace { - bool _waitForReplication(OperationContext* txn, std::string* errMsg) { - WriteConcernOptions writeConcern; - writeConcern.wMode = "majority"; - writeConcern.wTimeout = 60 * 60 * 1000; + const int kWTimeoutMillis = 60 * 60 * 1000; + + bool _waitForMajority(OperationContext* txn, std::string* errMsg) { + const WriteConcernOptions writeConcern("majority", + WriteConcernOptions::NONE, + kWTimeoutMillis); + repl::ReplicationCoordinator::StatusAndDuration replStatus = repl::getGlobalReplicationCoordinator()->awaitReplicationOfLastOp(txn, writeConcern); @@ -279,7 +282,7 @@ namespace { const BSONObj& min, const BSONObj& max, const BSONObj& shardKeyPattern, - bool secondaryThrottle, + const WriteConcernOptions& writeConcern, string* errMsg) { if (stopRequested()) { *errMsg = "deleter is already stopped."; @@ -314,7 +317,7 @@ namespace { << " cursors in " << ns << " to finish" << endl; } - RangeDeleteEntry taskDetails(ns, min, max, shardKeyPattern, secondaryThrottle); + RangeDeleteEntry taskDetails(ns, min, max, shardKeyPattern, writeConcern); taskDetails.stats.queueStartTS = jsTime(); Date_t timeSinceLastLog; @@ -373,7 +376,7 @@ namespace { if (result) { taskDetails.stats.waitForReplStartTS = jsTime(); - result = _waitForReplication(txn, errMsg); + result = _waitForMajority(txn, errMsg); taskDetails.stats.waitForReplEndTS = jsTime(); } @@ -555,7 +558,7 @@ namespace { if (delResult) { nextTask->stats.waitForReplStartTS = jsTime(); - if (!_waitForReplication(txn.get(), &errMsg)) { + if (!_waitForMajority(txn.get(), &errMsg)) { warning() << "Error encountered while waiting for replication: " << errMsg; } @@ -662,12 +665,12 @@ namespace { const BSONObj& min, const BSONObj& max, const BSONObj& shardKey, - bool secondaryThrottle): + const WriteConcernOptions& writeConcern): ns(ns), min(min), max(max), shardKeyPattern(shardKey), - secondaryThrottle(secondaryThrottle), + writeConcern(writeConcern), notifyDone(NULL) { } diff --git a/src/mongo/db/range_deleter.h b/src/mongo/db/range_deleter.h index 022f3c84872..cda4578b02b 100644 --- a/src/mongo/db/range_deleter.h +++ b/src/mongo/db/range_deleter.h @@ -39,6 +39,7 @@ #include "mongo/db/clientcursor.h" #include "mongo/db/jsobj.h" #include "mongo/db/operation_context.h" +#include "mongo/db/write_concern_options.h" #include "mongo/util/concurrency/mutex.h" #include "mongo/util/concurrency/synchronization.h" #include "mongo/util/time_support.h" @@ -138,7 +139,7 @@ namespace mongo { const BSONObj& min, const BSONObj& max, const BSONObj& shardKeyPattern, - bool secondaryThrottle, + const WriteConcernOptions& writeConcern, Notification* notifyDone, std::string* errMsg); @@ -154,7 +155,7 @@ namespace mongo { const BSONObj& min, const BSONObj& max, const BSONObj& shardKeyPattern, - bool secondaryThrottle, + const WriteConcernOptions& writeConcern, std::string* errMsg); /** @@ -309,7 +310,7 @@ namespace mongo { const BSONObj& min, const BSONObj& max, const BSONObj& shardKey, - bool secondaryThrottle); + const WriteConcernOptions& writeConcern); const std::string ns; @@ -324,7 +325,7 @@ namespace mongo { // like hash indexes. const BSONObj shardKeyPattern; - const bool secondaryThrottle; + const WriteConcernOptions writeConcern; // Sets of cursors to wait to close until this can be ready // for deletion. diff --git a/src/mongo/db/range_deleter_db_env.cpp b/src/mongo/db/range_deleter_db_env.cpp index 3658f99cd2c..29e96e4ead4 100644 --- a/src/mongo/db/range_deleter_db_env.cpp +++ b/src/mongo/db/range_deleter_db_env.cpp @@ -63,7 +63,7 @@ namespace mongo { const BSONObj inclusiveLower(taskDetails.min); const BSONObj exclusiveUpper(taskDetails.max); const BSONObj keyPattern(taskDetails.shardKeyPattern); - const bool secondaryThrottle(taskDetails.secondaryThrottle); + const WriteConcernOptions writeConcern(taskDetails.writeConcern); const bool initiallyHaveClient = haveClient(); @@ -85,8 +85,6 @@ namespace mongo { << endl; try { - bool throttle = repl::getGlobalReplicationCoordinator()->getReplicationMode() == - repl::ReplicationCoordinator::modeReplSet ? secondaryThrottle : false; *deletedDocs = Helpers::removeRange(txn, KeyRange(ns, @@ -94,7 +92,7 @@ namespace mongo { exclusiveUpper, keyPattern), false, /*maxInclusive*/ - throttle, + writeConcern, serverGlobalParams.moveParanoia ? &removeSaver : NULL, true, /*fromMigrate*/ true); /*onlyRemoveOrphans*/ diff --git a/src/mongo/db/range_deleter_test.cpp b/src/mongo/db/range_deleter_test.cpp index 05ce3918320..c0b50aee619 100644 --- a/src/mongo/db/range_deleter_test.cpp +++ b/src/mongo/db/range_deleter_test.cpp @@ -35,6 +35,7 @@ #include "mongo/db/repl/repl_coordinator_global.h" #include "mongo/db/repl/repl_coordinator_mock.h" #include "mongo/db/repl/repl_settings.h" +#include "mongo/db/write_concern_options.h" #include "mongo/stdx/functional.h" #include "mongo/unittest/unittest.h" @@ -57,6 +58,7 @@ namespace { const int MAX_IMMEDIATE_DELETE_WAIT_SECS = 2; const mongo::repl::ReplSettings replSettings; + const mongo::WriteConcernOptions dummyWriteConcern; // Should not be able to queue deletes if deleter workers were not started. TEST(QueueDelete, CantAfterStop) { @@ -73,7 +75,7 @@ namespace { BSON("x" << 120), BSON("x" << 200), BSON("x" << 1), - true, + dummyWriteConcern, NULL /* notifier not needed */, &errMsg)); ASSERT_FALSE(errMsg.empty()); @@ -93,8 +95,13 @@ namespace { env->addCursorId(ns, 345); Notification notifyDone; - ASSERT_TRUE(deleter.queueDelete(ns, BSON("x" << 0), BSON("x" << 10), BSON("x" << 1), - true, ¬ifyDone, NULL /* errMsg not needed */)); + ASSERT_TRUE(deleter.queueDelete(ns, + BSON("x" << 0), + BSON("x" << 10), + BSON("x" << 1), + dummyWriteConcern, + ¬ifyDone, + NULL /* errMsg not needed */)); env->waitForNthGetCursor(1u); @@ -129,8 +136,13 @@ namespace { env->addCursorId(ns, 345); Notification notifyDone; - ASSERT_TRUE(deleter.queueDelete(ns, BSON("x" << 0), BSON("x" << 10), BSON("x" << 1), - true, ¬ifyDone, NULL /* errMsg not needed */)); + ASSERT_TRUE(deleter.queueDelete(ns, + BSON("x" << 0), + BSON("x" << 10), + BSON("x" << 1), + dummyWriteConcern, + ¬ifyDone, + NULL /* errMsg not needed */)); env->waitForNthGetCursor(1u); @@ -145,9 +157,9 @@ namespace { const BSONObj& min, const BSONObj& max, const BSONObj& shardKeyPattern, - bool secondaryThrottle, + const mongo::WriteConcernOptions& secondaryThrottle, std::string* errMsg) { - deleter->deleteNow(txn, ns, min, max, shardKeyPattern, secondaryThrottle, errMsg); + deleter->deleteNow(txn,ns, min, max, shardKeyPattern, secondaryThrottle, errMsg); } // Should not start delete if the set of cursors that were open when the @@ -171,7 +183,7 @@ namespace { BSON("x" << 0), BSON("x" << 10), BSON("x" << 1), - true, + dummyWriteConcern, &errMsg)); env->waitForNthGetCursor(1u); @@ -220,7 +232,7 @@ namespace { BSON("x" << 0), BSON("x" << 10), BSON("x" << 1), - true, + dummyWriteConcern, &errMsg)); env->waitForNthGetCursor(1u); @@ -262,7 +274,7 @@ namespace { BSON("x" << 10), BSON("x" << 20), BSON("x" << 1), - true, + dummyWriteConcern, ¬ifyDone1, NULL /* don't care errMsg */)); @@ -276,7 +288,7 @@ namespace { BSON("x" << 20), BSON("x" << 30), BSON("x" << 1), - true, + dummyWriteConcern, ¬ifyDone2, NULL /* don't care errMsg */)); @@ -285,7 +297,7 @@ namespace { BSON("x" << 30), BSON("x" << 40), BSON("x" << 1), - true, + dummyWriteConcern, ¬ifyDone3, NULL /* don't care errMsg */)); @@ -358,13 +370,23 @@ namespace { ASSERT_TRUE(errMsg.empty()); errMsg.clear(); - ASSERT_FALSE(deleter.queueDelete(ns, BSON("x" << 120), BSON("x" << 140), BSON("x" << 1), - false, NULL /* notifier not needed */, &errMsg)); + ASSERT_FALSE(deleter.queueDelete(ns, + BSON("x" << 120), + BSON("x" << 140), + BSON("x" << 1), + dummyWriteConcern, + NULL /* notifier not needed */, + &errMsg)); ASSERT_FALSE(errMsg.empty()); errMsg.clear(); - ASSERT_FALSE(deleter.deleteNow(noTxn, ns, BSON("x" << 120), BSON("x" << 140), - BSON("x" << 1), false, &errMsg)); + ASSERT_FALSE(deleter.deleteNow(noTxn, + ns, + BSON("x" << 120), + BSON("x" << 140), + BSON("x" << 1), + dummyWriteConcern, + &errMsg)); ASSERT_FALSE(errMsg.empty()); ASSERT_FALSE(env->deleteOccured()); @@ -410,8 +432,13 @@ namespace { env->addCursorId(ns, 58); Notification notifyDone; - deleter.queueDelete(ns, BSON("x" << 0), BSON("x" << 10), BSON("x" << 1), - false, ¬ifyDone, NULL /* errMsg not needed */); + deleter.queueDelete(ns, + BSON("x" << 0), + BSON("x" << 10), + BSON("x" << 1), + dummyWriteConcern, + ¬ifyDone, + NULL /* errMsg not needed */); string errMsg; ASSERT_FALSE(deleter.addToBlackList(ns, BSON("x" << 5), BSON("x" << 15), &errMsg)); @@ -448,7 +475,7 @@ namespace { BSON("x" << 64), BSON("x" << 70), BSON("x" << 1), - true, + dummyWriteConcern, &delErrMsg)); env->waitForNthPausedDelete(1u); @@ -484,8 +511,13 @@ namespace { ASSERT_FALSE(deleter.removeFromBlackList(ns, BSON("x" << 1234), BSON("x" << 9000))); // Range should still be blacklisted - ASSERT_FALSE(deleter.deleteNow(noTxn, ns, BSON("x" << 2000), BSON("x" << 4000), BSON("x" << 1), - false, NULL /* errMsg not needed */)); + ASSERT_FALSE(deleter.deleteNow(noTxn, + ns, + BSON("x" << 2000), + BSON("x" << 4000), + BSON("x" << 1), + dummyWriteConcern, + NULL /* errMsg not needed */)); deleter.stopWorkers(); } @@ -503,15 +535,25 @@ namespace { ASSERT_TRUE(errMsg.empty()); errMsg.clear(); - ASSERT_FALSE(deleter.deleteNow(noTxn, ns, BSON("x" << 600), BSON("x" << 700), - BSON("x" << 1), false, &errMsg)); + ASSERT_FALSE(deleter.deleteNow(noTxn, + ns, + BSON("x" << 600), + BSON("x" << 700), + BSON("x" << 1), + dummyWriteConcern, + &errMsg)); ASSERT_FALSE(errMsg.empty()); ASSERT_TRUE(deleter.removeFromBlackList(ns, BSON("x" << 500), BSON("x" << 801))); errMsg.clear(); - ASSERT_TRUE(deleter.deleteNow(noTxn, ns, BSON("x" << 600), BSON("x" << 700), - BSON("x" << 1), false, &errMsg)); + ASSERT_TRUE(deleter.deleteNow(noTxn, + ns, + BSON("x" << 600), + BSON("x" << 700), + BSON("x" << 1), + dummyWriteConcern, + &errMsg)); ASSERT_TRUE(errMsg.empty()); deleter.stopWorkers(); @@ -527,8 +569,13 @@ namespace { deleter.addToBlackList("foo.bar", BSON("x" << 100), BSON("x" << 200), NULL /* errMsg not needed */); - ASSERT_TRUE(deleter.deleteNow(noTxn, "test.user", BSON("x" << 120), BSON("x" << 140), - BSON("x" << 1), true, NULL /* errMsg not needed */)); + ASSERT_TRUE(deleter.deleteNow(noTxn, + "test.user", + BSON("x" << 120), + BSON("x" << 140), + BSON("x" << 1), + dummyWriteConcern, + NULL /* errMsg not needed */)); deleter.stopWorkers(); } diff --git a/src/mongo/db/repl/repl_coordinator.h b/src/mongo/db/repl/repl_coordinator.h index cf9b20630e6..000476fd4e8 100644 --- a/src/mongo/db/repl/repl_coordinator.h +++ b/src/mongo/db/repl/repl_coordinator.h @@ -201,6 +201,17 @@ namespace repl { virtual bool canAcceptWritesForDatabase(const StringData& dbName) = 0; /** + * Checks if the current replica set configuration can satisfy the given write concern. + * + * Things that are taken into consideration include: + * 1. If the set has enough members. + * 2. If the tag exists. + * 3. If there are enough members for the tag specified. + */ + virtual Status checkIfWriteConcernCanBeSatisfied( + const WriteConcernOptions& writeConcern) const = 0; + + /* * Returns Status::OK() if it is valid for this node to serve reads on the given collection * and an errorcode indicating why the node cannot if it cannot. */ diff --git a/src/mongo/db/repl/repl_coordinator_hybrid.cpp b/src/mongo/db/repl/repl_coordinator_hybrid.cpp index c065008e033..7ca34597f20 100644 --- a/src/mongo/db/repl/repl_coordinator_hybrid.cpp +++ b/src/mongo/db/repl/repl_coordinator_hybrid.cpp @@ -275,5 +275,12 @@ namespace repl { return legacyResponse; } + Status HybridReplicationCoordinator::checkIfWriteConcernCanBeSatisfied( + const WriteConcernOptions& writeConcern) const { + Status legacyStatus = _legacy.checkIfWriteConcernCanBeSatisfied(writeConcern); + Status implStatus = _impl.checkIfWriteConcernCanBeSatisfied(writeConcern); + return legacyStatus; + } + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/repl_coordinator_hybrid.h b/src/mongo/db/repl/repl_coordinator_hybrid.h index 8a339e556d5..bf375e0a0ee 100644 --- a/src/mongo/db/repl/repl_coordinator_hybrid.h +++ b/src/mongo/db/repl/repl_coordinator_hybrid.h @@ -83,6 +83,9 @@ namespace repl { virtual bool canAcceptWritesForDatabase(const StringData& dbName); + virtual Status checkIfWriteConcernCanBeSatisfied( + const WriteConcernOptions& writeConcern) const; + virtual Status canServeReadsFor(const NamespaceString& ns, bool slaveOk); virtual bool shouldIgnoreUniqueIndex(const IndexDescriptor* idx); diff --git a/src/mongo/db/repl/repl_coordinator_impl.cpp b/src/mongo/db/repl/repl_coordinator_impl.cpp index 9949b10a673..5da1a1deb39 100644 --- a/src/mongo/db/repl/repl_coordinator_impl.cpp +++ b/src/mongo/db/repl/repl_coordinator_impl.cpp @@ -446,5 +446,12 @@ namespace repl { // TODO return std::vector<BSONObj>(); } + + Status ReplicationCoordinatorImpl::checkIfWriteConcernCanBeSatisfied( + const WriteConcernOptions& writeConcern) const { + // TODO + return Status::OK(); + } + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/repl_coordinator_impl.h b/src/mongo/db/repl/repl_coordinator_impl.h index 095c3897a70..62e043f9d0c 100644 --- a/src/mongo/db/repl/repl_coordinator_impl.h +++ b/src/mongo/db/repl/repl_coordinator_impl.h @@ -89,6 +89,9 @@ namespace repl { virtual bool canAcceptWritesForDatabase(const StringData& database); + virtual Status checkIfWriteConcernCanBeSatisfied( + const WriteConcernOptions& writeConcern) const; + virtual Status canServeReadsFor(const NamespaceString& ns, bool slaveOk); virtual bool shouldIgnoreUniqueIndex(const IndexDescriptor* idx); diff --git a/src/mongo/db/repl/repl_coordinator_legacy.cpp b/src/mongo/db/repl/repl_coordinator_legacy.cpp index f4be0d26369..17c30dfb9e0 100644 --- a/src/mongo/db/repl/repl_coordinator_legacy.cpp +++ b/src/mongo/db/repl/repl_coordinator_legacy.cpp @@ -944,5 +944,19 @@ namespace { return repl::getHostsWrittenTo(op); } + Status LegacyReplicationCoordinator::checkIfWriteConcernCanBeSatisfied( + const WriteConcernOptions& writeConcern) const { + // TODO: rewrite this method with the correct version. Note that this just a + // temporary stub for secondary throttle. + + if (getReplicationMode() == ReplicationCoordinator::modeReplSet) { + if (writeConcern.wNumNodes > 1 && theReplSet->config().getMajority() <= 1) { + return Status(ErrorCodes::CannotSatisfyWriteConcern, "not enough nodes"); + } + } + + return Status::OK(); + } + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/repl_coordinator_legacy.h b/src/mongo/db/repl/repl_coordinator_legacy.h index a9095f80a09..f9cf15be96c 100644 --- a/src/mongo/db/repl/repl_coordinator_legacy.h +++ b/src/mongo/db/repl/repl_coordinator_legacy.h @@ -79,6 +79,9 @@ namespace repl { virtual bool canAcceptWritesForDatabase(const StringData& dbName); + virtual Status checkIfWriteConcernCanBeSatisfied( + const WriteConcernOptions& writeConcern) const; + virtual Status canServeReadsFor(const NamespaceString& ns, bool slaveOk); virtual bool shouldIgnoreUniqueIndex(const IndexDescriptor* idx); diff --git a/src/mongo/db/repl/repl_coordinator_mock.cpp b/src/mongo/db/repl/repl_coordinator_mock.cpp index b7b712bd0d4..aab04be4938 100644 --- a/src/mongo/db/repl/repl_coordinator_mock.cpp +++ b/src/mongo/db/repl/repl_coordinator_mock.cpp @@ -223,5 +223,11 @@ namespace repl { // TODO return std::vector<BSONObj>(); } + + Status ReplicationCoordinatorMock::checkIfWriteConcernCanBeSatisfied( + const WriteConcernOptions& writeConcern) const { + return Status::OK(); + } + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/repl_coordinator_mock.h b/src/mongo/db/repl/repl_coordinator_mock.h index 260ed685705..1ac42d4a248 100644 --- a/src/mongo/db/repl/repl_coordinator_mock.h +++ b/src/mongo/db/repl/repl_coordinator_mock.h @@ -82,6 +82,9 @@ namespace repl { virtual bool canAcceptWritesForDatabase(const StringData& dbName); + virtual Status checkIfWriteConcernCanBeSatisfied( + const WriteConcernOptions& writeConcern) const; + virtual Status canServeReadsFor(const NamespaceString& ns, bool slaveOk); virtual bool shouldIgnoreUniqueIndex(const IndexDescriptor* idx); diff --git a/src/mongo/db/write_concern_options.cpp b/src/mongo/db/write_concern_options.cpp index 848c4e4464c..90690b87599 100644 --- a/src/mongo/db/write_concern_options.cpp +++ b/src/mongo/db/write_concern_options.cpp @@ -27,7 +27,9 @@ #include "mongo/db/write_concern_options.h" +#include "mongo/bson/bson_field.h" #include "mongo/client/dbclientinterface.h" +#include "mongo/db/field_parser.h" namespace mongo { @@ -36,6 +38,27 @@ namespace mongo { const BSONObj WriteConcernOptions::AllConfigs = BSONObj(); const BSONObj WriteConcernOptions::Unacknowledged(BSON("w" << W_NONE)); + static const BSONField<bool> mongosSecondaryThrottleField("_secondaryThrottle", true); + static const BSONField<bool> secondaryThrottleField("secondaryThrottle", true); + static const BSONField<BSONObj> writeConcernField("writeConcern"); + + WriteConcernOptions::WriteConcernOptions(int numNodes, + SyncMode sync, + int timeout): + syncMode(sync), + wNumNodes(numNodes), + wTimeout(timeout) { + } + + WriteConcernOptions::WriteConcernOptions(const std::string& mode, + SyncMode sync, + int timeout): + syncMode(sync), + wNumNodes(0), + wMode(mode), + wTimeout(timeout) { + } + Status WriteConcernOptions::parse( const BSONObj& obj ) { if ( obj.isEmpty() ) { return Status( ErrorCodes::FailedToParse, "write concern object cannot be empty" ); @@ -86,4 +109,84 @@ namespace mongo { return Status::OK(); } + + Status WriteConcernOptions::parseSecondaryThrottle(const BSONObj& doc, + BSONObj* rawWriteConcernObj) { + string errMsg; + bool isSecondaryThrottle; + FieldParser::FieldState fieldState = FieldParser::extract(doc, + secondaryThrottleField, + &isSecondaryThrottle, + &errMsg); + if (fieldState == FieldParser::FIELD_INVALID) { + return Status(ErrorCodes::FailedToParse, errMsg); + } + + if (fieldState != FieldParser::FIELD_SET) { + fieldState = FieldParser::extract(doc, + mongosSecondaryThrottleField, + &isSecondaryThrottle, + &errMsg); + + if (fieldState == FieldParser::FIELD_INVALID) { + return Status(ErrorCodes::FailedToParse, errMsg); + } + } + + BSONObj dummyBSON; + if (!rawWriteConcernObj) { + rawWriteConcernObj = &dummyBSON; + } + + fieldState = FieldParser::extract(doc, + writeConcernField, + rawWriteConcernObj, + &errMsg); + if (fieldState == FieldParser::FIELD_INVALID) { + return Status(ErrorCodes::FailedToParse, errMsg); + } + + if (!isSecondaryThrottle) { + if (!rawWriteConcernObj->isEmpty()) { + return Status(ErrorCodes::UnsupportedFormat, + "Cannot have write concern when secondary throttle is false"); + } + + wNumNodes = 1; + return Status::OK(); + } + + if (rawWriteConcernObj->isEmpty()) { + return Status(ErrorCodes::WriteConcernNotDefined, + "Secondary throttle is on, but write concern is not specified"); + } + + return parse(*rawWriteConcernObj); + } + + BSONObj WriteConcernOptions::toBSON() const { + BSONObjBuilder builder; + + if (wMode.empty()) { + builder.append("w", wNumNodes); + } + else { + builder.append("w", wMode); + } + + if (syncMode == FSYNC) { + builder.append("fsync", true); + } + else if (syncMode == JOURNAL) { + builder.append("j", true); + } + + builder.append("wtimeout", wTimeout); + + return builder.obj(); + } + + bool WriteConcernOptions::shouldWaitForOtherNodes() const { + return !wMode.empty() || wNumNodes > 1; + } } diff --git a/src/mongo/db/write_concern_options.h b/src/mongo/db/write_concern_options.h index 44c67b2938b..31b0f556c30 100644 --- a/src/mongo/db/write_concern_options.h +++ b/src/mongo/db/write_concern_options.h @@ -37,8 +37,11 @@ namespace mongo { struct WriteConcernOptions { public: + enum SyncMode { NONE, FSYNC, JOURNAL }; + static const int kNoTimeout = 0; static const int kNoWaiting = -1; + static const BSONObj Default; static const BSONObj Acknowledged; static const BSONObj AllConfigs; @@ -46,8 +49,43 @@ namespace mongo { WriteConcernOptions() { reset(); } + WriteConcernOptions(int numNodes, + SyncMode sync, + int timeout); + + WriteConcernOptions(const std::string& mode, + SyncMode sync, + int timeout); + Status parse( const BSONObj& obj ); + /** + * Extracts the write concern settings from the BSONObj. The BSON object should have + * the format: + * + * { + * ... + * secondaryThrottle: <bool>, // optional + * _secondaryThrottle: <bool>, // optional + * writeConcern: <BSONObj> // optional + * } + * + * Note: secondaryThrottle takes precedence over _secondaryThrottle. + * + * Also sets output parameter rawWriteConcernObj if the writeCocnern field exists. + * + * Returns OK if the parse was successful. Also returns ErrorCodes::WriteConcernNotDefined + * when secondary throttle is true but write concern was not specified. + */ + Status parseSecondaryThrottle(const BSONObj& doc, + BSONObj* rawWriteConcernObj); + + /** + * Return true if the server needs to wait for other secondary nodes to satisfy this + * write concern setting. Errs on the false positive for non-empty wMode. + */ + bool shouldWaitForOtherNodes() const; + void reset() { syncMode = NONE; wNumNodes = 0; @@ -55,11 +93,18 @@ namespace mongo { wTimeout = 0; } - enum SyncMode { NONE, FSYNC, JOURNAL } syncMode; + // Returns the BSON representation of this object. + // Warning: does not return the same object passed on the last parse() call. + BSONObj toBSON() const; + + SyncMode syncMode; + // 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; + // Timeout in milliseconds. int wTimeout; }; |