summaryrefslogtreecommitdiff
path: root/src/mongo/s/commands/cluster_write_cmd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/s/commands/cluster_write_cmd.cpp')
-rw-r--r--src/mongo/s/commands/cluster_write_cmd.cpp252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/mongo/s/commands/cluster_write_cmd.cpp b/src/mongo/s/commands/cluster_write_cmd.cpp
new file mode 100644
index 00000000000..70958cbb32f
--- /dev/null
+++ b/src/mongo/s/commands/cluster_write_cmd.cpp
@@ -0,0 +1,252 @@
+/**
+ * Copyright (C) 2013 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/base/init.h"
+#include "mongo/base/error_codes.h"
+#include "mongo/db/client_basic.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/commands/write_commands/write_commands_common.h"
+#include "mongo/s/cluster_write.h"
+#include "mongo/db/lasterror.h"
+#include "mongo/db/stats/counters.h"
+#include "mongo/s/client_info.h"
+#include "mongo/s/write_ops/batched_command_request.h"
+#include "mongo/s/write_ops/batched_command_response.h"
+#include "mongo/s/write_ops/batch_upconvert.h"
+#include "mongo/db/stats/counters.h"
+
+namespace mongo {
+
+ /**
+ * Base class for mongos write commands. Cluster write commands support batch writes and write
+ * concern, and return per-item error information. All cluster write commands use the entry
+ * point ClusterWriteCmd::run().
+ *
+ * Batch execution (targeting and dispatching) is performed by the BatchWriteExec class.
+ */
+ class ClusterWriteCmd : public Command {
+ MONGO_DISALLOW_COPYING(ClusterWriteCmd);
+ public:
+
+ virtual ~ClusterWriteCmd() {
+ }
+
+ bool logTheOp() {
+ return false;
+ }
+
+ bool slaveOk() const {
+ return false;
+ }
+
+ LockType locktype() const {
+ return Command::NONE;
+ }
+
+ Status checkAuthForCommand( ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj ) {
+
+ Status status = auth::checkAuthForWriteCommand( client->getAuthorizationSession(),
+ _writeType,
+ NamespaceString( parseNs( dbname,
+ cmdObj ) ),
+ cmdObj );
+
+ // TODO: Remove this when we standardize GLE reporting from commands
+ if ( !status.isOK() ) {
+ setLastError( status.code(), status.reason().c_str() );
+ }
+
+ return status;
+ }
+
+ // Cluster write command entry point.
+ bool run( const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl );
+
+ protected:
+
+ /**
+ * Instantiates a command that can be invoked by "name", which will be capable of issuing
+ * write batches of type "writeType", and will require privilege "action" to run.
+ */
+ ClusterWriteCmd( const StringData& name, BatchedCommandRequest::BatchType writeType ) :
+ Command( name ), _writeType( writeType ) {
+ }
+
+ private:
+
+ // Type of batch (e.g. insert).
+ BatchedCommandRequest::BatchType _writeType;
+ };
+
+ class ClusterCmdInsert : public ClusterWriteCmd {
+ MONGO_DISALLOW_COPYING(ClusterCmdInsert);
+ public:
+ ClusterCmdInsert() :
+ ClusterWriteCmd( "insert", BatchedCommandRequest::BatchType_Insert ) {
+ }
+
+ void help( stringstream& help ) const {
+ help << "insert documents";
+ }
+ };
+
+ class ClusterCmdUpdate : public ClusterWriteCmd {
+ MONGO_DISALLOW_COPYING(ClusterCmdUpdate);
+ public:
+ ClusterCmdUpdate() :
+ ClusterWriteCmd( "update", BatchedCommandRequest::BatchType_Update ) {
+ }
+
+ void help( stringstream& help ) const {
+ help << "update documents";
+ }
+ };
+
+ class ClusterCmdDelete : public ClusterWriteCmd {
+ MONGO_DISALLOW_COPYING(ClusterCmdDelete);
+ public:
+ ClusterCmdDelete() :
+ ClusterWriteCmd( "delete", BatchedCommandRequest::BatchType_Delete ) {
+ }
+
+ void help( stringstream& help ) const {
+ help << "delete documents";
+ }
+ };
+
+ //
+ // Cluster write command implementation(s) below
+ //
+
+ bool ClusterWriteCmd::run( const string& dbName,
+ BSONObj& cmdObj,
+ int options,
+ string& errMsg,
+ BSONObjBuilder& result,
+ bool ) {
+
+ BatchedCommandRequest request( _writeType );
+ BatchedCommandResponse response;
+ ClusterWriter writer( true /* autosplit */, 0 /* timeout */ );
+
+ // NOTE: Sometimes this command is invoked with LE disabled for legacy writes
+ LastError* cmdLastError = lastError.get( false );
+
+ {
+ // Disable the last error object for the duration of the write
+ LastError::Disabled disableLastError( cmdLastError );
+
+ // TODO: if we do namespace parsing, push this to the type
+ if ( !request.parseBSON( cmdObj, &errMsg ) || !request.isValid( &errMsg ) ) {
+
+ // Batch parse failure
+ response.setOk( false );
+ response.setErrCode( ErrorCodes::FailedToParse );
+ response.setErrMessage( errMsg );
+ }
+ else {
+
+ // Fixup the namespace to be a full ns internally
+ NamespaceString nss( dbName, request.getNS() );
+ request.setNS( nss.ns() );
+
+ writer.write( request, &response );
+ }
+
+ dassert( response.isValid( NULL ) );
+ }
+
+ if ( cmdLastError ) {
+ // Populate the lastError object based on the write response
+ cmdLastError->reset();
+ batchErrorToLastError( request, response, cmdLastError );
+ }
+
+ size_t numAttempts;
+ if ( !response.getOk() ) {
+ numAttempts = 0;
+ } else if ( request.getOrdered() && response.isErrDetailsSet() ) {
+ numAttempts = response.getErrDetailsAt(0)->getIndex() + 1; // Add one failed attempt
+ } else {
+ numAttempts = request.sizeWriteOps();
+ }
+
+ // TODO: increase opcounters by more than one
+ if ( _writeType == BatchedCommandRequest::BatchType_Insert ) {
+ for( size_t i = 0; i < numAttempts; ++i ) {
+ globalOpCounters.gotInsert();
+ }
+ } else if ( _writeType == BatchedCommandRequest::BatchType_Update ) {
+ for( size_t i = 0; i < numAttempts; ++i ) {
+ globalOpCounters.gotUpdate();
+ }
+ } else if ( _writeType == BatchedCommandRequest::BatchType_Delete ) {
+ for( size_t i = 0; i < numAttempts; ++i ) {
+ globalOpCounters.gotDelete();
+ }
+ }
+
+ // Save the last opTimes written on each shard for this client, to allow GLE to work
+ if ( ClientInfo::exists() && writer.getStats().hasShardStats() ) {
+ ClientInfo* clientInfo = ClientInfo::get( NULL );
+ clientInfo->addHostOpTimes( writer.getStats().getShardStats().getWriteOpTimes() );
+ }
+
+ // TODO
+ // There's a pending issue about how to report response here. If we use
+ // the command infra-structure, we should reuse the 'errmsg' field. But
+ // we have already filed that message inside the BatchCommandResponse.
+ // return response.getOk();
+ result.appendElements( response.toBSON() );
+ return true;
+ }
+
+ //
+ // Register write commands at startup
+ //
+
+ namespace {
+
+ MONGO_INITIALIZER(RegisterWriteCommands)(InitializerContext* context) {
+ // Leaked intentionally: a Command registers itself when constructed.
+ new ClusterCmdInsert();
+ new ClusterCmdUpdate();
+ new ClusterCmdDelete();
+ return Status::OK();
+ }
+
+ } // namespace
+
+} // namespace mongo