summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2014-06-17 17:45:53 -0400
committerRandolph Tan <randolph@10gen.com>2014-07-15 13:33:25 -0400
commitfa1233fbe4a48ef0675820f381987f1df4f42f75 (patch)
tree2f16f64be178a8aa417414dabb5e23f72c13e276 /src/mongo/db
parenta78c439ba94e08ab6079e5575f1959c39c9beb87 (diff)
downloadmongo-fa1233fbe4a48ef0675820f381987f1df4f42f75.tar.gz
SERVER-14041 enhance secondaryThrottle parameter
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/commands/cleanup_orphaned_cmd.cpp59
-rw-r--r--src/mongo/db/dbhelpers.cpp12
-rw-r--r--src/mongo/db/dbhelpers.h5
-rw-r--r--src/mongo/db/field_parser.cpp2
-rw-r--r--src/mongo/db/range_deleter.cpp27
-rw-r--r--src/mongo/db/range_deleter.h9
-rw-r--r--src/mongo/db/range_deleter_db_env.cpp6
-rw-r--r--src/mongo/db/range_deleter_test.cpp101
-rw-r--r--src/mongo/db/repl/repl_coordinator.h11
-rw-r--r--src/mongo/db/repl/repl_coordinator_hybrid.cpp7
-rw-r--r--src/mongo/db/repl/repl_coordinator_hybrid.h3
-rw-r--r--src/mongo/db/repl/repl_coordinator_impl.cpp7
-rw-r--r--src/mongo/db/repl/repl_coordinator_impl.h3
-rw-r--r--src/mongo/db/repl/repl_coordinator_legacy.cpp14
-rw-r--r--src/mongo/db/repl/repl_coordinator_legacy.h3
-rw-r--r--src/mongo/db/repl/repl_coordinator_mock.cpp6
-rw-r--r--src/mongo/db/repl/repl_coordinator_mock.h3
-rw-r--r--src/mongo/db/write_concern_options.cpp103
-rw-r--r--src/mongo/db/write_concern_options.h47
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, &notifyDone, NULL /* errMsg not needed */));
+ ASSERT_TRUE(deleter.queueDelete(ns,
+ BSON("x" << 0),
+ BSON("x" << 10),
+ BSON("x" << 1),
+ dummyWriteConcern,
+ &notifyDone,
+ 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, &notifyDone, NULL /* errMsg not needed */));
+ ASSERT_TRUE(deleter.queueDelete(ns,
+ BSON("x" << 0),
+ BSON("x" << 10),
+ BSON("x" << 1),
+ dummyWriteConcern,
+ &notifyDone,
+ 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,
&notifyDone1,
NULL /* don't care errMsg */));
@@ -276,7 +288,7 @@ namespace {
BSON("x" << 20),
BSON("x" << 30),
BSON("x" << 1),
- true,
+ dummyWriteConcern,
&notifyDone2,
NULL /* don't care errMsg */));
@@ -285,7 +297,7 @@ namespace {
BSON("x" << 30),
BSON("x" << 40),
BSON("x" << 1),
- true,
+ dummyWriteConcern,
&notifyDone3,
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, &notifyDone, NULL /* errMsg not needed */);
+ deleter.queueDelete(ns,
+ BSON("x" << 0),
+ BSON("x" << 10),
+ BSON("x" << 1),
+ dummyWriteConcern,
+ &notifyDone,
+ 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;
};