diff options
Diffstat (limited to 'src/mongo/db/commands/get_last_error.cpp')
-rw-r--r-- | src/mongo/db/commands/get_last_error.cpp | 467 |
1 files changed, 235 insertions, 232 deletions
diff --git a/src/mongo/db/commands/get_last_error.cpp b/src/mongo/db/commands/get_last_error.cpp index ac80e1de823..2e5cd625086 100644 --- a/src/mongo/db/commands/get_last_error.cpp +++ b/src/mongo/db/commands/get_last_error.cpp @@ -44,264 +44,267 @@ namespace mongo { - using std::string; - using std::stringstream; - - /* reset any errors so that getlasterror comes back clean. - - useful before performing a long series of operations where we want to - see if any of the operations triggered an error, but don't want to check - after each op as that woudl be a client/server turnaround. - */ - class CmdResetError : public Command { - public: - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual bool slaveOk() const { - return true; - } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) {} // No auth required - virtual void help( stringstream& help ) const { - help << "reset error state (used with getpreverror)"; +using std::string; +using std::stringstream; + +/* reset any errors so that getlasterror comes back clean. + + useful before performing a long series of operations where we want to + see if any of the operations triggered an error, but don't want to check + after each op as that woudl be a client/server turnaround. +*/ +class CmdResetError : public Command { +public: + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual bool slaveOk() const { + return true; + } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) {} // No auth required + virtual void help(stringstream& help) const { + help << "reset error state (used with getpreverror)"; + } + CmdResetError() : Command("resetError", false, "reseterror") {} + bool run(OperationContext* txn, + const string& db, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + LastError::get(txn->getClient()).reset(); + return true; + } +} cmdResetError; + +class CmdGetLastError : public Command { +public: + CmdGetLastError() : Command("getLastError", false, "getlasterror") {} + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual bool slaveOk() const { + return true; + } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) {} // No auth required + virtual void help(stringstream& help) const { + LastError::get(cc()).disable(); // SERVER-11492 + help << "return error status of the last operation on this connection\n" + << "options:\n" + << " { fsync:true } - fsync before returning, or wait for journal commit if running " + "with --journal\n" + << " { j:true } - wait for journal commit if running with --journal\n" + << " { w:n } - await replication to n servers (including self) before returning\n" + << " { w:'majority' } - await replication to majority of set\n" + << " { wtimeout:m} - timeout for w in m milliseconds"; + } + + bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + // + // Correct behavior here is very finicky. + // + // 1. The first step is to append the error that occurred on the previous operation. + // This adds an "err" field to the command, which is *not* the command failing. + // + // 2. Next we parse and validate write concern options. If these options are invalid + // the command fails no matter what, even if we actually had an error earlier. The + // reason for checking here is to match legacy behavior on these kind of failures - + // we'll still get an "err" field for the write error. + // + // 3. If we had an error on the previous operation, we then return immediately. + // + // 4. Finally, we actually enforce the write concern. All errors *except* timeout are + // reported with ok : 0.0, to match legacy behavior. + // + // There is a special case when "wOpTime" and "wElectionId" are explicitly provided by + // the client (mongos) - in this case we *only* enforce the write concern if it is + // valid. + // + // We always need to either report "err" (if ok : 1) or "errmsg" (if ok : 0), even if + // err is null. + // + + LastError* le = &LastError::get(txn->getClient()); + le->disable(); + + // Always append lastOp and connectionId + Client& c = *txn->getClient(); + if (repl::getGlobalReplicationCoordinator()->getReplicationMode() == + repl::ReplicationCoordinator::modeReplSet) { + const repl::OpTime lastOp = repl::ReplClientInfo::forClient(c).getLastOp(); + if (!lastOp.isNull()) { + result.append("lastOp", lastOp.getTimestamp()); + // TODO(siyuan) Add "lastOpTerm" + } } - CmdResetError() : Command("resetError", false, "reseterror") {} - bool run(OperationContext* txn, - const string& db, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - LastError::get(txn->getClient()).reset(); - return true; + + // for sharding; also useful in general for debugging + result.appendNumber("connectionId", c.getConnectionId()); + + Timestamp lastTimestamp; + BSONField<Timestamp> wOpTimeField("wOpTime"); + FieldParser::FieldState extracted = + FieldParser::extract(cmdObj, wOpTimeField, &lastTimestamp, &errmsg); + if (!extracted) { + result.append("badGLE", cmdObj); + appendCommandStatus(result, false, errmsg); + return false; } - } cmdResetError; - - class CmdGetLastError : public Command { - public: - CmdGetLastError() : Command("getLastError", false, "getlasterror") { } - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual bool slaveOk() const { return true; } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) {} // No auth required - virtual void help( stringstream& help ) const { - LastError::get(cc()).disable(); // SERVER-11492 - help << "return error status of the last operation on this connection\n" - << "options:\n" - << " { fsync:true } - fsync before returning, or wait for journal commit if running with --journal\n" - << " { j:true } - wait for journal commit if running with --journal\n" - << " { w:n } - await replication to n servers (including self) before returning\n" - << " { w:'majority' } - await replication to majority of set\n" - << " { wtimeout:m} - timeout for w in m milliseconds"; + + repl::OpTime lastOpTime; + bool lastOpTimePresent = extracted != FieldParser::FIELD_NONE; + if (!lastOpTimePresent) { + // Use the client opTime if no wOpTime is specified + lastOpTime = repl::ReplClientInfo::forClient(c).getLastOp(); + // TODO(siyuan) Fix mongos to supply wOpTimeTerm, then parse out that value here + } else { + // TODO(siyuan) Don't use the default term after fixing mongos. + lastOpTime = repl::OpTime(lastTimestamp, repl::OpTime::kDefaultTerm); } - bool run(OperationContext* txn, const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - - // - // Correct behavior here is very finicky. - // - // 1. The first step is to append the error that occurred on the previous operation. - // This adds an "err" field to the command, which is *not* the command failing. - // - // 2. Next we parse and validate write concern options. If these options are invalid - // the command fails no matter what, even if we actually had an error earlier. The - // reason for checking here is to match legacy behavior on these kind of failures - - // we'll still get an "err" field for the write error. - // - // 3. If we had an error on the previous operation, we then return immediately. - // - // 4. Finally, we actually enforce the write concern. All errors *except* timeout are - // reported with ok : 0.0, to match legacy behavior. - // - // There is a special case when "wOpTime" and "wElectionId" are explicitly provided by - // the client (mongos) - in this case we *only* enforce the write concern if it is - // valid. - // - // We always need to either report "err" (if ok : 1) or "errmsg" (if ok : 0), even if - // err is null. - // - - LastError *le = &LastError::get(txn->getClient()); - le->disable(); - - // Always append lastOp and connectionId - Client& c = *txn->getClient(); - if (repl::getGlobalReplicationCoordinator()->getReplicationMode() == - repl::ReplicationCoordinator::modeReplSet) { - const repl::OpTime lastOp = repl::ReplClientInfo::forClient(c).getLastOp(); - if (!lastOp.isNull()) { - result.append("lastOp", lastOp.getTimestamp()); - // TODO(siyuan) Add "lastOpTerm" - } - } + OID electionId; + BSONField<OID> wElectionIdField("wElectionId"); + extracted = FieldParser::extract(cmdObj, wElectionIdField, &electionId, &errmsg); + if (!extracted) { + result.append("badGLE", cmdObj); + appendCommandStatus(result, false, errmsg); + return false; + } - // for sharding; also useful in general for debugging - result.appendNumber( "connectionId" , c.getConnectionId() ); - - Timestamp lastTimestamp; - BSONField<Timestamp> wOpTimeField("wOpTime"); - FieldParser::FieldState extracted = FieldParser::extract(cmdObj, wOpTimeField, - &lastTimestamp, &errmsg); - if (!extracted) { - result.append("badGLE", cmdObj); - appendCommandStatus(result, false, errmsg); - return false; - } + bool electionIdPresent = extracted != FieldParser::FIELD_NONE; + bool errorOccurred = false; - repl::OpTime lastOpTime; - bool lastOpTimePresent = extracted != FieldParser::FIELD_NONE; - if (!lastOpTimePresent) { - // Use the client opTime if no wOpTime is specified - lastOpTime = repl::ReplClientInfo::forClient(c).getLastOp(); - // TODO(siyuan) Fix mongos to supply wOpTimeTerm, then parse out that value here + // Errors aren't reported when wOpTime is used + if (!lastOpTimePresent) { + if (le->getNPrev() != 1) { + errorOccurred = LastError::noError.appendSelf(result, false); } else { - // TODO(siyuan) Don't use the default term after fixing mongos. - lastOpTime = repl::OpTime(lastTimestamp, repl::OpTime::kDefaultTerm); - } - - OID electionId; - BSONField<OID> wElectionIdField("wElectionId"); - extracted = FieldParser::extract(cmdObj, wElectionIdField, - &electionId, &errmsg); - if (!extracted) { - result.append("badGLE", cmdObj); - appendCommandStatus(result, false, errmsg); - return false; + errorOccurred = le->appendSelf(result, false); } + } - bool electionIdPresent = extracted != FieldParser::FIELD_NONE; - bool errorOccurred = false; - - // Errors aren't reported when wOpTime is used - if ( !lastOpTimePresent ) { - if ( le->getNPrev() != 1 ) { - errorOccurred = LastError::noError.appendSelf( result, false ); - } - else { - errorOccurred = le->appendSelf( result, false ); - } - } + BSONObj writeConcernDoc = cmdObj; + // Use the default options if we have no gle options aside from wOpTime/wElectionId + const int nFields = cmdObj.nFields(); + bool useDefaultGLEOptions = (nFields == 1) || (nFields == 2 && lastOpTimePresent) || + (nFields == 3 && lastOpTimePresent && electionIdPresent); - BSONObj writeConcernDoc = cmdObj; - // Use the default options if we have no gle options aside from wOpTime/wElectionId - const int nFields = cmdObj.nFields(); - bool useDefaultGLEOptions = (nFields == 1) || - (nFields == 2 && lastOpTimePresent) || - (nFields == 3 && lastOpTimePresent && electionIdPresent); + WriteConcernOptions writeConcern; - WriteConcernOptions writeConcern; + if (useDefaultGLEOptions) { + writeConcern = repl::getGlobalReplicationCoordinator()->getGetLastErrorDefault(); + } - if (useDefaultGLEOptions) { - writeConcern = repl::getGlobalReplicationCoordinator()->getGetLastErrorDefault(); - } + Status status = writeConcern.parse(writeConcernDoc); - Status status = writeConcern.parse( writeConcernDoc ); + // + // Validate write concern no matter what, this matches 2.4 behavior + // - // - // Validate write concern no matter what, this matches 2.4 behavior - // + if (status.isOK()) { + // Ensure options are valid for this host + status = validateWriteConcern(writeConcern); + } - if ( status.isOK() ) { - // Ensure options are valid for this host - status = validateWriteConcern( writeConcern ); - } + if (!status.isOK()) { + result.append("badGLE", writeConcernDoc); + return appendCommandStatus(result, status); + } - if ( !status.isOK() ) { - result.append( "badGLE", writeConcernDoc ); - return appendCommandStatus( result, status ); - } + // Don't wait for replication if there was an error reported - this matches 2.4 behavior + if (errorOccurred) { + dassert(!lastOpTimePresent); + return true; + } - // Don't wait for replication if there was an error reported - this matches 2.4 behavior - if ( errorOccurred ) { - dassert( !lastOpTimePresent ); - return true; - } + // No error occurred, so we won't duplicate these fields with write concern errors + dassert(result.asTempObj()["err"].eoo()); + dassert(result.asTempObj()["code"].eoo()); - // No error occurred, so we won't duplicate these fields with write concern errors - dassert( result.asTempObj()["err"].eoo() ); - dassert( result.asTempObj()["code"].eoo() ); - - // If we got an electionId, make sure it matches - if (electionIdPresent) { - if (repl::getGlobalReplicationCoordinator()->getReplicationMode() != - repl::ReplicationCoordinator::modeReplSet) { - // Ignore electionIds of 0 from mongos. - if (electionId != OID()) { - errmsg = "wElectionId passed but no replication active"; - result.append("code", ErrorCodes::BadValue); - return false; - } - } - else { - if (electionId != repl::getGlobalReplicationCoordinator()->getElectionId()) { - LOG(3) << "oid passed in is " << electionId - << ", but our id is " - << repl::getGlobalReplicationCoordinator()->getElectionId(); - errmsg = "election occurred after write"; - result.append("code", ErrorCodes::WriteConcernFailed); - return false; - } + // If we got an electionId, make sure it matches + if (electionIdPresent) { + if (repl::getGlobalReplicationCoordinator()->getReplicationMode() != + repl::ReplicationCoordinator::modeReplSet) { + // Ignore electionIds of 0 from mongos. + if (electionId != OID()) { + errmsg = "wElectionId passed but no replication active"; + result.append("code", ErrorCodes::BadValue); + return false; + } + } else { + if (electionId != repl::getGlobalReplicationCoordinator()->getElectionId()) { + LOG(3) << "oid passed in is " << electionId << ", but our id is " + << repl::getGlobalReplicationCoordinator()->getElectionId(); + errmsg = "election occurred after write"; + result.append("code", ErrorCodes::WriteConcernFailed); + return false; } } + } - txn->setWriteConcern(writeConcern); - { - stdx::lock_guard<Client> lk(*txn->getClient()); - txn->setMessage_inlock( "waiting for write concern" ); - } - - WriteConcernResult wcResult; - status = waitForWriteConcern( txn, lastOpTime, &wcResult ); - wcResult.appendTo( writeConcern, &result ); - - // For backward compatibility with 2.4, wtimeout returns ok : 1.0 - if ( wcResult.wTimedOut ) { - dassert( !wcResult.err.empty() ); // so we always report err - dassert( !status.isOK() ); - result.append( "errmsg", "timed out waiting for slaves" ); - result.append( "code", status.code() ); - return true; - } - - return appendCommandStatus( result, status ); + txn->setWriteConcern(writeConcern); + { + stdx::lock_guard<Client> lk(*txn->getClient()); + txn->setMessage_inlock("waiting for write concern"); } - } cmdGetLastError; + WriteConcernResult wcResult; + status = waitForWriteConcern(txn, lastOpTime, &wcResult); + wcResult.appendTo(writeConcern, &result); - class CmdGetPrevError : public Command { - public: - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual void help( stringstream& help ) const { - help << "check for errors since last reseterror commandcal"; - } - virtual bool slaveOk() const { - return true; - } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) {} // No auth required - CmdGetPrevError() : Command("getPrevError", false, "getpreverror") {} - bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - LastError *le = &LastError::get(txn->getClient()); - le->disable(); - le->appendSelf(result, true); - if (le->isValid()) - result.append("nPrev", le->getNPrev()); - else - result.append("nPrev", -1); + // For backward compatibility with 2.4, wtimeout returns ok : 1.0 + if (wcResult.wTimedOut) { + dassert(!wcResult.err.empty()); // so we always report err + dassert(!status.isOK()); + result.append("errmsg", "timed out waiting for slaves"); + result.append("code", status.code()); return true; } - } cmdGetPrevError; + return appendCommandStatus(result, status); + } + +} cmdGetLastError; + +class CmdGetPrevError : public Command { +public: + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual void help(stringstream& help) const { + help << "check for errors since last reseterror commandcal"; + } + virtual bool slaveOk() const { + return true; + } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) {} // No auth required + CmdGetPrevError() : Command("getPrevError", false, "getpreverror") {} + bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + LastError* le = &LastError::get(txn->getClient()); + le->disable(); + le->appendSelf(result, true); + if (le->isValid()) + result.append("nPrev", le->getNPrev()); + else + result.append("nPrev", -1); + return true; + } +} cmdGetPrevError; } |