From 7ed530a55304bd9cc2b80c054f379b10d9cea8e2 Mon Sep 17 00:00:00 2001 From: Judah Schvimer Date: Tue, 12 Apr 2016 19:11:23 -0400 Subject: SERVER-20224 commands that write support writeConcern --- src/mongo/client/SConscript | 1 + src/mongo/client/dbclientinterface.h | 20 ++- src/mongo/db/SConscript | 12 +- src/mongo/db/auth/sasl_commands.cpp | 6 + src/mongo/db/clientlistplugin.cpp | 4 + src/mongo/db/commands.cpp | 13 +- src/mongo/db/commands.h | 22 ++- src/mongo/db/commands/apply_ops_cmd.cpp | 21 +-- src/mongo/db/commands/authentication_commands.cpp | 6 + src/mongo/db/commands/authentication_commands.h | 3 + src/mongo/db/commands/clone.cpp | 4 + src/mongo/db/commands/clone_collection.cpp | 4 + src/mongo/db/commands/collection_to_capped.cpp | 6 + src/mongo/db/commands/compact.cpp | 3 + src/mongo/db/commands/conn_pool_stats.cpp | 4 + src/mongo/db/commands/conn_pool_sync.cpp | 3 + src/mongo/db/commands/connection_status.cpp | 3 + src/mongo/db/commands/copydb.cpp | 3 + src/mongo/db/commands/copydb_start_commands.cpp | 8 + src/mongo/db/commands/count_cmd.cpp | 3 + src/mongo/db/commands/cpuprofile.cpp | 3 + src/mongo/db/commands/create_indexes.cpp | 3 + src/mongo/db/commands/current_op.cpp | 4 + src/mongo/db/commands/dbhash.cpp | 4 + src/mongo/db/commands/distinct.cpp | 3 + src/mongo/db/commands/drop_indexes.cpp | 6 + src/mongo/db/commands/explain_cmd.cpp | 4 + src/mongo/db/commands/fail_point_cmd.cpp | 4 + src/mongo/db/commands/find_and_modify.cpp | 24 +-- src/mongo/db/commands/find_cmd.cpp | 4 + src/mongo/db/commands/fsync.cpp | 7 + src/mongo/db/commands/generic.cpp | 28 +++ src/mongo/db/commands/geo_near_cmd.cpp | 3 + src/mongo/db/commands/get_last_error.cpp | 9 + src/mongo/db/commands/getmore_cmd.cpp | 4 + src/mongo/db/commands/group_cmd.cpp | 4 + src/mongo/db/commands/hashcmd.cpp | 3 + src/mongo/db/commands/index_filter_commands.cpp | 4 + src/mongo/db/commands/index_filter_commands.h | 2 + src/mongo/db/commands/isself.cpp | 3 + src/mongo/db/commands/kill_op.cpp | 4 + src/mongo/db/commands/killcursors_common.h | 4 + src/mongo/db/commands/list_collections.cpp | 3 + src/mongo/db/commands/list_databases.cpp | 3 + src/mongo/db/commands/list_indexes.cpp | 3 + src/mongo/db/commands/mr.cpp | 23 ++- src/mongo/db/commands/mr.h | 6 + src/mongo/db/commands/mr_common.cpp | 10 ++ src/mongo/db/commands/oplog_note.cpp | 3 + src/mongo/db/commands/parallel_collection_scan.cpp | 3 + src/mongo/db/commands/parameters.cpp | 6 + src/mongo/db/commands/pipeline_command.cpp | 9 + src/mongo/db/commands/plan_cache_commands.cpp | 4 + src/mongo/db/commands/plan_cache_commands.h | 2 + src/mongo/db/commands/rename_collection_cmd.cpp | 3 + src/mongo/db/commands/repair_cursor.cpp | 3 + src/mongo/db/commands/server_status.cpp | 3 + src/mongo/db/commands/shutdown.h | 3 + src/mongo/db/commands/snapshot_management.cpp | 6 + src/mongo/db/commands/test_commands.cpp | 13 ++ src/mongo/db/commands/top_command.cpp | 3 + src/mongo/db/commands/touch.cpp | 3 + src/mongo/db/commands/user_management_commands.cpp | 190 ++++++++++++++++----- src/mongo/db/commands/validate.cpp | 3 + .../db/commands/write_commands/batch_executor.cpp | 53 ++---- .../db/commands/write_commands/write_commands.cpp | 11 +- .../db/commands/write_commands/write_commands.h | 2 + src/mongo/db/dbcommands.cpp | 79 +++++++++ src/mongo/db/dbeval.cpp | 3 + src/mongo/db/driverHelpers.cpp | 3 + src/mongo/db/exec/stagedebug_cmd.cpp | 3 + src/mongo/db/geo/haystack.cpp | 3 + src/mongo/db/namespace_string.h | 3 + src/mongo/db/ops/update.cpp | 5 + src/mongo/db/pipeline/pipeline.cpp | 20 +++ src/mongo/db/pipeline/pipeline.h | 5 + src/mongo/db/repl/master_slave.cpp | 3 + src/mongo/db/repl/repl_set_command.h | 3 + src/mongo/db/repl/replication_info.cpp | 3 + src/mongo/db/repl/resync.cpp | 3 + src/mongo/db/s/check_sharding_index_command.cpp | 4 + src/mongo/db/s/cleanup_orphaned_cmd.cpp | 3 + src/mongo/db/s/get_shard_version_command.cpp | 4 + src/mongo/db/s/merge_chunks_command.cpp | 3 + ...gration_chunk_cloner_source_legacy_commands.cpp | 8 + ...gration_destination_manager_legacy_commands.cpp | 17 ++ src/mongo/db/s/move_chunk_command.cpp | 4 + src/mongo/db/s/set_shard_version_command.cpp | 4 + src/mongo/db/s/sharding_state_command.cpp | 4 + src/mongo/db/s/split_chunk_command.cpp | 4 + src/mongo/db/s/split_vector_command.cpp | 3 + src/mongo/db/s/unset_sharding_command.cpp | 4 + .../storage/mmap_v1/journal_latency_test_cmd.cpp | 3 + src/mongo/db/write_concern.cpp | 48 +++--- src/mongo/db/write_concern.h | 3 +- src/mongo/db/write_concern_options.cpp | 34 ++++ src/mongo/db/write_concern_options.h | 12 ++ src/mongo/rpc/SConscript | 3 +- src/mongo/s/client/shard_connection.cpp | 3 + src/mongo/s/commands/cluster_add_shard_cmd.cpp | 4 + src/mongo/s/commands/cluster_count_cmd.cpp | 4 + src/mongo/s/commands/cluster_current_op.cpp | 4 + src/mongo/s/commands/cluster_db_stats_cmd.cpp | 4 + src/mongo/s/commands/cluster_drop_database_cmd.cpp | 4 + .../s/commands/cluster_enable_sharding_cmd.cpp | 4 + src/mongo/s/commands/cluster_explain_cmd.cpp | 4 + .../s/commands/cluster_find_and_modify_cmd.cpp | 4 + src/mongo/s/commands/cluster_find_cmd.cpp | 4 + .../s/commands/cluster_flush_router_config_cmd.cpp | 4 + src/mongo/s/commands/cluster_fsync_cmd.cpp | 4 + .../s/commands/cluster_get_last_error_cmd.cpp | 4 + .../s/commands/cluster_get_prev_error_cmd.cpp | 4 + src/mongo/s/commands/cluster_get_shard_map_cmd.cpp | 4 + .../s/commands/cluster_get_shard_version_cmd.cpp | 4 + src/mongo/s/commands/cluster_getmore_cmd.cpp | 4 + src/mongo/s/commands/cluster_index_filter_cmd.cpp | 4 + src/mongo/s/commands/cluster_is_db_grid_cmd.cpp | 4 + src/mongo/s/commands/cluster_is_master_cmd.cpp | 4 + src/mongo/s/commands/cluster_kill_op.cpp | 4 + .../s/commands/cluster_list_databases_cmd.cpp | 4 + src/mongo/s/commands/cluster_list_shards_cmd.cpp | 4 + src/mongo/s/commands/cluster_map_reduce_cmd.cpp | 4 + src/mongo/s/commands/cluster_merge_chunks_cmd.cpp | 3 + src/mongo/s/commands/cluster_move_chunk_cmd.cpp | 4 + src/mongo/s/commands/cluster_move_primary_cmd.cpp | 4 + src/mongo/s/commands/cluster_netstat_cmd.cpp | 4 + src/mongo/s/commands/cluster_pipeline_cmd.cpp | 4 + src/mongo/s/commands/cluster_plan_cache_cmd.cpp | 4 + src/mongo/s/commands/cluster_profile_cmd.cpp | 4 + src/mongo/s/commands/cluster_remove_shard_cmd.cpp | 4 + .../s/commands/cluster_repair_database_cmd.cpp | 4 + .../s/commands/cluster_repl_set_get_status_cmd.cpp | 4 + src/mongo/s/commands/cluster_reset_error_cmd.cpp | 4 + .../s/commands/cluster_shard_collection_cmd.cpp | 4 + src/mongo/s/commands/cluster_split_cmd.cpp | 4 + .../commands/cluster_user_management_commands.cpp | 76 +++++++++ src/mongo/s/commands/cluster_whats_my_uri_cmd.cpp | 4 + src/mongo/s/commands/cluster_write_cmd.cpp | 4 + src/mongo/s/commands/commands_public.cpp | 82 ++++++++- src/mongo/s/config.cpp | 4 +- src/mongo/tools/SConscript | 1 + 141 files changed, 1102 insertions(+), 170 deletions(-) (limited to 'src') diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript index 99f9003d419..47846230a70 100644 --- a/src/mongo/client/SConscript +++ b/src/mongo/client/SConscript @@ -206,6 +206,7 @@ env.CppUnitTest( 'replica_set_monitor_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/write_concern_options', 'clientdriver' ] ) diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h index a4e23c3f7e5..942e5caf5fb 100644 --- a/src/mongo/client/dbclientinterface.h +++ b/src/mongo/client/dbclientinterface.h @@ -34,6 +34,7 @@ #include "mongo/client/connection_string.h" #include "mongo/client/read_preference.h" #include "mongo/db/jsobj.h" +#include "mongo/db/write_concern_options.h" #include "mongo/platform/atomic_word.h" #include "mongo/rpc/protocol.h" #include "mongo/rpc/metadata.h" @@ -709,17 +710,20 @@ public: * @param info An optional output parameter that receives the result object the database * returns from the drop command. May be null if the caller doesn't need that info. */ - virtual bool dropCollection(const std::string& ns, BSONObj* info = NULL) { + virtual bool dropCollection(const std::string& ns, + const WriteConcernOptions& writeConcern = WriteConcernOptions(), + BSONObj* info = nullptr) { std::string db = nsGetDB(ns); std::string coll = nsGetCollection(ns); uassert(10011, "no collection name", coll.size()); BSONObj temp; - if (info == NULL) { + if (info == nullptr) { info = &temp; } - bool res = runCommand(db.c_str(), BSON("drop" << coll), *info); + bool res = runCommand( + db.c_str(), BSON("drop" << coll << "writeConcern" << writeConcern.toBSON()), *info); return res; } @@ -862,8 +866,14 @@ public: static std::string genIndexName(const BSONObj& keys); /** Erase / drop an entire database */ - virtual bool dropDatabase(const std::string& dbname, BSONObj* info = 0) { - return simpleCommand(dbname, info, "dropDatabase"); + virtual bool dropDatabase(const std::string& dbname, + const WriteConcernOptions& writeConcern = WriteConcernOptions(), + BSONObj* info = nullptr) { + BSONObj o; + if (info == nullptr) + info = &o; + return runCommand( + dbname, BSON("dropDatabase" << 1 << "writeConcern" << writeConcern.toBSON()), *info); } virtual std::string toString() const = 0; diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index e530792b0de..2f7a108d72f 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -40,11 +40,11 @@ env.Library( 'field_ref_set.cpp', 'field_parser.cpp', 'keypattern.cpp', - 'write_concern_options.cpp' ], LIBDEPS=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/index_names', + '$BUILD_DIR/mongo/db/write_concern_options', '$BUILD_DIR/mongo/util/foundation', ] ) @@ -442,6 +442,16 @@ env.Library( ], ) +env.Library( + target="write_concern_options", + source=[ + "write_concern_options.cpp", + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/bson/util/bson_extract', + ], +) + env.Library( target='service_context', source=[ diff --git a/src/mongo/db/auth/sasl_commands.cpp b/src/mongo/db/auth/sasl_commands.cpp index fe049a988d0..f006cded7d0 100644 --- a/src/mongo/db/auth/sasl_commands.cpp +++ b/src/mongo/db/auth/sasl_commands.cpp @@ -81,6 +81,9 @@ public: BSONObjBuilder& result); virtual void help(stringstream& help) const; + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } @@ -106,6 +109,9 @@ public: BSONObjBuilder& result); virtual void help(stringstream& help) const; + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } diff --git a/src/mongo/db/clientlistplugin.cpp b/src/mongo/db/clientlistplugin.cpp index d3636f39099..ccb799571c5 100644 --- a/src/mongo/db/clientlistplugin.cpp +++ b/src/mongo/db/clientlistplugin.cpp @@ -141,6 +141,10 @@ public: CurrentOpContexts() : Command("currentOpCtx") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return true; } diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index dff5e6f91a1..d538800a63a 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -185,11 +185,16 @@ void Command::appendCommandStatus(BSONObjBuilder& result, bool ok, const std::st } } -void Command::appendCommandWCStatus(BSONObjBuilder& result, const Status& status) { - if (!status.isOK()) { +void Command::appendCommandWCStatus(BSONObjBuilder& result, + const Status& awaitReplicationStatus, + const WriteConcernResult& wcResult) { + if (!awaitReplicationStatus.isOK() && !result.hasField("writeConcernError")) { WCErrorDetail wcError; - wcError.setErrCode(status.code()); - wcError.setErrMessage(status.reason()); + wcError.setErrCode(awaitReplicationStatus.code()); + wcError.setErrMessage(awaitReplicationStatus.reason()); + if (wcResult.wTimedOut) { + wcError.setErrInfo(BSON("wtimeout" << true)); + } result.append("writeConcernError", wcError.toBSON()); } } diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index e8bd2591da1..526ba1f89d9 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -40,6 +40,7 @@ #include "mongo/db/commands/server_status_metric.h" #include "mongo/db/jsobj.h" #include "mongo/db/query/explain.h" +#include "mongo/db/write_concern.h" #include "mongo/rpc/reply_builder_interface.h" #include "mongo/rpc/request_interface.h" #include "mongo/util/string_map.h" @@ -141,6 +142,16 @@ public: const rpc::RequestInterface& request, rpc::ReplyBuilderInterface* replyBuilder); + /** + * supportsWriteConcern returns true if this command should be parsed for a writeConcern + * field and wait for that write concern to be satisfied after the command runs. + * + * @param cmd is a BSONObj representation of the command that is used to determine if the + * the command supports a write concern. Ex. aggregate only supports write concern + * when $out is provided. + */ + virtual bool supportsWriteConcern(const BSONObj& cmd) const = 0; + /* Return true if only the admin ns has privileges to run this command. */ virtual bool adminOnly() const { return false; @@ -335,8 +346,17 @@ public: /** * Helper for setting a writeConcernError field in the command result object if * a writeConcern error occurs. + * + * @param result is the BSONObjBuilder for the command response. This function creates the + * writeConcernError field for the response. + * @param awaitReplicationStatus is the status received from awaitReplication. + * @param wcResult is the writeConcernResult object that holds other write concern information. + * This is primarily used for populating errInfo when a timeout occurs, and is populated + * by waitForWriteConcern. */ - static void appendCommandWCStatus(BSONObjBuilder& result, const Status& status); + static void appendCommandWCStatus(BSONObjBuilder& result, + const Status& awaitReplicationStatus, + const WriteConcernResult& wcResult = WriteConcernResult()); /** * If true, then testing commands are available. Defaults to false. diff --git a/src/mongo/db/commands/apply_ops_cmd.cpp b/src/mongo/db/commands/apply_ops_cmd.cpp index 2301916ef6c..12df18040a9 100644 --- a/src/mongo/db/commands/apply_ops_cmd.cpp +++ b/src/mongo/db/commands/apply_ops_cmd.cpp @@ -53,7 +53,6 @@ #include "mongo/db/repl/oplog.h" #include "mongo/db/repl/repl_client_info.h" #include "mongo/db/repl/replication_coordinator_global.h" -#include "mongo/db/write_concern.h" #include "mongo/util/log.h" #include "mongo/util/scopeguard.h" @@ -72,6 +71,10 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& help) const { help << "internal (sharding)\n{ applyOps : [ ] , preCondition : [ { ns : ... , q : ... , " "res : ... } ] }"; @@ -112,14 +115,6 @@ public: } } - StatusWith wcResult = extractWriteConcern(txn, cmdObj, dbname); - if (!wcResult.isOK()) { - return appendCommandStatus(result, wcResult.getStatus()); - } - txn->setWriteConcern(wcResult.getValue()); - setupSynchronousCommit(txn); - - auto client = txn->getClient(); auto lastOpAtOperationStart = repl::ReplClientInfo::forClient(client).getLastOp(); ScopeGuard lastOpSetterGuard = @@ -136,14 +131,6 @@ public: lastOpSetterGuard.Dismiss(); } - WriteConcernResult res; - auto waitForWCStatus = - waitForWriteConcern(txn, - repl::ReplClientInfo::forClient(txn->getClient()).getLastOp(), - txn->getWriteConcern(), - &res); - appendCommandWCStatus(result, waitForWCStatus); - return applyOpsStatus; } diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp index b30a7694417..6fc6268a807 100644 --- a/src/mongo/db/commands/authentication_commands.cpp +++ b/src/mongo/db/commands/authentication_commands.cpp @@ -105,6 +105,9 @@ public: void help(stringstream& h) const { h << "internal"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) {} // No auth required @@ -360,6 +363,9 @@ public: void help(stringstream& h) const { h << "de-authenticate"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } CmdLogout() : Command("logout") {} bool run(OperationContext* txn, const string& dbname, diff --git a/src/mongo/db/commands/authentication_commands.h b/src/mongo/db/commands/authentication_commands.h index d11ae5bc536..4b1caf54913 100644 --- a/src/mongo/db/commands/authentication_commands.h +++ b/src/mongo/db/commands/authentication_commands.h @@ -46,6 +46,9 @@ public: virtual void help(std::stringstream& ss) const { ss << "internal"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) {} // No auth required diff --git a/src/mongo/db/commands/clone.cpp b/src/mongo/db/commands/clone.cpp index 499b8827a76..9ce858feb3d 100644 --- a/src/mongo/db/commands/clone.cpp +++ b/src/mongo/db/commands/clone.cpp @@ -60,6 +60,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& help) const { help << "clone this database from an instance of the db on another host\n"; help << "{clone: \"host13\"[, slaveOk: ]}"; diff --git a/src/mongo/db/commands/clone_collection.cpp b/src/mongo/db/commands/clone_collection.cpp index efd3500d395..85bd9c6b1ab 100644 --- a/src/mongo/db/commands/clone_collection.cpp +++ b/src/mongo/db/commands/clone_collection.cpp @@ -71,6 +71,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const { return parseNsFullyQualified(dbname, cmdObj); } diff --git a/src/mongo/db/commands/collection_to_capped.cpp b/src/mongo/db/commands/collection_to_capped.cpp index a0b283ebc49..03d8552e2ae 100644 --- a/src/mongo/db/commands/collection_to_capped.cpp +++ b/src/mongo/db/commands/collection_to_capped.cpp @@ -56,6 +56,9 @@ public: virtual bool slaveOk() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& help) const { help << "{ cloneCollectionAsCapped:, toCollection:, size: }"; } @@ -129,6 +132,9 @@ public: virtual bool slaveOk() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& help) const { help << "{ convertToCapped:, size: }"; } diff --git a/src/mongo/db/commands/compact.cpp b/src/mongo/db/commands/compact.cpp index cff4a4c359b..a87f2167be6 100644 --- a/src/mongo/db/commands/compact.cpp +++ b/src/mongo/db/commands/compact.cpp @@ -56,6 +56,9 @@ using std::stringstream; class CompactCmd : public Command { public: + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool adminOnly() const { return false; } diff --git a/src/mongo/db/commands/conn_pool_stats.cpp b/src/mongo/db/commands/conn_pool_stats.cpp index ffd0945ed3f..2a3b2881d45 100644 --- a/src/mongo/db/commands/conn_pool_stats.cpp +++ b/src/mongo/db/commands/conn_pool_stats.cpp @@ -53,6 +53,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) override { diff --git a/src/mongo/db/commands/conn_pool_sync.cpp b/src/mongo/db/commands/conn_pool_sync.cpp index 839e3a045b4..cb9410d7619 100644 --- a/src/mongo/db/commands/conn_pool_sync.cpp +++ b/src/mongo/db/commands/conn_pool_sync.cpp @@ -43,6 +43,9 @@ public: virtual void help(std::stringstream& help) const { help << "internal"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/db/commands/connection_status.cpp b/src/mongo/db/commands/connection_status.cpp index 340bc032cdb..8cf1a94ebd6 100644 --- a/src/mongo/db/commands/connection_status.cpp +++ b/src/mongo/db/commands/connection_status.cpp @@ -44,6 +44,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) {} // No auth required diff --git a/src/mongo/db/commands/copydb.cpp b/src/mongo/db/commands/copydb.cpp index a0ad9c46d81..7619c87a20d 100644 --- a/src/mongo/db/commands/copydb.cpp +++ b/src/mongo/db/commands/copydb.cpp @@ -98,6 +98,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, diff --git a/src/mongo/db/commands/copydb_start_commands.cpp b/src/mongo/db/commands/copydb_start_commands.cpp index feb96a9552a..e37695e4181 100644 --- a/src/mongo/db/commands/copydb_start_commands.cpp +++ b/src/mongo/db/commands/copydb_start_commands.cpp @@ -80,6 +80,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { @@ -149,6 +153,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp index c292f27c40c..b8367c35642 100644 --- a/src/mongo/db/commands/count_cmd.cpp +++ b/src/mongo/db/commands/count_cmd.cpp @@ -55,6 +55,9 @@ using std::stringstream; class CmdCount : public Command { public: CmdCount() : Command("count") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { // ok on --slave setups return repl::getGlobalReplicationCoordinator()->getSettings().isSlave(); diff --git a/src/mongo/db/commands/cpuprofile.cpp b/src/mongo/db/commands/cpuprofile.cpp index 2706d44510b..24fbd034b81 100644 --- a/src/mongo/db/commands/cpuprofile.cpp +++ b/src/mongo/db/commands/cpuprofile.cpp @@ -91,6 +91,9 @@ public: // This is an abuse of the global dbmutex. We only really need to // ensure that only one cpuprofiler command runs at once; it would // be fine for it to run concurrently with other operations. + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } }; /** diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp index cb1f09988d2..d99c74fc1ae 100644 --- a/src/mongo/db/commands/create_indexes.cpp +++ b/src/mongo/db/commands/create_indexes.cpp @@ -62,6 +62,9 @@ class CmdCreateIndex : public Command { public: CmdCreateIndex() : Command("createIndexes") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual bool slaveOk() const { return false; } // TODO: this could be made true... diff --git a/src/mongo/db/commands/current_op.cpp b/src/mongo/db/commands/current_op.cpp index 680a99ced73..fed56b550ad 100644 --- a/src/mongo/db/commands/current_op.cpp +++ b/src/mongo/db/commands/current_op.cpp @@ -54,6 +54,10 @@ public: CurrentOpCommand() : Command("currentOp") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const final { return true; } diff --git a/src/mongo/db/commands/dbhash.cpp b/src/mongo/db/commands/dbhash.cpp index dcb63a5936c..2bc75db0b74 100644 --- a/src/mongo/db/commands/dbhash.cpp +++ b/src/mongo/db/commands/dbhash.cpp @@ -64,6 +64,10 @@ class DBHashCmd : public Command { public: DBHashCmd() : Command("dbHash", false, "dbhash") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return true; } diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp index c03f6382fe0..966a7d5b32b 100644 --- a/src/mongo/db/commands/distinct.cpp +++ b/src/mongo/db/commands/distinct.cpp @@ -75,6 +75,9 @@ public: virtual bool slaveOverrideOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } bool supportsReadConcern() const final { return true; } diff --git a/src/mongo/db/commands/drop_indexes.cpp b/src/mongo/db/commands/drop_indexes.cpp index cead3ea45c7..950cf437750 100644 --- a/src/mongo/db/commands/drop_indexes.cpp +++ b/src/mongo/db/commands/drop_indexes.cpp @@ -70,6 +70,9 @@ public: virtual bool slaveOk() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& help) const { help << "drop indexes for a collection"; } @@ -99,6 +102,9 @@ public: virtual bool slaveOk() const { return true; } // can reindex on a secondary + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& help) const { help << "re-index a collection"; } diff --git a/src/mongo/db/commands/explain_cmd.cpp b/src/mongo/db/commands/explain_cmd.cpp index dc467846603..b0c8f1baec3 100644 --- a/src/mongo/db/commands/explain_cmd.cpp +++ b/src/mongo/db/commands/explain_cmd.cpp @@ -57,6 +57,10 @@ public: CmdExplain() : Command("explain") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + /** * Running an explain on a secondary requires explicitly setting slaveOk. */ diff --git a/src/mongo/db/commands/fail_point_cmd.cpp b/src/mongo/db/commands/fail_point_cmd.cpp index 65ca22d59e0..22de01b1d2c 100644 --- a/src/mongo/db/commands/fail_point_cmd.cpp +++ b/src/mongo/db/commands/fail_point_cmd.cpp @@ -70,6 +70,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool adminOnly() const { return true; } diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index 32b48d4eb3e..c806e67bdb7 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -63,6 +63,7 @@ #include "mongo/db/repl/replication_coordinator_global.h" #include "mongo/db/s/collection_sharding_state.h" #include "mongo/db/write_concern.h" +#include "mongo/s/d_state.h" #include "mongo/util/log.h" #include "mongo/util/scopeguard.h" @@ -214,6 +215,9 @@ public: bool slaveOk() const override { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) override { @@ -332,13 +336,6 @@ public: const FindAndModifyRequest& args = parseStatus.getValue(); const NamespaceString& nsString = args.getNamespaceString(); - StatusWith wcResult = extractWriteConcern(txn, cmdObj, dbName); - if (!wcResult.isOK()) { - return appendCommandStatus(result, wcResult.getStatus()); - } - txn->setWriteConcern(wcResult.getValue()); - setupSynchronousCommit(txn); - boost::optional maybeDisableValidation; if (shouldBypassDocumentValidationForCommand(cmdObj)) maybeDisableValidation.emplace(txn); @@ -350,6 +347,11 @@ public: &repl::ReplClientInfo::setLastOpToSystemLastOpTime, txn); + // If this is the local database, don't set last op. + if (dbName == "local") { + lastOpSetterGuard.Dismiss(); + } + // Although usually the PlanExecutor handles WCE internally, it will throw WCEs when it is // executing a findAndModify. This is done to ensure that we can always match, modify, and // return the document under concurrency, if a matching document exists. @@ -507,14 +509,6 @@ public: lastOpSetterGuard.Dismiss(); } - WriteConcernResult res; - auto waitForWCStatus = - waitForWriteConcern(txn, - repl::ReplClientInfo::forClient(txn->getClient()).getLastOp(), - txn->getWriteConcern(), - &res); - appendCommandWCStatus(result, waitForWCStatus); - return true; } diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp index 82c63c01cf8..f3a9ea67897 100644 --- a/src/mongo/db/commands/find_cmd.cpp +++ b/src/mongo/db/commands/find_cmd.cpp @@ -72,6 +72,10 @@ public: FindCmd() : Command("find") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const override { return false; } diff --git a/src/mongo/db/commands/fsync.cpp b/src/mongo/db/commands/fsync.cpp index 8bc028c926f..d8545f0853d 100644 --- a/src/mongo/db/commands/fsync.cpp +++ b/src/mongo/db/commands/fsync.cpp @@ -103,6 +103,9 @@ public: locked = false; pendingUnlock = false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } @@ -185,6 +188,10 @@ public: FSyncUnlockCommand() : Command("fsyncUnlock") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const override { return true; } diff --git a/src/mongo/db/commands/generic.cpp b/src/mongo/db/commands/generic.cpp index 9795e7f2b3e..05724e33705 100644 --- a/src/mongo/db/commands/generic.cpp +++ b/src/mongo/db/commands/generic.cpp @@ -78,6 +78,9 @@ public: virtual bool adminOnly() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) {} // No auth required @@ -110,6 +113,9 @@ public: help << "a way to check that the server is alive. responds immediately even if server is " "in a db lock."; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) {} // No auth required @@ -133,6 +139,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) {} // No auth required @@ -165,6 +174,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(stringstream& help) const { help << "returns information about the daemon's host"; } @@ -207,6 +220,9 @@ public: class LogRotateCmd : public Command { public: LogRotateCmd() : Command("logRotate") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } @@ -240,6 +256,9 @@ public: help << "get a list of all db commands"; } ListCommandsCmd() : Command("listCommands", false) {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } @@ -341,6 +360,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) {} // No auth required @@ -363,6 +385,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool adminOnly() const { return true; } @@ -430,6 +455,9 @@ public: void help(stringstream& h) const { h << "get argv"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool adminOnly() const { return true; } diff --git a/src/mongo/db/commands/geo_near_cmd.cpp b/src/mongo/db/commands/geo_near_cmd.cpp index 743329370be..080903e2320 100644 --- a/src/mongo/db/commands/geo_near_cmd.cpp +++ b/src/mongo/db/commands/geo_near_cmd.cpp @@ -63,6 +63,9 @@ class Geo2dFindNearCmd : public Command { public: Geo2dFindNearCmd() : Command("geoNear") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } bool slaveOk() const { return true; } diff --git a/src/mongo/db/commands/get_last_error.cpp b/src/mongo/db/commands/get_last_error.cpp index 4b026165cb0..98fbdcb9049 100644 --- a/src/mongo/db/commands/get_last_error.cpp +++ b/src/mongo/db/commands/get_last_error.cpp @@ -56,6 +56,9 @@ using std::stringstream; */ class CmdResetError : public Command { public: + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } @@ -80,6 +83,9 @@ public: class CmdGetLastError : public Command { public: CmdGetLastError() : Command("getLastError", false, "getlasterror") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } @@ -283,6 +289,9 @@ public: class CmdGetPrevError : public Command { public: + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(stringstream& help) const { help << "check for errors since last reseterror commandcal"; } diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp index 0d474ed100d..9b7e54b04ca 100644 --- a/src/mongo/db/commands/getmore_cmd.cpp +++ b/src/mongo/db/commands/getmore_cmd.cpp @@ -80,6 +80,10 @@ public: GetMoreCmd() : Command("getMore") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const override { return true; } diff --git a/src/mongo/db/commands/group_cmd.cpp b/src/mongo/db/commands/group_cmd.cpp index e49a9081b49..25554261b69 100644 --- a/src/mongo/db/commands/group_cmd.cpp +++ b/src/mongo/db/commands/group_cmd.cpp @@ -52,6 +52,10 @@ public: GroupCommand() : Command("group") {} private: + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool maintenanceOk() const { return false; } diff --git a/src/mongo/db/commands/hashcmd.cpp b/src/mongo/db/commands/hashcmd.cpp index f65690dddb3..76c1960f804 100644 --- a/src/mongo/db/commands/hashcmd.cpp +++ b/src/mongo/db/commands/hashcmd.cpp @@ -53,6 +53,9 @@ using std::stringstream; class CmdHashElt : public Command { public: CmdHashElt() : Command("_hashBSONElement"){}; + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } diff --git a/src/mongo/db/commands/index_filter_commands.cpp b/src/mongo/db/commands/index_filter_commands.cpp index 936dab77670..287e7e5950f 100644 --- a/src/mongo/db/commands/index_filter_commands.cpp +++ b/src/mongo/db/commands/index_filter_commands.cpp @@ -145,6 +145,10 @@ bool IndexFilterCommand::run(OperationContext* txn, } +bool IndexFilterCommand::supportsWriteConcern(const BSONObj& cmd) const { + return false; +} + bool IndexFilterCommand::slaveOk() const { return false; } diff --git a/src/mongo/db/commands/index_filter_commands.h b/src/mongo/db/commands/index_filter_commands.h index ddd2553823a..7ba1157bef7 100644 --- a/src/mongo/db/commands/index_filter_commands.h +++ b/src/mongo/db/commands/index_filter_commands.h @@ -70,6 +70,8 @@ public: std::string& errmsg, BSONObjBuilder& result); + virtual bool supportsWriteConcern(const BSONObj& cmd) const override; + virtual bool slaveOk() const; virtual bool slaveOverrideOk() const; diff --git a/src/mongo/db/commands/isself.cpp b/src/mongo/db/commands/isself.cpp index a3d8a3af918..0db7ba01440 100644 --- a/src/mongo/db/commands/isself.cpp +++ b/src/mongo/db/commands/isself.cpp @@ -45,6 +45,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(stringstream& help) const { help << "{ _isSelf : 1 } INTERNAL ONLY"; } diff --git a/src/mongo/db/commands/kill_op.cpp b/src/mongo/db/commands/kill_op.cpp index 753d48b9362..57838d625d1 100644 --- a/src/mongo/db/commands/kill_op.cpp +++ b/src/mongo/db/commands/kill_op.cpp @@ -50,6 +50,10 @@ public: KillOpCommand() : Command("killOp") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const final { return true; } diff --git a/src/mongo/db/commands/killcursors_common.h b/src/mongo/db/commands/killcursors_common.h index 7d082063858..db92c7ab21b 100644 --- a/src/mongo/db/commands/killcursors_common.h +++ b/src/mongo/db/commands/killcursors_common.h @@ -42,6 +42,10 @@ public: KillCursorsCmdBase() : Command("killCursors") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const final { return true; } diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp index abd8302434f..0f9876420de 100644 --- a/src/mongo/db/commands/list_collections.cpp +++ b/src/mongo/db/commands/list_collections.cpp @@ -149,6 +149,9 @@ public: virtual bool adminOnly() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(stringstream& help) const { help << "list collections for this db"; diff --git a/src/mongo/db/commands/list_databases.cpp b/src/mongo/db/commands/list_databases.cpp index f7184d7de7d..88978246b19 100644 --- a/src/mongo/db/commands/list_databases.cpp +++ b/src/mongo/db/commands/list_databases.cpp @@ -57,6 +57,9 @@ public: virtual bool adminOnly() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(stringstream& help) const { help << "list databases on this server"; } diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp index 5d443e98e4b..f6d144de358 100644 --- a/src/mongo/db/commands/list_indexes.cpp +++ b/src/mongo/db/commands/list_indexes.cpp @@ -82,6 +82,9 @@ public: virtual bool adminOnly() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(stringstream& help) const { help << "list indexes for a collection"; diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp index 56aedcd16ae..9e5e6259d3e 100644 --- a/src/mongo/db/commands/mr.cpp +++ b/src/mongo/db/commands/mr.cpp @@ -602,7 +602,7 @@ long long State::postProcessCollectionNonAtomic(OperationContext* txn, ScopedTransaction transaction(txn, MODE_X); Lock::GlobalWrite lock(txn->lockState()); // TODO(erh): why global??? // replace: just rename from temp to final collection name, dropping previous collection - _db.dropCollection(_config.outputOptions.finalNamespace); + _db.dropCollection(_config.outputOptions.finalNamespace, txn->getWriteConcern()); BSONObj info; if (!_db.runCommand("admin", @@ -613,7 +613,7 @@ long long State::postProcessCollectionNonAtomic(OperationContext* txn, uasserted(10076, str::stream() << "rename failed: " << info); } - _db.dropCollection(_config.tempNamespace); + _db.dropCollection(_config.tempNamespace, txn->getWriteConcern()); } else if (_config.outputOptions.outType == Config::MERGE) { // merge: upsert new docs into old collection { @@ -632,7 +632,7 @@ long long State::postProcessCollectionNonAtomic(OperationContext* txn, Helpers::upsert(_txn, _config.outputOptions.finalNamespace, o); pm.hit(); } - _db.dropCollection(_config.tempNamespace); + _db.dropCollection(_config.tempNamespace, txn->getWriteConcern()); pm.finished(); } else if (_config.outputOptions.outType == Config::REDUCE) { // reduce: apply reduce op on new result and existing one @@ -1287,6 +1287,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return mrSupportsWriteConcern(cmd); + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { @@ -1301,6 +1305,11 @@ public: BSONObjBuilder& result) { Timer t; + // Save and reset the write concern so that it doesn't get changed accidentally by + // DBDirectClient. + auto oldWC = txn->getWriteConcern(); + ON_BLOCK_EXIT([txn, oldWC] { txn->setWriteConcern(oldWC); }); + boost::optional maybeDisableValidation; if (shouldBypassDocumentValidationForCommand(cmd)) maybeDisableValidation.emplace(txn); @@ -1588,6 +1597,9 @@ public: virtual bool slaveOverrideOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { @@ -1609,6 +1621,11 @@ public: << dbname)); } + // Save and reset the write concern so that it doesn't get changed accidentally by + // DBDirectClient. + auto oldWC = txn->getWriteConcern(); + ON_BLOCK_EXIT([txn, oldWC] { txn->setWriteConcern(oldWC); }); + boost::optional maybeDisableValidation; if (shouldBypassDocumentValidationForCommand(cmdObj)) maybeDisableValidation.emplace(txn); diff --git a/src/mongo/db/commands/mr.h b/src/mongo/db/commands/mr.h index 04b5f12a661..3d967438179 100644 --- a/src/mongo/db/commands/mr.h +++ b/src/mongo/db/commands/mr.h @@ -411,5 +411,11 @@ void addPrivilegesRequiredForMapReduce(Command* commandTemplate, const std::string& dbname, const BSONObj& cmdObj, std::vector* out); + +/** + * Returns true if the provided mapReduce command has an 'out' parameter. + */ +bool mrSupportsWriteConcern(const BSONObj& cmd); + } // end mr namespace } diff --git a/src/mongo/db/commands/mr_common.cpp b/src/mongo/db/commands/mr_common.cpp index 4d11661fd2d..d180f276249 100644 --- a/src/mongo/db/commands/mr_common.cpp +++ b/src/mongo/db/commands/mr_common.cpp @@ -132,5 +132,15 @@ void addPrivilegesRequiredForMapReduce(Command* commandTemplate, out->push_back(Privilege(outputResource, outputActions)); } } + +bool mrSupportsWriteConcern(const BSONObj& cmd) { + if (!cmd.hasField("out")) { + return false; + } else if (cmd["out"].type() == Object && cmd["out"].Obj().hasField("inline")) { + return false; + } else { + return true; + } +} } } diff --git a/src/mongo/db/commands/oplog_note.cpp b/src/mongo/db/commands/oplog_note.cpp index 6c96f0907b8..bebf6d4d13c 100644 --- a/src/mongo/db/commands/oplog_note.cpp +++ b/src/mongo/db/commands/oplog_note.cpp @@ -54,6 +54,9 @@ public: virtual bool adminOnly() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& help) const { help << "Adds a no-op entry to the oplog"; } diff --git a/src/mongo/db/commands/parallel_collection_scan.cpp b/src/mongo/db/commands/parallel_collection_scan.cpp index 4ca7a131f60..bc280f4b937 100644 --- a/src/mongo/db/commands/parallel_collection_scan.cpp +++ b/src/mongo/db/commands/parallel_collection_scan.cpp @@ -58,6 +58,9 @@ public: ParallelCollectionScanCmd() : Command("parallelCollectionScan") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } diff --git a/src/mongo/db/commands/parameters.cpp b/src/mongo/db/commands/parameters.cpp index 17a85c757b3..c9bc974e040 100644 --- a/src/mongo/db/commands/parameters.cpp +++ b/src/mongo/db/commands/parameters.cpp @@ -71,6 +71,9 @@ public: virtual bool adminOnly() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { @@ -118,6 +121,9 @@ public: virtual bool adminOnly() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp index b04d6ffe1ef..d8836346c5e 100644 --- a/src/mongo/db/commands/pipeline_command.cpp +++ b/src/mongo/db/commands/pipeline_command.cpp @@ -53,6 +53,7 @@ #include "mongo/db/query/get_executor.h" #include "mongo/db/storage/storage_options.h" #include "mongo/stdx/memory.h" +#include "mongo/util/scopeguard.h" namespace mongo { @@ -154,6 +155,9 @@ public: PipelineCommand() : Command(Pipeline::commandName) {} // command is called "aggregate" // Locks are managed manually, in particular by DocumentSourceCursor. + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return Pipeline::aggSupportsWriteConcern(cmd); + } virtual bool slaveOk() const { return false; } @@ -199,6 +203,11 @@ public: if (!pPipeline.get()) return false; + // Save and reset the write concern so that it doesn't get changed accidentally by + // DBDirectClient. + auto oldWC = txn->getWriteConcern(); + ON_BLOCK_EXIT([txn, oldWC] { txn->setWriteConcern(oldWC); }); + // This is outside of the if block to keep the object alive until the pipeline is finished. BSONObj parsed; if (kDebugBuild && !pPipeline->isExplain() && !pCtx->inShard) { diff --git a/src/mongo/db/commands/plan_cache_commands.cpp b/src/mongo/db/commands/plan_cache_commands.cpp index 49c25de5514..b7c408f3b3c 100644 --- a/src/mongo/db/commands/plan_cache_commands.cpp +++ b/src/mongo/db/commands/plan_cache_commands.cpp @@ -141,6 +141,10 @@ bool PlanCacheCommand::run(OperationContext* txn, } +bool PlanCacheCommand::supportsWriteConcern(const BSONObj& cmd) const { + return false; +} + bool PlanCacheCommand::slaveOk() const { return false; } diff --git a/src/mongo/db/commands/plan_cache_commands.h b/src/mongo/db/commands/plan_cache_commands.h index f419eaed8eb..87316b84178 100644 --- a/src/mongo/db/commands/plan_cache_commands.h +++ b/src/mongo/db/commands/plan_cache_commands.h @@ -64,6 +64,8 @@ public: std::string& errmsg, BSONObjBuilder& result); + virtual bool supportsWriteConcern(const BSONObj& cmd) const override; + virtual bool slaveOk() const; virtual bool slaveOverrideOk() const; diff --git a/src/mongo/db/commands/rename_collection_cmd.cpp b/src/mongo/db/commands/rename_collection_cmd.cpp index b2b12c5fed3..eb42664c57b 100644 --- a/src/mongo/db/commands/rename_collection_cmd.cpp +++ b/src/mongo/db/commands/rename_collection_cmd.cpp @@ -67,6 +67,9 @@ public: virtual bool slaveOk() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { diff --git a/src/mongo/db/commands/repair_cursor.cpp b/src/mongo/db/commands/repair_cursor.cpp index 8792afe2092..ad8afe526bb 100644 --- a/src/mongo/db/commands/repair_cursor.cpp +++ b/src/mongo/db/commands/repair_cursor.cpp @@ -48,6 +48,9 @@ class RepairCursorCmd : public Command { public: RepairCursorCmd() : Command("repairCursor") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } diff --git a/src/mongo/db/commands/server_status.cpp b/src/mongo/db/commands/server_status.cpp index e7f15f8e4cb..423991bdbfc 100644 --- a/src/mongo/db/commands/server_status.cpp +++ b/src/mongo/db/commands/server_status.cpp @@ -67,6 +67,9 @@ public: CmdServerStatus() : Command("serverStatus", true), _started(curTimeMillis64()), _runCalled(false) {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } diff --git a/src/mongo/db/commands/shutdown.h b/src/mongo/db/commands/shutdown.h index 2bb71e7373d..f1f55d0668c 100644 --- a/src/mongo/db/commands/shutdown.h +++ b/src/mongo/db/commands/shutdown.h @@ -54,6 +54,9 @@ public: virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out); + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } protected: static void shutdownHelper(); diff --git a/src/mongo/db/commands/snapshot_management.cpp b/src/mongo/db/commands/snapshot_management.cpp index cb90e41d685..9eec705e4af 100644 --- a/src/mongo/db/commands/snapshot_management.cpp +++ b/src/mongo/db/commands/snapshot_management.cpp @@ -45,6 +45,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool adminOnly() const { return true; } @@ -92,6 +95,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool adminOnly() const { return true; } diff --git a/src/mongo/db/commands/test_commands.cpp b/src/mongo/db/commands/test_commands.cpp index 39b6ef90636..58d554cef84 100644 --- a/src/mongo/db/commands/test_commands.cpp +++ b/src/mongo/db/commands/test_commands.cpp @@ -65,6 +65,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } // No auth needed because it only works when enabled via command line. virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, @@ -110,6 +113,10 @@ public: /* for diagnostic / testing purposes. Enabled via command line. */ class CmdSleep : public Command { public: + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool adminOnly() const { return true; } @@ -204,6 +211,9 @@ public: virtual bool slaveOk() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } // No auth needed because it only works when enabled via command line. virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, @@ -271,6 +281,9 @@ public: virtual bool slaveOk() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } // No auth needed because it only works when enabled via command line. virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, diff --git a/src/mongo/db/commands/top_command.cpp b/src/mongo/db/commands/top_command.cpp index 70407d67ea6..e4b788dc711 100644 --- a/src/mongo/db/commands/top_command.cpp +++ b/src/mongo/db/commands/top_command.cpp @@ -52,6 +52,9 @@ public: virtual bool adminOnly() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(std::stringstream& help) const { help << "usage by collection, in micros "; } diff --git a/src/mongo/db/commands/touch.cpp b/src/mongo/db/commands/touch.cpp index 530d8937588..520f74e64a1 100644 --- a/src/mongo/db/commands/touch.cpp +++ b/src/mongo/db/commands/touch.cpp @@ -57,6 +57,9 @@ using std::stringstream; class TouchCmd : public Command { public: + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool adminOnly() const { return false; } diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index 5e35694173e..f058a892a9b 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -62,12 +62,18 @@ #include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" #include "mongo/platform/unordered_set.h" +#include "mongo/rpc/get_status_from_command_result.h" #include "mongo/rpc/protocol.h" +#include "mongo/s/write_ops/batched_command_response.h" +#include "mongo/s/write_ops/batched_delete_request.h" +#include "mongo/s/write_ops/batched_insert_request.h" +#include "mongo/s/write_ops/batched_update_request.h" #include "mongo/stdx/functional.h" #include "mongo/stdx/mutex.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" #include "mongo/util/net/ssl_manager.h" +#include "mongo/util/scopeguard.h" #include "mongo/util/sequence_util.h" #include "mongo/util/time_support.h" @@ -260,24 +266,28 @@ Status insertAuthzDocument(OperationContext* txn, const NamespaceString& collectionName, const BSONObj& document, const BSONObj& writeConcern) { + // Save and reset the write concern so that it doesn't get changed accidentally by + // DBDirectClient. + auto oldWC = txn->getWriteConcern(); + ON_BLOCK_EXIT([txn, oldWC] { txn->setWriteConcern(oldWC); }); + try { DBDirectClient client(txn); - client.insert(collectionName.ns(), document); - // Handle write concern - BSONObjBuilder gleBuilder; - gleBuilder.append("getLastError", 1); - gleBuilder.appendElements(writeConcern); + BatchedInsertRequest req; + req.setNS(collectionName); + req.addToDocuments(document); + BSONObj res; - client.runCommand("admin", gleBuilder.done(), res); - string errstr = client.getLastErrorString(res); - if (errstr.empty()) { - return Status::OK(); - } - if (res.hasField("code") && res["code"].Int() == ASSERT_ID_DUPKEY) { - return Status(ErrorCodes::DuplicateKey, errstr); + client.runCommand(collectionName.db().toString(), req.toBSON(), res); + + BatchedCommandResponse response; + std::string errmsg; + response.parseBSON(res, &errmsg); + if (errmsg != "") { + return Status(ErrorCodes::FailedToParse, errmsg); } - return Status(ErrorCodes::UnknownError, errstr); + return response.toStatus(); } catch (const DBException& e) { return e.toStatus(); } @@ -296,23 +306,38 @@ Status updateAuthzDocuments(OperationContext* txn, bool upsert, bool multi, const BSONObj& writeConcern, - int* nMatched) { + long long* nMatched) { + // Save and reset the write concern so that it doesn't get changed accidentally by + // DBDirectClient. + auto oldWC = txn->getWriteConcern(); + ON_BLOCK_EXIT([txn, oldWC] { txn->setWriteConcern(oldWC); }); + try { DBDirectClient client(txn); - client.update(collectionName.ns(), query, updatePattern, upsert, multi); - // Handle write concern - BSONObjBuilder gleBuilder; - gleBuilder.append("getLastError", 1); - gleBuilder.appendElements(writeConcern); + auto doc = stdx::make_unique(); + doc->setQuery(query); + doc->setUpdateExpr(updatePattern); + doc->setMulti(multi); + doc->setUpsert(upsert); + + BatchedUpdateRequest req; + req.setNS(collectionName); + req.addToUpdates(doc.release()); + BSONObj res; - client.runCommand("admin", gleBuilder.done(), res); - string errstr = client.getLastErrorString(res); - if (errstr.empty()) { - *nMatched = res["n"].numberInt(); - return Status::OK(); + client.runCommand(collectionName.db().toString(), req.toBSON(), res); + + BatchedCommandResponse response; + std::string errmsg; + response.parseBSON(res, &errmsg); + if (errmsg != "") { + return Status(ErrorCodes::FailedToParse, errmsg); } - return Status(ErrorCodes::UnknownError, errstr); + if (response.getOk()) { + *nMatched = response.getN(); + } + return response.toStatus(); } catch (const DBException& e) { return e.toStatus(); } @@ -336,7 +361,7 @@ Status updateOneAuthzDocument(OperationContext* txn, const BSONObj& updatePattern, bool upsert, const BSONObj& writeConcern) { - int nMatched; + long long nMatched; Status status = updateAuthzDocuments( txn, collectionName, query, updatePattern, upsert, false, writeConcern, &nMatched); if (!status.isOK()) { @@ -359,23 +384,36 @@ Status removeAuthzDocuments(OperationContext* txn, const NamespaceString& collectionName, const BSONObj& query, const BSONObj& writeConcern, - int* numRemoved) { + long long* numRemoved) { + // Save and reset the write concern so that it doesn't get changed accidentally by + // DBDirectClient. + auto oldWC = txn->getWriteConcern(); + ON_BLOCK_EXIT([txn, oldWC] { txn->setWriteConcern(oldWC); }); + try { DBDirectClient client(txn); - client.remove(collectionName.ns(), query); - // Handle write concern - BSONObjBuilder gleBuilder; - gleBuilder.append("getLastError", 1); - gleBuilder.appendElements(writeConcern); + auto doc = stdx::make_unique(); + doc->setQuery(query); + doc->setLimit(0); + + BatchedDeleteRequest req; + req.setNS(collectionName); + req.addToDeletes(doc.release()); + BSONObj res; - client.runCommand("admin", gleBuilder.done(), res); - string errstr = client.getLastErrorString(res); - if (errstr.empty()) { - *numRemoved = res["n"].numberInt(); - return Status::OK(); + client.runCommand(collectionName.db().toString(), req.toBSON(), res); + + BatchedCommandResponse response; + std::string errmsg; + response.parseBSON(res, &errmsg); + if (errmsg != "") { + return Status(ErrorCodes::FailedToParse, errmsg); } - return Status(ErrorCodes::UnknownError, errstr); + if (response.getOk()) { + *numRemoved = response.getN(); + } + return response.toStatus(); } catch (const DBException& e) { return e.toStatus(); } @@ -445,7 +483,7 @@ Status updateRoleDocument(OperationContext* txn, Status removeRoleDocuments(OperationContext* txn, const BSONObj& query, const BSONObj& writeConcern, - int* numRemoved) { + long long* numRemoved) { Status status = removeAuthzDocuments( txn, AuthorizationManager::rolesCollectionNamespace, query, writeConcern, numRemoved); if (status.code() == ErrorCodes::UnknownError) { @@ -518,7 +556,7 @@ Status updatePrivilegeDocument(OperationContext* txn, Status removePrivilegeDocuments(OperationContext* txn, const BSONObj& query, const BSONObj& writeConcern, - int* numRemoved) { + long long* numRemoved) { Status status = removeAuthzDocuments( txn, AuthorizationManager::usersCollectionNamespace, query, writeConcern, numRemoved); if (status.code() == ErrorCodes::UnknownError) { @@ -608,6 +646,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Adds a user to the system" << endl; @@ -750,6 +791,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Used to update a user, for example to change its password" << endl; @@ -864,6 +908,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Drops a single user." << endl; @@ -899,7 +946,7 @@ public: audit::logDropUser(ClientBasic::getCurrent(), userName); - int nMatched; + long long nMatched; status = removePrivilegeDocuments(txn, BSON(AuthorizationManager::USER_NAME_FIELD_NAME << userName.getUser() @@ -933,6 +980,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Drops all users for a single database." << endl; @@ -967,7 +1017,7 @@ public: audit::logDropAllUsersFromDatabase(ClientBasic::getCurrent(), dbname); - int numRemoved; + long long numRemoved; status = removePrivilegeDocuments(txn, BSON(AuthorizationManager::USER_DB_FIELD_NAME << dbname), writeConcern, @@ -992,6 +1042,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Grants roles to a user." << endl; @@ -1064,6 +1117,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Revokes roles from a user." << endl; @@ -1138,6 +1194,9 @@ public: return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } CmdUsersInfo() : Command("usersInfo") {} @@ -1289,6 +1348,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Adds a role to the system" << endl; @@ -1400,6 +1462,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Used to update a role" << endl; @@ -1496,6 +1561,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Grants privileges to a role" << endl; @@ -1601,6 +1669,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Revokes privileges from a role" << endl; @@ -1714,6 +1785,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Grants roles to another role." << endl; @@ -1806,6 +1880,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Revokes roles from another role." << endl; @@ -1892,6 +1969,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Drops a single role. Before deleting the role completely it must remove it " @@ -1943,7 +2023,7 @@ public: } // Remove this role from all users - int nMatched; + long long nMatched; status = updateAuthzDocuments( txn, AuthorizationManager::usersCollectionNamespace, @@ -2043,6 +2123,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Drops all roles from the given database. Before deleting the roles completely " @@ -2080,7 +2163,7 @@ public: } // Remove these roles from all users - int nMatched; + long long nMatched; status = updateAuthzDocuments( txn, AuthorizationManager::usersCollectionNamespace, @@ -2163,6 +2246,9 @@ public: return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } CmdRolesInfo() : Command("rolesInfo") {} @@ -2235,6 +2321,9 @@ public: return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } CmdInvalidateUserCache() : Command("invalidateUserCache") {} @@ -2271,6 +2360,9 @@ public: return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } CmdGetCacheGeneration() : Command("_getUserCacheGeneration") {} @@ -2315,6 +2407,9 @@ public: return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual bool adminOnly() const { return true; @@ -2559,7 +2654,7 @@ public: } if (drop) { - int numRemoved; + long long numRemoved; for (unordered_set::iterator it = usersToDrop.begin(); it != usersToDrop.end(); ++it) { @@ -2640,7 +2735,7 @@ public: } if (drop) { - int numRemoved; + long long numRemoved; for (unordered_set::iterator it = rolesToDrop.begin(); it != rolesToDrop.end(); ++it) { @@ -2897,6 +2992,9 @@ public: return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& ss) const { ss << "Upgrades the auth data storage schema"; diff --git a/src/mongo/db/commands/validate.cpp b/src/mongo/db/commands/validate.cpp index e41374cba6a..1569bf5cdcc 100644 --- a/src/mongo/db/commands/validate.cpp +++ b/src/mongo/db/commands/validate.cpp @@ -62,6 +62,9 @@ public: "Add full:true option to do a more thorough check"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp index 51b446532a7..bd71a02d6aa 100644 --- a/src/mongo/db/commands/write_commands/batch_executor.cpp +++ b/src/mongo/db/commands/write_commands/batch_executor.cpp @@ -121,19 +121,6 @@ private: std::unique_ptr _error; }; -unique_ptr toWriteConcernError(const Status& wcStatus, - const WriteConcernResult& wcResult) { - auto wcError = stdx::make_unique(); - - wcError->setErrCode(wcStatus.code()); - wcError->setErrMessage(wcStatus.reason()); - if (wcResult.wTimedOut) { - wcError->setErrInfo(BSON("wtimeout" << true)); - } - - return wcError; -} - WriteErrorDetail* toWriteError(const Status& status) { WriteErrorDetail* error = new WriteErrorDetail; @@ -277,27 +264,6 @@ void WriteBatchExecutor::executeBatch(const BatchedCommandRequest& request, bulkExecute(request, &upserted, &writeErrors); - // - // Always try to enforce the write concern, even if everything failed. - // If something failed, we have already set the lastOp to be the last op to have succeeded - // and written to the oplog. - - { - stdx::lock_guard lk(*_txn->getClient()); - CurOp::get(_txn)->setMessage_inlock("waiting for write concern"); - } - - unique_ptr wcError; - WriteConcernResult res; - Status status = - waitForWriteConcern(_txn, - repl::ReplClientInfo::forClient(_txn->getClient()).getLastOp(), - _txn->getWriteConcern(), - &res); - if (!status.isOK()) { - wcError = toWriteConcernError(status, res); - } - // // Refresh metadata if needed // @@ -328,10 +294,6 @@ void WriteBatchExecutor::executeBatch(const BatchedCommandRequest& request, response->setErrDetails(writeErrors); } - if (wcError.get()) { - response->setWriteConcernError(wcError.release()); - } - repl::ReplicationCoordinator* replCoord = repl::getGlobalReplicationCoordinator(); const repl::ReplicationCoordinator::Mode replMode = replCoord->getReplicationMode(); if (replMode != repl::ReplicationCoordinator::modeNone) { @@ -743,6 +705,11 @@ void WriteBatchExecutor::execInserts(const BatchedCommandRequest& request, &repl::ReplClientInfo::setLastOpToSystemLastOpTime, _txn); + // If this is the local database, don't set last op. + if (request.getNS().isLocal()) { + lastOpSetterGuard.Dismiss(); + } + int64_t chunkCount = 0; int64_t chunkBytes = 0; const int64_t chunkMaxCount = internalQueryExecYieldIterations / 2; @@ -1047,6 +1014,11 @@ static void multiUpdate(OperationContext* txn, &repl::ReplClientInfo::setLastOpToSystemLastOpTime, txn); + // If this is the local database, don't set last op. + if (nsString.isLocal()) { + lastOpSetterGuard.Dismiss(); + } + int attempt = 0; bool createCollection = false; for (int fakeLoop = 0; fakeLoop < 1; fakeLoop++) { @@ -1226,6 +1198,11 @@ static void multiRemove(OperationContext* txn, &repl::ReplClientInfo::setLastOpToSystemLastOpTime, txn); + // If this is the local database, don't set last op. + if (nss.isLocal()) { + lastOpSetterGuard.Dismiss(); + } + int attempt = 1; while (1) { try { diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp index cd3c13674a1..7e56675ef3c 100644 --- a/src/mongo/db/commands/write_commands/write_commands.cpp +++ b/src/mongo/db/commands/write_commands/write_commands.cpp @@ -92,6 +92,10 @@ bool WriteCmd::slaveOk() const { } +bool WriteCmd::supportsWriteConcern(const BSONObj& cmd) const { + return true; +} + Status WriteCmd::checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { @@ -128,13 +132,6 @@ bool WriteCmd::run(OperationContext* txn, return appendCommandStatus(result, Status(ErrorCodes::FailedToParse, errMsg)); } - StatusWith wcStatus = extractWriteConcern(txn, cmdObj, dbName); - - if (!wcStatus.isOK()) { - return appendCommandStatus(result, wcStatus.getStatus()); - } - txn->setWriteConcern(wcStatus.getValue()); - WriteBatchExecutor writeBatchExecutor( txn, &globalOpCounters, &LastError::get(txn->getClient())); diff --git a/src/mongo/db/commands/write_commands/write_commands.h b/src/mongo/db/commands/write_commands/write_commands.h index e8dfaaa9608..e01ade2b2ff 100644 --- a/src/mongo/db/commands/write_commands/write_commands.h +++ b/src/mongo/db/commands/write_commands/write_commands.h @@ -63,6 +63,8 @@ protected: private: virtual bool slaveOk() const; + virtual bool supportsWriteConcern(const BSONObj& cmd) const override; + virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj); diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index 1516a27989a..94e9c05c80e 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -180,6 +180,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + CmdDropDatabase() : Command("dropDatabase") {} bool run(OperationContext* txn, @@ -236,6 +240,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { @@ -309,6 +317,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { @@ -398,6 +410,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { @@ -462,6 +478,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, @@ -499,6 +519,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& help) const { help << "create a collection explicitly\n" "{ create: [, capped: , size: , max: ] }"; @@ -554,6 +578,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const { std::string collectionName = cmdObj.getStringField("root"); if (collectionName.empty()) @@ -724,6 +752,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(stringstream& help) const { help << "determine data size for a set of data in a certain range" "\nexample: { dataSize:\"blog.posts\", keyPattern:{x:1}, min:{x:10}, max:{x:55} }" @@ -869,6 +900,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(stringstream& help) const { help << "{ collStats:\"blog.posts\" , scale : 1 } scale divides sizes e.g. for KB use 1024\n" @@ -976,6 +1010,9 @@ public: virtual bool slaveOk() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual void help(stringstream& help) const { help << "Sets collection options.\n" "Example: { collMod: 'foo', usePowerOf2Sizes:true }\n" @@ -1009,6 +1046,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(stringstream& help) const { help << "Get stats on a database. Not instantaneous. Slower for databases with large " ".ns files.\n" @@ -1096,6 +1136,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void help(stringstream& help) const { help << "{whatsmyuri:1}"; } @@ -1120,6 +1163,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { @@ -1459,8 +1505,41 @@ bool Command::run(OperationContext* txn, // run expects const db std::string (can't bind to temporary) const std::string db = request.getDatabase().toString(); + StatusWith wcResult = + extractWriteConcern(txn, cmd, db, this->supportsWriteConcern(cmd)); + if (!wcResult.isOK()) { + auto result = appendCommandStatus(inPlaceReplyBob, wcResult.getStatus()); + inPlaceReplyBob.doneFast(); + replyBuilder->setMetadata(rpc::makeEmptyMetadata()); + return result; + } + + if (this->supportsWriteConcern(cmd)) { + txn->setWriteConcern(wcResult.getValue()); + } + // TODO: remove queryOptions parameter from command's run method. bool result = this->run(txn, db, cmd, 0, errmsg, inPlaceReplyBob); + + if (this->supportsWriteConcern(cmd)) { + if (shouldLog(logger::LogSeverity::Debug(1))) { + BSONObj oldWC = wcResult.getValue().toBSON(); + BSONObj newWC = txn->getWriteConcern().toBSON(); + if (oldWC != newWC) { + LOG(1) << "Provided writeConcern was overridden from " << oldWC << " to " << newWC + << " for command " << cmd; + } + } + + WriteConcernResult res; + auto waitForWCStatus = + waitForWriteConcern(txn, + repl::ReplClientInfo::forClient(txn->getClient()).getLastOp(), + txn->getWriteConcern(), + &res); + appendCommandWCStatus(inPlaceReplyBob, waitForWCStatus, res); + } + appendCommandStatus(inPlaceReplyBob, result, errmsg); inPlaceReplyBob.doneFast(); diff --git a/src/mongo/db/dbeval.cpp b/src/mongo/db/dbeval.cpp index 6df169bfefd..055c3a2dc68 100644 --- a/src/mongo/db/dbeval.cpp +++ b/src/mongo/db/dbeval.cpp @@ -160,6 +160,9 @@ public: << "Evaluate javascript at the server.\n" << "http://dochub.mongodb.org/core/serversidecodeexecution"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/db/driverHelpers.cpp b/src/mongo/db/driverHelpers.cpp index 0480050edce..8b6163e678b 100644 --- a/src/mongo/db/driverHelpers.cpp +++ b/src/mongo/db/driverHelpers.cpp @@ -56,6 +56,9 @@ class BasicDriverHelper : public Command { public: BasicDriverHelper(const char* name) : Command(name) {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } diff --git a/src/mongo/db/exec/stagedebug_cmd.cpp b/src/mongo/db/exec/stagedebug_cmd.cpp index 8c012f01862..a56f7417063 100644 --- a/src/mongo/db/exec/stagedebug_cmd.cpp +++ b/src/mongo/db/exec/stagedebug_cmd.cpp @@ -119,6 +119,9 @@ class StageDebugCmd : public Command { public: StageDebugCmd() : Command("stageDebug") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } bool slaveOk() const { return false; } diff --git a/src/mongo/db/geo/haystack.cpp b/src/mongo/db/geo/haystack.cpp index 2027dc5dd8c..9fc3a2cccf8 100644 --- a/src/mongo/db/geo/haystack.cpp +++ b/src/mongo/db/geo/haystack.cpp @@ -62,6 +62,9 @@ class GeoHaystackSearchCommand : public Command { public: GeoHaystackSearchCommand() : Command("geoSearch") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } bool slaveOk() const { return true; } diff --git a/src/mongo/db/namespace_string.h b/src/mongo/db/namespace_string.h index 651c5fc830e..2f9ccc44490 100644 --- a/src/mongo/db/namespace_string.h +++ b/src/mongo/db/namespace_string.h @@ -134,6 +134,9 @@ public: bool isSystem() const { return coll().startsWith("system."); } + bool isLocal() const { + return db() == "local"; + } bool isSystemDotIndexes() const { return coll() == "system.indexes"; } diff --git a/src/mongo/db/ops/update.cpp b/src/mongo/db/ops/update.cpp index 5580d040fda..df64f72b232 100644 --- a/src/mongo/db/ops/update.cpp +++ b/src/mongo/db/ops/update.cpp @@ -74,6 +74,11 @@ UpdateResult update(OperationContext* txn, const NamespaceString& nsString = request.getNamespaceString(); Collection* collection = db->getCollection(nsString.ns()); + // If this is the local database, don't set last op. + if (db->name() == "local") { + lastOpSetterGuard.Dismiss(); + } + // The update stage does not create its own collection. As such, if the update is // an upsert, create the collection that the update stage inserts into beforehand. if (!collection && request.isUpsert()) { diff --git a/src/mongo/db/pipeline/pipeline.cpp b/src/mongo/db/pipeline/pipeline.cpp index ac861a0f6dd..535c05a4030 100644 --- a/src/mongo/db/pipeline/pipeline.cpp +++ b/src/mongo/db/pipeline/pipeline.cpp @@ -96,6 +96,11 @@ intrusive_ptr Pipeline::parseCommand(string& errmsg, continue; } + // ignore writeConcern since it's handled externally + if (str::equals(pFieldName, "writeConcern")) { + continue; + } + /* look for the aggregation command */ if (!strcmp(pFieldName, commandName)) { continue; @@ -254,6 +259,21 @@ Status Pipeline::checkAuthForCommand(ClientBasic* client, return Status(ErrorCodes::Unauthorized, "unauthorized"); } +bool Pipeline::aggSupportsWriteConcern(const BSONObj& cmd) { + if (cmd.hasField("pipeline") == false) { + return false; + } + + auto stages = cmd["pipeline"].Array(); + for (auto stage : stages) { + if (stage.Obj().hasField("$out")) { + return true; + } + } + + return false; +} + void Pipeline::detachFromOperationContext() { pCtx->opCtx = nullptr; diff --git a/src/mongo/db/pipeline/pipeline.h b/src/mongo/db/pipeline/pipeline.h index 0a23cbd88e6..61fab79c347 100644 --- a/src/mongo/db/pipeline/pipeline.h +++ b/src/mongo/db/pipeline/pipeline.h @@ -73,6 +73,11 @@ public: const std::string& dbname, const BSONObj& cmdObj); + /** + * Returns true if the provided aggregation command has a $out stage. + */ + static bool aggSupportsWriteConcern(const BSONObj& cmd); + const boost::intrusive_ptr& getContext() const { return pCtx; } diff --git a/src/mongo/db/repl/master_slave.cpp b/src/mongo/db/repl/master_slave.cpp index dd60bd07c6f..e716cf58b3e 100644 --- a/src/mongo/db/repl/master_slave.cpp +++ b/src/mongo/db/repl/master_slave.cpp @@ -356,6 +356,9 @@ public: h << "internal"; } HandshakeCmd() : Command("handshake") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } diff --git a/src/mongo/db/repl/repl_set_command.h b/src/mongo/db/repl/repl_set_command.h index 61e2b9b07f3..03320087835 100644 --- a/src/mongo/db/repl/repl_set_command.h +++ b/src/mongo/db/repl/repl_set_command.h @@ -55,6 +55,9 @@ protected: return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp index 02a7a6f8a81..9852803a3b3 100644 --- a/src/mongo/db/repl/replication_info.cpp +++ b/src/mongo/db/repl/replication_info.cpp @@ -210,6 +210,9 @@ public: "--slave in simple master/slave setups.\n"; help << "{ isMaster : 1 }"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) {} // No auth required diff --git a/src/mongo/db/repl/resync.cpp b/src/mongo/db/repl/resync.cpp index 1b53ac4d162..674b43f969a 100644 --- a/src/mongo/db/repl/resync.cpp +++ b/src/mongo/db/repl/resync.cpp @@ -49,6 +49,9 @@ public: virtual bool adminOnly() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/db/s/check_sharding_index_command.cpp b/src/mongo/db/s/check_sharding_index_command.cpp index aa10e22d433..de1dbf6487c 100644 --- a/src/mongo/db/s/check_sharding_index_command.cpp +++ b/src/mongo/db/s/check_sharding_index_command.cpp @@ -58,6 +58,10 @@ public: help << "Internal command.\n"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return false; } diff --git a/src/mongo/db/s/cleanup_orphaned_cmd.cpp b/src/mongo/db/s/cleanup_orphaned_cmd.cpp index 9c2a6477186..9421784b28c 100644 --- a/src/mongo/db/s/cleanup_orphaned_cmd.cpp +++ b/src/mongo/db/s/cleanup_orphaned_cmd.cpp @@ -188,6 +188,9 @@ public: return Status::OK(); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } // Input static BSONField nsField; diff --git a/src/mongo/db/s/get_shard_version_command.cpp b/src/mongo/db/s/get_shard_version_command.cpp index 9a41aa5a70b..1331bbde061 100644 --- a/src/mongo/db/s/get_shard_version_command.cpp +++ b/src/mongo/db/s/get_shard_version_command.cpp @@ -58,6 +58,10 @@ public: help << " example: { getShardVersion : 'alleyinsider.foo' } "; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const override { return false; } diff --git a/src/mongo/db/s/merge_chunks_command.cpp b/src/mongo/db/s/merge_chunks_command.cpp index 0f73ff2c2a9..b65d0931149 100644 --- a/src/mongo/db/s/merge_chunks_command.cpp +++ b/src/mongo/db/s/merge_chunks_command.cpp @@ -383,6 +383,9 @@ public: bool slaveOk() const override { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } // Required static BSONField nsField; 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 e414bcc4d8e..eb72772db1e 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 @@ -115,6 +115,10 @@ public: h << "internal"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return false; } @@ -175,6 +179,10 @@ public: h << "internal"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return false; } 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 27bacbca200..3fdc0b7bcb3 100644 --- a/src/mongo/db/s/migration_destination_manager_legacy_commands.cpp +++ b/src/mongo/db/s/migration_destination_manager_legacy_commands.cpp @@ -73,6 +73,11 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + // This is required to be true to support moveChunk. + return true; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { @@ -196,6 +201,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { @@ -233,6 +242,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { @@ -275,6 +288,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/db/s/move_chunk_command.cpp b/src/mongo/db/s/move_chunk_command.cpp index 48dc6285345..a3295e974b7 100644 --- a/src/mongo/db/s/move_chunk_command.cpp +++ b/src/mongo/db/s/move_chunk_command.cpp @@ -150,6 +150,10 @@ public: return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) override { diff --git a/src/mongo/db/s/set_shard_version_command.cpp b/src/mongo/db/s/set_shard_version_command.cpp index b527679fb35..acdb419538d 100644 --- a/src/mongo/db/s/set_shard_version_command.cpp +++ b/src/mongo/db/s/set_shard_version_command.cpp @@ -76,6 +76,10 @@ public: return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) override { diff --git a/src/mongo/db/s/sharding_state_command.cpp b/src/mongo/db/s/sharding_state_command.cpp index cbf54e69281..672bb581ba7 100644 --- a/src/mongo/db/s/sharding_state_command.cpp +++ b/src/mongo/db/s/sharding_state_command.cpp @@ -47,6 +47,10 @@ class ShardingStateCmd : public Command { public: ShardingStateCmd() : Command("shardingState") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const override { return false; } diff --git a/src/mongo/db/s/split_chunk_command.cpp b/src/mongo/db/s/split_chunk_command.cpp index a63dd7af121..7e3473299f3 100644 --- a/src/mongo/db/s/split_chunk_command.cpp +++ b/src/mongo/db/s/split_chunk_command.cpp @@ -113,6 +113,10 @@ public: "splitKeys : [ {a:150} , ... ]}"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const override { return false; } diff --git a/src/mongo/db/s/split_vector_command.cpp b/src/mongo/db/s/split_vector_command.cpp index d0dcf35dbcc..9c4dccfac96 100644 --- a/src/mongo/db/s/split_vector_command.cpp +++ b/src/mongo/db/s/split_vector_command.cpp @@ -69,6 +69,9 @@ BSONObj prettyKey(const BSONObj& keyPattern, const BSONObj& key) { class SplitVector : public Command { public: SplitVector() : Command("splitVector", false) {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return false; } diff --git a/src/mongo/db/s/unset_sharding_command.cpp b/src/mongo/db/s/unset_sharding_command.cpp index 35a39c9da9f..9aa63819135 100644 --- a/src/mongo/db/s/unset_sharding_command.cpp +++ b/src/mongo/db/s/unset_sharding_command.cpp @@ -52,6 +52,10 @@ public: help << "internal"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool adminOnly() const override { return true; } diff --git a/src/mongo/db/storage/mmap_v1/journal_latency_test_cmd.cpp b/src/mongo/db/storage/mmap_v1/journal_latency_test_cmd.cpp index 743d3d9f2dc..a67ecd85f3d 100644 --- a/src/mongo/db/storage/mmap_v1/journal_latency_test_cmd.cpp +++ b/src/mongo/db/storage/mmap_v1/journal_latency_test_cmd.cpp @@ -71,6 +71,9 @@ public: virtual bool slaveOk() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool adminOnly() const { return true; } diff --git a/src/mongo/db/write_concern.cpp b/src/mongo/db/write_concern.cpp index fd65f66862a..b6d23b63aa2 100644 --- a/src/mongo/db/write_concern.cpp +++ b/src/mongo/db/write_concern.cpp @@ -74,39 +74,33 @@ const std::string kLocalDB = "local"; StatusWith extractWriteConcern(OperationContext* txn, const BSONObj& cmdObj, - const std::string& dbName) { + const std::string& dbName, + const bool supportsWriteConcern) { // The default write concern if empty is w : 1 // Specifying w : 0 is/was allowed, but is interpreted identically to w : 1 WriteConcernOptions writeConcern = repl::getGlobalReplicationCoordinator()->getGetLastErrorDefault(); - if (writeConcern.wNumNodes == 0 && writeConcern.wMode.empty()) { - writeConcern.wNumNodes = 1; - } - BSONElement writeConcernElement; - Status wcStatus = bsonExtractTypedField(cmdObj, "writeConcern", Object, &writeConcernElement); - if (!wcStatus.isOK()) { - if (wcStatus == ErrorCodes::NoSuchKey) { - // Return default write concern if no write concern is given. - return writeConcern; - } - return wcStatus; + auto wcResult = WriteConcernOptions::extractWCFromCommand(cmdObj, dbName, writeConcern); + if (!wcResult.isOK()) { + return wcResult.getStatus(); } - - BSONObj writeConcernObj = writeConcernElement.Obj(); - // Empty write concern is interpreted to default. - if (writeConcernObj.isEmpty()) { - return writeConcern; - } - - wcStatus = writeConcern.parse(writeConcernObj); - if (!wcStatus.isOK()) { - return wcStatus; - } - - wcStatus = validateWriteConcern(txn, writeConcern, dbName); - if (!wcStatus.isOK()) { - return wcStatus; + writeConcern = wcResult.getValue(); + + // We didn't use the default, so the user supplied their own writeConcern. + if (!wcResult.getValue().usedDefault) { + // If it supports writeConcern and does not use the default, validate the writeConcern. + if (supportsWriteConcern) { + Status wcStatus = validateWriteConcern(txn, writeConcern, dbName); + if (!wcStatus.isOK()) { + return wcStatus; + } + } else { + // This command doesn't do writes so it should not be passed a writeConcern. + // If we did not use the default writeConcern, one was provided when it shouldn't have + // been by the user. + return Status(ErrorCodes::InvalidOptions, "Command does not support writeConcern"); + } } return writeConcern; diff --git a/src/mongo/db/write_concern.h b/src/mongo/db/write_concern.h index 873e6114af1..653871ffe12 100644 --- a/src/mongo/db/write_concern.h +++ b/src/mongo/db/write_concern.h @@ -56,7 +56,8 @@ void setupSynchronousCommit(OperationContext* txn); */ StatusWith extractWriteConcern(OperationContext* txn, const BSONObj& cmdObj, - const std::string& dbName); + const std::string& dbName, + const bool supportsWriteConcern); /** * Verifies that a WriteConcern is valid for this particular host. diff --git a/src/mongo/db/write_concern_options.cpp b/src/mongo/db/write_concern_options.cpp index d0f003fb383..2b0c9abef23 100644 --- a/src/mongo/db/write_concern_options.cpp +++ b/src/mongo/db/write_concern_options.cpp @@ -30,6 +30,7 @@ #include "mongo/db/write_concern_options.h" #include "mongo/base/status.h" +#include "mongo/bson/util/bson_extract.h" #include "mongo/db/field_parser.h" namespace mongo { @@ -119,6 +120,39 @@ Status WriteConcernOptions::parse(const BSONObj& obj) { return Status::OK(); } +StatusWith WriteConcernOptions::extractWCFromCommand( + const BSONObj& cmdObj, const std::string& dbName, const WriteConcernOptions& defaultWC) { + WriteConcernOptions writeConcern = defaultWC; + writeConcern.usedDefault = true; + if (writeConcern.wNumNodes == 0 && writeConcern.wMode.empty()) { + writeConcern.wNumNodes = 1; + } + + BSONElement writeConcernElement; + Status wcStatus = bsonExtractTypedField(cmdObj, "writeConcern", Object, &writeConcernElement); + if (!wcStatus.isOK()) { + if (wcStatus == ErrorCodes::NoSuchKey) { + // Return default write concern if no write concern is given. + return writeConcern; + } + return wcStatus; + } + + BSONObj writeConcernObj = writeConcernElement.Obj(); + // Empty write concern is interpreted to default. + if (writeConcernObj.isEmpty()) { + return writeConcern; + } + + wcStatus = writeConcern.parse(writeConcernObj); + writeConcern.usedDefault = false; + if (!wcStatus.isOK()) { + return wcStatus; + } + + return writeConcern; +} + BSONObj WriteConcernOptions::toBSON() const { BSONObjBuilder builder; diff --git a/src/mongo/db/write_concern_options.h b/src/mongo/db/write_concern_options.h index 0c187ed2887..f969fc08031 100644 --- a/src/mongo/db/write_concern_options.h +++ b/src/mongo/db/write_concern_options.h @@ -63,6 +63,15 @@ public: Status parse(const BSONObj& obj); + /** + * Attempts to extract a writeConcern from cmdObj. + * Verifies that the writeConcern is of type Object (BSON type). + */ + static StatusWith extractWCFromCommand( + const BSONObj& cmdObj, + const std::string& dbName, + const WriteConcernOptions& defaultWC = WriteConcernOptions()); + /** * 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. @@ -95,6 +104,9 @@ public: // Timeout in milliseconds. int wTimeout; + + // True if the default write concern was used. + bool usedDefault = false; }; } // namespace mongo diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript index 133f5cbe26c..da05a551b7a 100644 --- a/src/mongo/rpc/SConscript +++ b/src/mongo/rpc/SConscript @@ -172,7 +172,8 @@ env.CppUnitTest( ], LIBDEPS=[ 'rpc', - '$BUILD_DIR/mongo/client/clientdriver' + '$BUILD_DIR/mongo/client/clientdriver', + '$BUILD_DIR/mongo/db/write_concern_options', ], ) diff --git a/src/mongo/s/client/shard_connection.cpp b/src/mongo/s/client/shard_connection.cpp index 6e3dd46e6d2..4be29904827 100644 --- a/src/mongo/s/client/shard_connection.cpp +++ b/src/mongo/s/client/shard_connection.cpp @@ -93,6 +93,9 @@ public: virtual void help(stringstream& help) const { help << "stats about the shard connection pool"; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool slaveOk() const { return true; } diff --git a/src/mongo/s/commands/cluster_add_shard_cmd.cpp b/src/mongo/s/commands/cluster_add_shard_cmd.cpp index 2e50407f0a0..e3c18d4da72 100644 --- a/src/mongo/s/commands/cluster_add_shard_cmd.cpp +++ b/src/mongo/s/commands/cluster_add_shard_cmd.cpp @@ -58,6 +58,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "add a new shard to the system"; } diff --git a/src/mongo/s/commands/cluster_count_cmd.cpp b/src/mongo/s/commands/cluster_count_cmd.cpp index fa20f4f1447..acd426d7f2f 100644 --- a/src/mongo/s/commands/cluster_count_cmd.cpp +++ b/src/mongo/s/commands/cluster_count_cmd.cpp @@ -83,6 +83,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/s/commands/cluster_current_op.cpp b/src/mongo/s/commands/cluster_current_op.cpp index 6b35d491b11..d75f881c7c7 100644 --- a/src/mongo/s/commands/cluster_current_op.cpp +++ b/src/mongo/s/commands/cluster_current_op.cpp @@ -71,6 +71,10 @@ public: return isAuthorized ? Status::OK() : Status(ErrorCodes::Unauthorized, "Unauthorized"); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + void aggregateResults(const std::vector& results, BSONObjBuilder& output) final { // Each shard responds with a document containing an array of subdocuments. // Each subdocument represents an operation running on that shard. diff --git a/src/mongo/s/commands/cluster_db_stats_cmd.cpp b/src/mongo/s/commands/cluster_db_stats_cmd.cpp index 3378de75ca5..4661cac2b7b 100644 --- a/src/mongo/s/commands/cluster_db_stats_cmd.cpp +++ b/src/mongo/s/commands/cluster_db_stats_cmd.cpp @@ -48,6 +48,10 @@ public: out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void aggregateResults(const vector& results, BSONObjBuilder& output) { long long objects = 0; long long unscaledDataSize = 0; diff --git a/src/mongo/s/commands/cluster_drop_database_cmd.cpp b/src/mongo/s/commands/cluster_drop_database_cmd.cpp index 9577e9327fb..b2875998b71 100644 --- a/src/mongo/s/commands/cluster_drop_database_cmd.cpp +++ b/src/mongo/s/commands/cluster_drop_database_cmd.cpp @@ -57,6 +57,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/s/commands/cluster_enable_sharding_cmd.cpp b/src/mongo/s/commands/cluster_enable_sharding_cmd.cpp index 7023252ec0c..49a07183f1e 100644 --- a/src/mongo/s/commands/cluster_enable_sharding_cmd.cpp +++ b/src/mongo/s/commands/cluster_enable_sharding_cmd.cpp @@ -61,6 +61,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "Enable sharding for a database. " << "(Use 'shardcollection' command afterwards.)\n" diff --git a/src/mongo/s/commands/cluster_explain_cmd.cpp b/src/mongo/s/commands/cluster_explain_cmd.cpp index 2801984c8c7..a60bd3c197d 100644 --- a/src/mongo/s/commands/cluster_explain_cmd.cpp +++ b/src/mongo/s/commands/cluster_explain_cmd.cpp @@ -56,6 +56,10 @@ class ClusterExplainCmd : public Command { public: ClusterExplainCmd() : Command("explain") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + /** * Running an explain on a secondary requires explicitly setting slaveOk. */ diff --git a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp index 0b69571e7e2..e6accb504c6 100644 --- a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp +++ b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp @@ -67,6 +67,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp index f8a21610032..cc37be5606b 100644 --- a/src/mongo/s/commands/cluster_find_cmd.cpp +++ b/src/mongo/s/commands/cluster_find_cmd.cpp @@ -58,6 +58,10 @@ public: ClusterFindCmd() : Command("find") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const final { return false; } diff --git a/src/mongo/s/commands/cluster_flush_router_config_cmd.cpp b/src/mongo/s/commands/cluster_flush_router_config_cmd.cpp index 47e9d56afcd..d7053eabeb5 100644 --- a/src/mongo/s/commands/cluster_flush_router_config_cmd.cpp +++ b/src/mongo/s/commands/cluster_flush_router_config_cmd.cpp @@ -49,6 +49,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "flush all router config"; } diff --git a/src/mongo/s/commands/cluster_fsync_cmd.cpp b/src/mongo/s/commands/cluster_fsync_cmd.cpp index ec14aa91db3..4a8b20df062 100644 --- a/src/mongo/s/commands/cluster_fsync_cmd.cpp +++ b/src/mongo/s/commands/cluster_fsync_cmd.cpp @@ -51,6 +51,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "invoke fsync on all shards belonging to the cluster"; } diff --git a/src/mongo/s/commands/cluster_get_last_error_cmd.cpp b/src/mongo/s/commands/cluster_get_last_error_cmd.cpp index f59cb038e64..25f073a831e 100644 --- a/src/mongo/s/commands/cluster_get_last_error_cmd.cpp +++ b/src/mongo/s/commands/cluster_get_last_error_cmd.cpp @@ -46,6 +46,10 @@ public: GetLastErrorCmd() : Command("getLastError", false, "getlasterror") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return true; } diff --git a/src/mongo/s/commands/cluster_get_prev_error_cmd.cpp b/src/mongo/s/commands/cluster_get_prev_error_cmd.cpp index 473ad25c9ea..7eebe3bf5df 100644 --- a/src/mongo/s/commands/cluster_get_prev_error_cmd.cpp +++ b/src/mongo/s/commands/cluster_get_prev_error_cmd.cpp @@ -43,6 +43,10 @@ public: GetPrevErrorCmd() : Command("getPrevError", false, "getpreverror") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return true; } diff --git a/src/mongo/s/commands/cluster_get_shard_map_cmd.cpp b/src/mongo/s/commands/cluster_get_shard_map_cmd.cpp index 78796a4ce06..48bbdb2a9d4 100644 --- a/src/mongo/s/commands/cluster_get_shard_map_cmd.cpp +++ b/src/mongo/s/commands/cluster_get_shard_map_cmd.cpp @@ -43,6 +43,10 @@ public: CmdGetShardMap() : Command("getShardMap") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return true; } diff --git a/src/mongo/s/commands/cluster_get_shard_version_cmd.cpp b/src/mongo/s/commands/cluster_get_shard_version_cmd.cpp index 20791505430..5cb36e28f93 100644 --- a/src/mongo/s/commands/cluster_get_shard_version_cmd.cpp +++ b/src/mongo/s/commands/cluster_get_shard_version_cmd.cpp @@ -58,6 +58,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << " example: { getShardVersion : 'alleyinsider.foo' } "; } diff --git a/src/mongo/s/commands/cluster_getmore_cmd.cpp b/src/mongo/s/commands/cluster_getmore_cmd.cpp index ec8d444f769..3af7d0bbd84 100644 --- a/src/mongo/s/commands/cluster_getmore_cmd.cpp +++ b/src/mongo/s/commands/cluster_getmore_cmd.cpp @@ -51,6 +51,10 @@ public: ClusterGetMoreCmd() : Command("getMore") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const final { return true; } diff --git a/src/mongo/s/commands/cluster_index_filter_cmd.cpp b/src/mongo/s/commands/cluster_index_filter_cmd.cpp index 51fc87afa74..5a342cba195 100644 --- a/src/mongo/s/commands/cluster_index_filter_cmd.cpp +++ b/src/mongo/s/commands/cluster_index_filter_cmd.cpp @@ -69,6 +69,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + void help(stringstream& ss) const { ss << _helpText; } diff --git a/src/mongo/s/commands/cluster_is_db_grid_cmd.cpp b/src/mongo/s/commands/cluster_is_db_grid_cmd.cpp index 0dc5a666ab3..5d97540a876 100644 --- a/src/mongo/s/commands/cluster_is_db_grid_cmd.cpp +++ b/src/mongo/s/commands/cluster_is_db_grid_cmd.cpp @@ -38,6 +38,10 @@ public: IsDbGridCmd() : Command("isdbgrid") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return true; } diff --git a/src/mongo/s/commands/cluster_is_master_cmd.cpp b/src/mongo/s/commands/cluster_is_master_cmd.cpp index e88e8de9e37..2ed1694372d 100644 --- a/src/mongo/s/commands/cluster_is_master_cmd.cpp +++ b/src/mongo/s/commands/cluster_is_master_cmd.cpp @@ -41,6 +41,10 @@ public: CmdIsMaster() : Command("isMaster", false, "ismaster") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return true; } diff --git a/src/mongo/s/commands/cluster_kill_op.cpp b/src/mongo/s/commands/cluster_kill_op.cpp index 92a98d851d3..31590d3c5a9 100644 --- a/src/mongo/s/commands/cluster_kill_op.cpp +++ b/src/mongo/s/commands/cluster_kill_op.cpp @@ -56,6 +56,10 @@ public: ClusterKillOpCommand() : Command("killOp") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool slaveOk() const final { return true; } diff --git a/src/mongo/s/commands/cluster_list_databases_cmd.cpp b/src/mongo/s/commands/cluster_list_databases_cmd.cpp index 6b50526cee6..a8fb599871f 100644 --- a/src/mongo/s/commands/cluster_list_databases_cmd.cpp +++ b/src/mongo/s/commands/cluster_list_databases_cmd.cpp @@ -66,6 +66,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "list databases in a cluster"; } diff --git a/src/mongo/s/commands/cluster_list_shards_cmd.cpp b/src/mongo/s/commands/cluster_list_shards_cmd.cpp index 3c7d234d9df..de6d1ec82b9 100644 --- a/src/mongo/s/commands/cluster_list_shards_cmd.cpp +++ b/src/mongo/s/commands/cluster_list_shards_cmd.cpp @@ -52,6 +52,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "list all shards of the system"; } diff --git a/src/mongo/s/commands/cluster_map_reduce_cmd.cpp b/src/mongo/s/commands/cluster_map_reduce_cmd.cpp index fb82df1b7c6..662f7278367 100644 --- a/src/mongo/s/commands/cluster_map_reduce_cmd.cpp +++ b/src/mongo/s/commands/cluster_map_reduce_cmd.cpp @@ -165,6 +165,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return mr::mrSupportsWriteConcern(cmd); + } + virtual void help(std::stringstream& help) const { help << "Runs the sharded map/reduce command"; } diff --git a/src/mongo/s/commands/cluster_merge_chunks_cmd.cpp b/src/mongo/s/commands/cluster_merge_chunks_cmd.cpp index a530badf651..2138151321e 100644 --- a/src/mongo/s/commands/cluster_merge_chunks_cmd.cpp +++ b/src/mongo/s/commands/cluster_merge_chunks_cmd.cpp @@ -85,6 +85,9 @@ public: virtual bool slaveOk() const { return false; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } // Required static BSONField nsField; diff --git a/src/mongo/s/commands/cluster_move_chunk_cmd.cpp b/src/mongo/s/commands/cluster_move_chunk_cmd.cpp index afd761294f6..eebfa60b33f 100644 --- a/src/mongo/s/commands/cluster_move_chunk_cmd.cpp +++ b/src/mongo/s/commands/cluster_move_chunk_cmd.cpp @@ -69,6 +69,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(std::stringstream& help) const { help << "Example: move chunk that contains the doc {num : 7} to shard001\n" << " { movechunk : 'test.foo' , find : { num : 7 } , to : 'shard0001' }\n" diff --git a/src/mongo/s/commands/cluster_move_primary_cmd.cpp b/src/mongo/s/commands/cluster_move_primary_cmd.cpp index 61f8d3c2d5a..a09ddf30dd2 100644 --- a/src/mongo/s/commands/cluster_move_primary_cmd.cpp +++ b/src/mongo/s/commands/cluster_move_primary_cmd.cpp @@ -72,6 +72,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(std::stringstream& help) const { help << " example: { moveprimary : 'foo' , to : 'localhost:9999' }"; } diff --git a/src/mongo/s/commands/cluster_netstat_cmd.cpp b/src/mongo/s/commands/cluster_netstat_cmd.cpp index 6e19901c336..56db7cc94d8 100644 --- a/src/mongo/s/commands/cluster_netstat_cmd.cpp +++ b/src/mongo/s/commands/cluster_netstat_cmd.cpp @@ -49,6 +49,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << " shows status/reachability of servers in the cluster"; } diff --git a/src/mongo/s/commands/cluster_pipeline_cmd.cpp b/src/mongo/s/commands/cluster_pipeline_cmd.cpp index b3401a8c52f..3b558c597ca 100644 --- a/src/mongo/s/commands/cluster_pipeline_cmd.cpp +++ b/src/mongo/s/commands/cluster_pipeline_cmd.cpp @@ -81,6 +81,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return Pipeline::aggSupportsWriteConcern(cmd); + } + virtual void help(std::stringstream& help) const { help << "Runs the sharded aggregation command"; } diff --git a/src/mongo/s/commands/cluster_plan_cache_cmd.cpp b/src/mongo/s/commands/cluster_plan_cache_cmd.cpp index 839265f0a70..fe9bbad0b14 100644 --- a/src/mongo/s/commands/cluster_plan_cache_cmd.cpp +++ b/src/mongo/s/commands/cluster_plan_cache_cmd.cpp @@ -62,6 +62,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + void help(stringstream& ss) const { ss << _helpText; } diff --git a/src/mongo/s/commands/cluster_profile_cmd.cpp b/src/mongo/s/commands/cluster_profile_cmd.cpp index a93bcaea43f..dcdc63d4bb6 100644 --- a/src/mongo/s/commands/cluster_profile_cmd.cpp +++ b/src/mongo/s/commands/cluster_profile_cmd.cpp @@ -46,6 +46,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector* out) { diff --git a/src/mongo/s/commands/cluster_remove_shard_cmd.cpp b/src/mongo/s/commands/cluster_remove_shard_cmd.cpp index 7600808ef04..c2aef2fd0e6 100644 --- a/src/mongo/s/commands/cluster_remove_shard_cmd.cpp +++ b/src/mongo/s/commands/cluster_remove_shard_cmd.cpp @@ -63,6 +63,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "remove a shard from the system."; } diff --git a/src/mongo/s/commands/cluster_repair_database_cmd.cpp b/src/mongo/s/commands/cluster_repair_database_cmd.cpp index 5383b5688bf..9affce5c598 100644 --- a/src/mongo/s/commands/cluster_repair_database_cmd.cpp +++ b/src/mongo/s/commands/cluster_repair_database_cmd.cpp @@ -45,6 +45,10 @@ public: out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + } clusterRepairDatabaseCmd; } // namespace diff --git a/src/mongo/s/commands/cluster_repl_set_get_status_cmd.cpp b/src/mongo/s/commands/cluster_repl_set_get_status_cmd.cpp index 3695f978883..f019370818f 100644 --- a/src/mongo/s/commands/cluster_repl_set_get_status_cmd.cpp +++ b/src/mongo/s/commands/cluster_repl_set_get_status_cmd.cpp @@ -49,6 +49,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "Not supported through mongos"; } diff --git a/src/mongo/s/commands/cluster_reset_error_cmd.cpp b/src/mongo/s/commands/cluster_reset_error_cmd.cpp index 2da2656efa7..899d51b658e 100644 --- a/src/mongo/s/commands/cluster_reset_error_cmd.cpp +++ b/src/mongo/s/commands/cluster_reset_error_cmd.cpp @@ -45,6 +45,10 @@ public: CmdShardingResetError() : Command("resetError", false, "reseterror") {} + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool slaveOk() const { return true; } diff --git a/src/mongo/s/commands/cluster_shard_collection_cmd.cpp b/src/mongo/s/commands/cluster_shard_collection_cmd.cpp index a2390d91c90..b6b99fd9fe4 100644 --- a/src/mongo/s/commands/cluster_shard_collection_cmd.cpp +++ b/src/mongo/s/commands/cluster_shard_collection_cmd.cpp @@ -77,6 +77,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "Shard a collection. Requires key. Optional unique." << " Sharding must already be enabled for the database.\n" diff --git a/src/mongo/s/commands/cluster_split_cmd.cpp b/src/mongo/s/commands/cluster_split_cmd.cpp index b4f6f0f03c0..550d24d9246 100644 --- a/src/mongo/s/commands/cluster_split_cmd.cpp +++ b/src/mongo/s/commands/cluster_split_cmd.cpp @@ -68,6 +68,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << " example: - split the shard that contains give key\n" << " { split : 'alleyinsider.blog.posts' , find : { ts : 1 } }\n" diff --git a/src/mongo/s/commands/cluster_user_management_commands.cpp b/src/mongo/s/commands/cluster_user_management_commands.cpp index 505dc9225c6..a96306ee6ed 100644 --- a/src/mongo/s/commands/cluster_user_management_commands.cpp +++ b/src/mongo/s/commands/cluster_user_management_commands.cpp @@ -63,6 +63,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Adds a user to the system"; } @@ -98,6 +102,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Used to update a user, for example to change its password"; } @@ -144,6 +152,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Drops a single user."; } @@ -188,6 +200,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Drops all users for a single database."; } @@ -225,6 +241,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Grants roles to a user."; } @@ -270,6 +290,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Revokes roles from a user."; } @@ -317,6 +341,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + CmdUsersInfo() : Command("usersInfo") {} virtual void help(stringstream& ss) const { @@ -349,6 +377,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Adds a role to the system"; } @@ -380,6 +412,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Used to update a role"; } @@ -417,6 +453,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Grants privileges to a role"; } @@ -454,6 +494,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Revokes privileges from a role"; } @@ -491,6 +535,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Grants roles to another role."; } @@ -528,6 +576,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Revokes roles from another role."; } @@ -565,6 +617,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Drops a single role. Before deleting the role completely it must remove it " "from any users or roles that reference it. If any errors occur in the middle " @@ -605,6 +661,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Drops all roles from the given database. Before deleting the roles completely " "it must remove them from any users or other roles that reference them. If any " @@ -650,6 +710,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(stringstream& ss) const { ss << "Returns information about roles."; } @@ -684,6 +748,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(stringstream& ss) const { ss << "Invalidates the in-memory cache of user information"; } @@ -727,6 +795,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual bool adminOnly() const { return true; } @@ -804,6 +876,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual void help(stringstream& ss) const { ss << "Upgrades the auth data storage schema"; } diff --git a/src/mongo/s/commands/cluster_whats_my_uri_cmd.cpp b/src/mongo/s/commands/cluster_whats_my_uri_cmd.cpp index ab25514c0ef..9a029d0f861 100644 --- a/src/mongo/s/commands/cluster_whats_my_uri_cmd.cpp +++ b/src/mongo/s/commands/cluster_whats_my_uri_cmd.cpp @@ -43,6 +43,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual void help(std::stringstream& help) const { help << "{whatsmyuri:1}"; } diff --git a/src/mongo/s/commands/cluster_write_cmd.cpp b/src/mongo/s/commands/cluster_write_cmd.cpp index 01f0b79662e..afc5b126013 100644 --- a/src/mongo/s/commands/cluster_write_cmd.cpp +++ b/src/mongo/s/commands/cluster_write_cmd.cpp @@ -74,6 +74,10 @@ public: } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { diff --git a/src/mongo/s/commands/commands_public.cpp b/src/mongo/s/commands/commands_public.cpp index 933877fc369..96293f9d698 100644 --- a/src/mongo/s/commands/commands_public.cpp +++ b/src/mongo/s/commands/commands_public.cpp @@ -252,6 +252,9 @@ public: actions.addAction(ActionType::dropIndex); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } } dropIndexesCmd; class CreateIndexesCmd : public AllShardsCollectionCommand { @@ -361,6 +364,10 @@ public: out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + } createIndexesCmd; class ReIndexCmd : public AllShardsCollectionCommand { @@ -373,6 +380,10 @@ public: actions.addAction(ActionType::reIndex); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } } reIndexCmd; class CollectionModCmd : public AllShardsCollectionCommand { @@ -385,6 +396,10 @@ public: actions.addAction(ActionType::collMod); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } } collectionModCmd; @@ -399,6 +414,10 @@ public: out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -476,6 +495,9 @@ public: return Status(ErrorCodes::Unauthorized, "unauthorized"); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -505,6 +527,10 @@ public: out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -537,7 +563,6 @@ public: return true; } - } dropCmd; class RenameCollectionCmd : public PublicGridCommand { @@ -551,6 +576,9 @@ public: virtual bool adminOnly() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -590,6 +618,9 @@ public: const BSONObj& cmdObj) { return copydb::checkAuthForCopydbCommand(client, dbname, cmdObj); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } bool run(OperationContext* txn, const string& dbName, @@ -637,7 +668,6 @@ public: return adminPassthrough(txn, confTo, fixed, result); } } - } clusterCopyDBCmd; class CollectionStats : public PublicGridCommand { @@ -651,6 +681,10 @@ public: out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -815,6 +849,9 @@ public: actions.addAction(ActionType::find); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -896,6 +933,10 @@ public: out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + } convertToCappedCmd; class GroupCmd : public NotAllowedOnShardedCollectionCmd { @@ -909,6 +950,10 @@ public: out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + virtual bool passOptions() const { return true; } @@ -993,6 +1038,9 @@ public: virtual bool passOptions() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { @@ -1038,6 +1086,9 @@ public: actions.addAction(ActionType::find); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } bool run(OperationContext* txn, const string& dbName, @@ -1166,6 +1217,10 @@ public: out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), ActionType::find)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -1293,6 +1348,9 @@ public: actions.addAction(ActionType::find); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } bool run(OperationContext* txn, const string& dbName, @@ -1414,6 +1472,9 @@ public: // applyOps can do pretty much anything, so require all privileges. RoleGraph::generateUniversalPrivileges(out); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } virtual bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -1435,6 +1496,9 @@ public: actions.addAction(ActionType::compact); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -1455,6 +1519,9 @@ public: // $eval can do pretty much anything, so require all privileges. RoleGraph::generateUniversalPrivileges(out); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -1500,6 +1567,10 @@ public: str::stream() << "Not authorized to create users on db: " << dbname); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -1539,6 +1610,10 @@ public: << "Not authorized to list indexes on collection: " << ns.coll()); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + bool run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, @@ -1567,6 +1642,9 @@ public: const BSONObj& cmdObj) { return Status::OK(); } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } virtual bool run(OperationContext* txn, const string& dbname, diff --git a/src/mongo/s/config.cpp b/src/mongo/s/config.cpp index 26f1eb2b109..58242f633b9 100644 --- a/src/mongo/s/config.cpp +++ b/src/mongo/s/config.cpp @@ -580,7 +580,7 @@ bool DBConfig::dropDatabase(OperationContext* txn, string& errmsg) { const auto shard = grid.shardRegistry()->getShard(txn, _primaryId); ScopedDbConnection conn(shard->getConnString(), 30.0); BSONObj res; - if (!conn->dropDatabase(_name, &res)) { + if (!conn->dropDatabase(_name, txn->getWriteConcern(), &res)) { errmsg = res.toString(); return 0; } @@ -596,7 +596,7 @@ bool DBConfig::dropDatabase(OperationContext* txn, string& errmsg) { ScopedDbConnection conn(shard->getConnString(), 30.0); BSONObj res; - if (!conn->dropDatabase(_name, &res)) { + if (!conn->dropDatabase(_name, txn->getWriteConcern(), &res)) { errmsg = res.toString(); return 0; } diff --git a/src/mongo/tools/SConscript b/src/mongo/tools/SConscript index b514a3d10ac..b9c42f4f78f 100644 --- a/src/mongo/tools/SConscript +++ b/src/mongo/tools/SConscript @@ -15,6 +15,7 @@ mongobridge = env.Program( "$BUILD_DIR/mongo/client/clientdriver", "$BUILD_DIR/mongo/db/commands/test_commands_enabled", "$BUILD_DIR/mongo/db/service_context", + "$BUILD_DIR/mongo/db/write_concern_options", "$BUILD_DIR/mongo/util/net/network", "$BUILD_DIR/mongo/util/ntservice_mock", "$BUILD_DIR/mongo/util/signal_handlers", -- cgit v1.2.1