diff options
author | Yoonsoo Kim <yoonsoo.kim@mongodb.com> | 2021-07-22 01:48:29 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-07-22 02:13:04 +0000 |
commit | 52b6b281105274c3a66312fc6ee8a2c12f4a9e40 (patch) | |
tree | 9a641823ca82ac833444183251370dad61f81c6b /src | |
parent | 97e7a659d01f8f5ceef69a4f738cdf76396d99db (diff) | |
download | mongo-52b6b281105274c3a66312fc6ee8a2c12f4a9e40.tar.gz |
SERVER-57390 Remove getLastError implementation
Diffstat (limited to 'src')
37 files changed, 40 insertions, 1667 deletions
diff --git a/src/mongo/db/client.cpp b/src/mongo/db/client.cpp index 574224f8d3a..6bd225ac266 100644 --- a/src/mongo/db/client.cpp +++ b/src/mongo/db/client.cpp @@ -40,7 +40,6 @@ #include <vector> #include "mongo/base/status.h" -#include "mongo/db/lasterror.h" #include "mongo/db/operation_context.h" #include "mongo/db/operation_cpu_timer.h" #include "mongo/db/service_context.h" diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp index c0a063f4b9c..eb9461f8e16 100644 --- a/src/mongo/db/commands/dbcommands.cpp +++ b/src/mongo/db/commands/dbcommands.cpp @@ -70,7 +70,6 @@ #include "mongo/db/jsobj.h" #include "mongo/db/json.h" #include "mongo/db/keypattern.h" -#include "mongo/db/lasterror.h" #include "mongo/db/namespace_string.h" #include "mongo/db/op_observer.h" #include "mongo/db/ops/insert.h" diff --git a/src/mongo/db/commands/dbcommands_d.cpp b/src/mongo/db/commands/dbcommands_d.cpp index 8e9e40ab9b5..e7a6b76d2cf 100644 --- a/src/mongo/db/commands/dbcommands_d.cpp +++ b/src/mongo/db/commands/dbcommands_d.cpp @@ -69,7 +69,6 @@ #include "mongo/db/jsobj.h" #include "mongo/db/json.h" #include "mongo/db/keypattern.h" -#include "mongo/db/lasterror.h" #include "mongo/db/namespace_string.h" #include "mongo/db/op_observer.h" #include "mongo/db/ops/insert.h" diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index 4d2377d9c0c..52fd39f3683 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -47,7 +47,6 @@ #include "mongo/db/exec/delete.h" #include "mongo/db/exec/update_stage.h" #include "mongo/db/exec/working_set_common.h" -#include "mongo/db/lasterror.h" #include "mongo/db/matcher/extensions_callback_real.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" diff --git a/src/mongo/db/commands/get_last_error.cpp b/src/mongo/db/commands/get_last_error.cpp index 7cc13764c65..fab019a7379 100644 --- a/src/mongo/db/commands/get_last_error.cpp +++ b/src/mongo/db/commands/get_last_error.cpp @@ -33,23 +33,11 @@ #include "mongo/db/client.h" #include "mongo/db/commands.h" -#include "mongo/db/curop.h" -#include "mongo/db/field_parser.h" -#include "mongo/db/lasterror.h" -#include "mongo/db/repl/bson_extract_optime.h" -#include "mongo/db/repl/repl_client_info.h" -#include "mongo/db/repl/replication_coordinator.h" -#include "mongo/db/write_concern.h" -#include "mongo/idl/command_generic_argument.h" -#include "mongo/logv2/log.h" #include "mongo/rpc/warn_deprecated_wire_ops.h" namespace mongo { namespace { -using std::string; -using std::stringstream; - class CmdGetLastError : public ErrmsgCommandDeprecated { public: CmdGetLastError() : ErrmsgCommandDeprecated("getLastError", "getlasterror") {} @@ -68,207 +56,17 @@ public: } std::string help() const override { - return "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"; + return "no longer supported"; } bool errmsgRun(OperationContext* opCtx, - const string& dbname, - const BSONObj& cmdObj, - 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(opCtx->getClient()); - le->disable(); - - // Always append lastOp and connectionId - Client& c = *opCtx->getClient(); - auto replCoord = repl::ReplicationCoordinator::get(opCtx); - if (replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet) { - const repl::OpTime lastOp = repl::ReplClientInfo::forClient(c).getLastOp(); - if (!lastOp.isNull()) { - lastOp.append(&result, "lastOp"); - } - } - - warnDeprecation(c, "getLastError"); - - // for sharding; also useful in general for debugging - result.appendNumber("connectionId", c.getConnectionId()); - - repl::OpTime lastOpTime; - bool lastOpTimePresent = true; - const BSONElement opTimeElement = cmdObj["wOpTime"]; - if (opTimeElement.eoo()) { - lastOpTimePresent = false; - lastOpTime = repl::ReplClientInfo::forClient(c).getLastOp(); - } else if (opTimeElement.type() == bsonTimestamp) { - lastOpTime = repl::OpTime(opTimeElement.timestamp(), repl::OpTime::kUninitializedTerm); - } else if (opTimeElement.type() == Date) { - lastOpTime = - repl::OpTime(Timestamp(opTimeElement.date()), repl::OpTime::kUninitializedTerm); - } else if (opTimeElement.type() == Object) { - Status status = bsonExtractOpTimeField(cmdObj, "wOpTime", &lastOpTime); - if (!status.isOK()) { - result.append("badGLE", cmdObj); - return CommandHelpers::appendCommandStatusNoThrow(result, status); - } - } else { - uasserted(ErrorCodes::TypeMismatch, - str::stream() << "Expected \"wOpTime\" field in getLastError to " - "have type Date, Timestamp, or OpTime but found type " - << typeName(opTimeElement.type())); - } - - - OID electionId; - BSONField<OID> wElectionIdField("wElectionId"); - FieldParser::FieldState extracted = - FieldParser::extract(cmdObj, wElectionIdField, &electionId, &errmsg); - if (!extracted) { - result.append("badGLE", cmdObj); - CommandHelpers::appendSimpleCommandStatus(result, false, errmsg); - return 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 = ([&] { - BSONObjBuilder bob; - for (auto&& elem : cmdObj) { - if (!isGenericArgument(elem.fieldNameStringData())) - bob.append(elem); - } - return bob.obj(); - }()); - - WriteConcernOptions writeConcern; - auto sw = WriteConcernOptions::parse(writeConcernDoc); - Status status = sw.getStatus(); - - // - // Validate write concern no matter what, this matches 2.4 behavior - // - if (status.isOK()) { - writeConcern = sw.getValue(); - status = validateWriteConcern(opCtx, writeConcern); - } - - if (!status.isOK()) { - result.append("badGLE", writeConcernDoc); - return CommandHelpers::appendCommandStatusNoThrow(result, status); - } - - // 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()); - - // If we got an electionId, make sure it matches - if (electionIdPresent) { - if (repl::ReplicationCoordinator::get(opCtx)->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); - result.append("codeName", ErrorCodes::errorString(ErrorCodes::BadValue)); - return false; - } - } else { - if (electionId != repl::ReplicationCoordinator::get(opCtx)->getElectionId()) { - LOGV2_DEBUG(20476, - 3, - "OID passed in is {passedOID}, but our id is {ourOID}", - "OID mismatch during election", - "passedOID"_attr = electionId, - "ourOID"_attr = - repl::ReplicationCoordinator::get(opCtx)->getElectionId()); - errmsg = "election occurred after write"; - result.append("code", ErrorCodes::WriteConcernFailed); - result.append("codeName", - ErrorCodes::errorString(ErrorCodes::WriteConcernFailed)); - return false; - } - } - } - - { - stdx::lock_guard<Client> lk(*opCtx->getClient()); - CurOp::get(opCtx)->setMessage_inlock("waiting for write concern"); - } - - WriteConcernResult wcResult; - status = waitForWriteConcern(opCtx, lastOpTime, writeConcern, &wcResult); - // getLastError command returns a document that contains the writtenTo array. So we compute - // the writtenTo array here if we have waited for replication before. The call to - // getHostsWrittenTo needs to lock the ReplicationCoordinator mutex to guard against - // topology changes. Thus, we only compute this array here for the getLastError command - // (instead of in waitForWriteConcern for every single write) to avoid a serialization point - // for all writes. - if (!lastOpTime.isNull() && writeConcern.needToWaitForOtherNodes()) { - wcResult.writtenTo = replCoord->getHostsWrittenTo( - lastOpTime, - replCoord->populateUnsetWriteConcernOptionsSyncMode(writeConcern).syncMode == - WriteConcernOptions::SyncMode::JOURNAL); - } - wcResult.appendTo(&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 secondaries"); - result.append("code", status.code()); - result.append("codeName", ErrorCodes::errorString(status.code())); - return true; - } - - return CommandHelpers::appendCommandStatusNoThrow(result, status); + const std::string&, + const BSONObj&, + std::string&, + BSONObjBuilder&) { + warnDeprecation(*opCtx->getClient(), "getLastError"); + uasserted(5739000, "getLastError command is not supported"); + return false; } } cmdGetLastError; diff --git a/src/mongo/db/dbdirectclient.cpp b/src/mongo/db/dbdirectclient.cpp index 31d330ea11b..efbdc3db106 100644 --- a/src/mongo/db/dbdirectclient.cpp +++ b/src/mongo/db/dbdirectclient.cpp @@ -131,14 +131,9 @@ QueryOptions DBDirectClient::_lookupAvailableOptions() { } namespace { -DbResponse loopbackBuildResponse(OperationContext* const opCtx, - LastError* lastError, - Message& toSend) { +DbResponse loopbackBuildResponse(OperationContext* const opCtx, Message& toSend) { DirectClientScope directClientScope(opCtx); - boost::swap(*lastError, LastError::get(opCtx->getClient())); - ON_BLOCK_EXIT([&] { boost::swap(*lastError, LastError::get(opCtx->getClient())); }); - LastError::get(opCtx->getClient()).startRequest(); CurOp curOp(opCtx); toSend.header().setId(nextMessageId()); @@ -149,7 +144,7 @@ DbResponse loopbackBuildResponse(OperationContext* const opCtx, } // namespace bool DBDirectClient::call(Message& toSend, Message& response, bool assertOk, string* actualServer) { - auto dbResponse = loopbackBuildResponse(_opCtx, &_lastError, toSend); + auto dbResponse = loopbackBuildResponse(_opCtx, toSend); invariant(!dbResponse.response.empty()); response = std::move(dbResponse.response); @@ -157,7 +152,7 @@ bool DBDirectClient::call(Message& toSend, Message& response, bool assertOk, str } void DBDirectClient::say(Message& toSend, bool isRetry, string* actualServer) { - auto dbResponse = loopbackBuildResponse(_opCtx, &_lastError, toSend); + auto dbResponse = loopbackBuildResponse(_opCtx, toSend); invariant(dbResponse.response.empty()); } diff --git a/src/mongo/db/dbdirectclient.h b/src/mongo/db/dbdirectclient.h index b9097e9c5fd..f3dd6c6393d 100644 --- a/src/mongo/db/dbdirectclient.h +++ b/src/mongo/db/dbdirectclient.h @@ -32,7 +32,6 @@ #include "mongo/client/dbclient_base.h" #include "mongo/config.h" #include "mongo/db/dbmessage.h" -#include "mongo/db/lasterror.h" #include "mongo/db/ops/write_ops.h" #include "mongo/util/net/hostandport.h" @@ -123,7 +122,6 @@ protected: private: OperationContext* _opCtx; - LastError _lastError; // This LastError will be used for all operations on this client. }; } // namespace mongo diff --git a/src/mongo/db/lasterror.cpp b/src/mongo/db/lasterror.cpp index c81c7565ac9..396e707efb4 100644 --- a/src/mongo/db/lasterror.cpp +++ b/src/mongo/db/lasterror.cpp @@ -38,8 +38,6 @@ namespace mongo { -LastError LastError::noError; - const Client::Decoration<LastError> LastError::get = Client::declareDecoration<LastError>(); namespace { @@ -87,40 +85,6 @@ void LastError::recordDelete(long long nDeleted) { _nObjects = nDeleted; } -bool LastError::appendSelf(BSONObjBuilder& b, bool blankErr) const { - if (!_valid) { - if (blankErr) - b.appendNull("err"); - b.append("n", 0); - return false; - } - - if (_msg.empty()) { - if (blankErr) { - b.appendNull("err"); - } - } else { - b.append("err", _msg); - if (_msg.find("E11000 duplicate key error") != std::string::npos) { - appendDupKeyFields(b, _msg); - } - } - - if (_code) { - b.append("code", _code); - b.append("codeName", ErrorCodes::errorString(ErrorCodes::Error(_code))); - } - if (_updatedExisting != NotUpdate) - b.appendBool("updatedExisting", _updatedExisting == True); - if (!_upsertedId.isEmpty()) { - b.append(_upsertedId[kUpsertedFieldName]); - } - b.appendNumber("n", _nObjects); - - return !_msg.empty(); -} - - void LastError::disable() { invariant(!_disabled); _disabled = true; diff --git a/src/mongo/db/lasterror.h b/src/mongo/db/lasterror.h index 1d4414f8865..6eff2f08ef0 100644 --- a/src/mongo/db/lasterror.h +++ b/src/mongo/db/lasterror.h @@ -69,16 +69,6 @@ public: void recordDelete(long long nDeleted); - /** - * Writes the last-error state described by this object to "b". - * - * If "blankErr" is true, the "err" field will be explicitly set to null in the result - * instead of being omitted when the error string is empty. - * - * Returns true if there is a non-empty error message. - */ - bool appendSelf(BSONObjBuilder& b, bool blankErr) const; - bool isValid() const { return _valid; } @@ -105,8 +95,6 @@ public: const bool _prev; }; - static LastError noError; - private: enum UpdatedExistingType { NotUpdate, True, False }; diff --git a/src/mongo/db/repl/repl_set_commands.cpp b/src/mongo/db/repl/repl_set_commands.cpp index 7e5b7dcecee..bab337d9109 100644 --- a/src/mongo/db/repl/repl_set_commands.cpp +++ b/src/mongo/db/repl/repl_set_commands.cpp @@ -49,7 +49,6 @@ #include "mongo/db/commands/server_status_metric.h" #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/dbhelpers.h" -#include "mongo/db/lasterror.h" #include "mongo/db/op_observer.h" #include "mongo/db/repl/drop_pending_collection_reaper.h" #include "mongo/db/repl/oplog.h" diff --git a/src/mongo/db/s/dist_lock_catalog_replset.cpp b/src/mongo/db/s/dist_lock_catalog_replset.cpp index 7e62b1a9369..5a0dd02d83d 100644 --- a/src/mongo/db/s/dist_lock_catalog_replset.cpp +++ b/src/mongo/db/s/dist_lock_catalog_replset.cpp @@ -35,7 +35,6 @@ #include "mongo/bson/util/bson_extract.h" #include "mongo/client/read_preference.h" -#include "mongo/db/lasterror.h" #include "mongo/db/ops/write_ops.h" #include "mongo/db/repl/hello_gen.h" #include "mongo/db/repl/read_concern_args.h" @@ -44,7 +43,6 @@ #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/rpc/metadata.h" #include "mongo/rpc/metadata/repl_set_metadata.h" -#include "mongo/rpc/metadata/sharding_metadata.h" #include "mongo/s/client/shard_registry.h" #include "mongo/s/grid.h" #include "mongo/s/write_ops/batched_command_request.h" diff --git a/src/mongo/dbtests/directclienttests.cpp b/src/mongo/dbtests/directclienttests.cpp index 2df1ca8ec02..3c9b421ecfa 100644 --- a/src/mongo/dbtests/directclienttests.cpp +++ b/src/mongo/dbtests/directclienttests.cpp @@ -44,19 +44,9 @@ namespace DirectClientTests { using std::unique_ptr; using std::vector; -class ClientBase { -public: - ClientBase() { - mongo::LastError::get(cc()).reset(); - } - virtual ~ClientBase() { - mongo::LastError::get(cc()).reset(); - } -}; - const char* ns = "a.b"; -class InsertMany : ClientBase { +class InsertMany { public: virtual void run() { const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); @@ -82,7 +72,7 @@ public: } }; -class BadNSCmd : ClientBase { +class BadNSCmd { public: virtual void run() { const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); @@ -97,7 +87,7 @@ public: } }; -class BadNSQuery : ClientBase { +class BadNSQuery { public: virtual void run() { const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); @@ -110,7 +100,7 @@ public: } }; -class BadNSGetMore : ClientBase { +class BadNSGetMore { public: virtual void run() { const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); @@ -122,7 +112,7 @@ public: } }; -class BadNSInsert : ClientBase { +class BadNSInsert { public: virtual void run() { const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); @@ -134,7 +124,7 @@ public: } }; -class BadNSUpdate : ClientBase { +class BadNSUpdate { public: virtual void run() { const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); @@ -146,7 +136,7 @@ public: } }; -class BadNSRemove : ClientBase { +class BadNSRemove { public: virtual void run() { const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext(); diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp index 89b52a3f172..b99b686f3bf 100644 --- a/src/mongo/dbtests/querytests.cpp +++ b/src/mongo/dbtests/querytests.cpp @@ -260,12 +260,7 @@ public: class ClientBase { public: - ClientBase() : _client(&_opCtx) { - mongo::LastError::get(_opCtx.getClient()).reset(); - } - virtual ~ClientBase() { - mongo::LastError::get(_opCtx.getClient()).reset(); - } + ClientBase() : _client(&_opCtx) {} protected: void insert(const char* ns, BSONObj o) { diff --git a/src/mongo/dbtests/updatetests.cpp b/src/mongo/dbtests/updatetests.cpp index 6ec4144369c..f29ccdb9699 100644 --- a/src/mongo/dbtests/updatetests.cpp +++ b/src/mongo/dbtests/updatetests.cpp @@ -58,12 +58,7 @@ namespace dps = ::mongo::dotted_path_support; class ClientBase { public: - ClientBase() : _client(&_opCtx) { - mongo::LastError::get(_opCtx.getClient()).reset(); - } - virtual ~ClientBase() { - mongo::LastError::get(_opCtx.getClient()).reset(); - } + ClientBase() : _client(&_opCtx) {} protected: const ServiceContext::UniqueOperationContext _txnPtr = cc().makeOperationContext(); diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript index cdbbc15db10..2a8ded75b00 100644 --- a/src/mongo/rpc/SConscript +++ b/src/mongo/rpc/SConscript @@ -181,7 +181,6 @@ if wiredtiger: 'metadata/egress_metadata_hook_list_test.cpp', 'metadata/oplog_query_metadata_test.cpp', 'metadata/repl_set_metadata_test.cpp', - 'metadata/sharding_metadata_test.cpp', 'metadata/tracking_metadata_test.cpp', 'metadata_test.cpp', 'object_check_test.cpp', diff --git a/src/mongo/rpc/legacy_reply_builder.cpp b/src/mongo/rpc/legacy_reply_builder.cpp index 2ceffbf0df7..39dc8265548 100644 --- a/src/mongo/rpc/legacy_reply_builder.cpp +++ b/src/mongo/rpc/legacy_reply_builder.cpp @@ -37,7 +37,6 @@ #include "mongo/db/dbmessage.h" #include "mongo/db/jsobj.h" #include "mongo/rpc/metadata.h" -#include "mongo/rpc/metadata/sharding_metadata.h" #include "mongo/s/stale_exception.h" #include "mongo/util/assert_util.h" #include "mongo/util/str.h" diff --git a/src/mongo/rpc/metadata.cpp b/src/mongo/rpc/metadata.cpp index 42b85765f42..c965569d25c 100644 --- a/src/mongo/rpc/metadata.cpp +++ b/src/mongo/rpc/metadata.cpp @@ -40,7 +40,6 @@ #include "mongo/rpc/metadata/client_metadata.h" #include "mongo/rpc/metadata/config_server_metadata.h" #include "mongo/rpc/metadata/impersonated_user_metadata.h" -#include "mongo/rpc/metadata/sharding_metadata.h" #include "mongo/rpc/metadata/tracking_metadata.h" #include "mongo/util/string_map.h" #include "mongo/util/testing_proctor.h" diff --git a/src/mongo/rpc/metadata/sharding_metadata.cpp b/src/mongo/rpc/metadata/sharding_metadata.cpp index 9c62f4be82b..07e4f1b4699 100644 --- a/src/mongo/rpc/metadata/sharding_metadata.cpp +++ b/src/mongo/rpc/metadata/sharding_metadata.cpp @@ -50,52 +50,6 @@ const char kGLEStatsElectionIdFieldName[] = "electionId"; } // namespace -StatusWith<ShardingMetadata> ShardingMetadata::readFromMetadata(const BSONObj& metadataObj) { - BSONElement smElem; - auto smExtractStatus = - bsonExtractTypedField(metadataObj, kGLEStatsFieldName, mongo::Object, &smElem); - if (!smExtractStatus.isOK()) { - return smExtractStatus; - } - - if (smElem.embeddedObject().nFields() != 2) { - return Status(ErrorCodes::InvalidOptions, - str::stream() << "The $gleStats object can only have 2 fields, but got " - << smElem.embeddedObject().toString()); - } - - repl::OpTime opTime; - const BSONElement opTimeElement = smElem.embeddedObject()[kGLEStatsLastOpTimeFieldName]; - if (opTimeElement.eoo()) { - return Status(ErrorCodes::NoSuchKey, "lastOpTime field missing"); - } else if (opTimeElement.type() == bsonTimestamp) { - opTime = repl::OpTime(opTimeElement.timestamp(), repl::OpTime::kUninitializedTerm); - } else if (opTimeElement.type() == Date) { - opTime = repl::OpTime(Timestamp(opTimeElement.date()), repl::OpTime::kUninitializedTerm); - } else if (opTimeElement.type() == Object) { - Status status = - bsonExtractOpTimeField(smElem.embeddedObject(), kGLEStatsLastOpTimeFieldName, &opTime); - if (!status.isOK()) { - return status; - } - } else { - return Status(ErrorCodes::TypeMismatch, - str::stream() << "Expected \"" << kGLEStatsLastOpTimeFieldName - << "\" field in response to replSetHeartbeat " - "command to have type Date or Timestamp, but found type " - << typeName(opTimeElement.type())); - } - - BSONElement lastElectionIdElem; - auto lastElectionIdExtractStatus = bsonExtractTypedField( - smElem.embeddedObject(), kGLEStatsElectionIdFieldName, mongo::jstOID, &lastElectionIdElem); - if (!lastElectionIdExtractStatus.isOK()) { - return lastElectionIdExtractStatus; - } - - return ShardingMetadata(opTime, lastElectionIdElem.OID()); -} - Status ShardingMetadata::writeToMetadata(BSONObjBuilder* metadataBob) const { BSONObjBuilder subobj(metadataBob->subobjStart(kGLEStatsFieldName)); if (getLastOpTime().getTerm() > repl::OpTime::kUninitializedTerm) { diff --git a/src/mongo/rpc/metadata/sharding_metadata.h b/src/mongo/rpc/metadata/sharding_metadata.h index f614b1a72a2..21fa75c3db3 100644 --- a/src/mongo/rpc/metadata/sharding_metadata.h +++ b/src/mongo/rpc/metadata/sharding_metadata.h @@ -49,11 +49,6 @@ namespace rpc { class ShardingMetadata { public: /** - * Reads ShardingMetadata from a metadata object. - */ - static StatusWith<ShardingMetadata> readFromMetadata(const BSONObj& metadataObj); - - /** * Writes ShardingMetadata to a metadata builder. */ Status writeToMetadata(BSONObjBuilder* metadataBob) const; diff --git a/src/mongo/rpc/metadata/sharding_metadata_test.cpp b/src/mongo/rpc/metadata/sharding_metadata_test.cpp deleted file mode 100644 index dec0fb1c3d1..00000000000 --- a/src/mongo/rpc/metadata/sharding_metadata_test.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * 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 - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * 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 Server Side 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/platform/basic.h" - -#include "mongo/base/status.h" -#include "mongo/db/jsobj.h" -#include "mongo/db/repl/optime.h" -#include "mongo/rpc/metadata/sharding_metadata.h" -#include "mongo/stdx/chrono.h" -#include "mongo/unittest/unittest.h" - -namespace { - -using namespace mongo; -using namespace mongo::rpc; -using mongo::unittest::assertGet; - -ShardingMetadata checkParse(const BSONObj& metadata) { - return assertGet(ShardingMetadata::readFromMetadata(metadata)); -} - -const auto kElectionId = OID{"541b1a00e8a23afa832b218e"}; -const auto kLastOpTime = repl::OpTime(Timestamp(Seconds{1337}, 800u), 4); - -TEST(ShardingMetadata, ReadFromMetadata) { - { - auto sm = checkParse( - BSON("$gleStats" << BSON("lastOpTime" << BSON("ts" << kLastOpTime.getTimestamp() << "t" - << kLastOpTime.getTerm()) - << "electionId" << kElectionId))); - ASSERT_EQ(sm.getLastElectionId(), kElectionId); - ASSERT_EQ(sm.getLastOpTime(), kLastOpTime); - } - { - // We don't care about order. - auto sm = checkParse( - BSON("$gleStats" << BSON("electionId" << kElectionId << "lastOpTime" - << BSON("ts" << kLastOpTime.getTimestamp() << "t" - << kLastOpTime.getTerm())))); - - ASSERT_EQ(sm.getLastElectionId(), kElectionId); - ASSERT_EQ(sm.getLastOpTime(), kLastOpTime); - } -} - -void checkParseFails(const BSONObj& metadata, ErrorCodes::Error error) { - auto sm = ShardingMetadata::readFromMetadata(metadata); - ASSERT_NOT_OK(sm.getStatus()); - ASSERT_EQ(sm.getStatus(), error); -} - -TEST(ShardingMetadata, ReadFromInvalidMetadata) { - { checkParseFails(BSONObj(), ErrorCodes::NoSuchKey); } - { checkParseFails(BSON("$gleStats" << 1), ErrorCodes::TypeMismatch); } - { checkParseFails(BSON("$gleStats" << BSONObj()), ErrorCodes::InvalidOptions); } - { - checkParseFails(BSON("$gleStats" << BSON("lastOpTime" << 3 << "electionId" << kElectionId)), - ErrorCodes::TypeMismatch); - } - { - checkParseFails( - BSON("$gleStats" << BSON("lastOpTime" << BSON("ts" << kLastOpTime.getTimestamp() << "t" - << kLastOpTime.getTerm()) - << "electionId" << 3)), - ErrorCodes::TypeMismatch); - } - { - checkParseFails( - BSON("$gleStats" << BSON("lastOpTime" << kElectionId << "electionId" - << BSON("ts" << kLastOpTime.getTimestamp() << "t" - << kLastOpTime.getTerm()))), - ErrorCodes::TypeMismatch); - } - { - checkParseFails( - BSON("$gleStats" << BSON("lastOpTime" << BSON("ts" << kLastOpTime.getTimestamp() << "t" - << kLastOpTime.getTerm()) - << "electionId" << kElectionId << "extra" - << "this should not be here")), - ErrorCodes::InvalidOptions); - } -} - -} // namespace diff --git a/src/mongo/rpc/op_legacy_integration_test.cpp b/src/mongo/rpc/op_legacy_integration_test.cpp index 4fda3fb8fbc..c2d8614364a 100644 --- a/src/mongo/rpc/op_legacy_integration_test.cpp +++ b/src/mongo/rpc/op_legacy_integration_test.cpp @@ -204,28 +204,16 @@ bool wasLogged(DBClientBase* conn, const std::string& opName, const std::string& return false; } -std::string getLastError(DBClientBase* conn) { - BSONObj info; - BSONObjBuilder b; - b.append("getlasterror", 1); - conn->runCommand("admin", b.obj(), info); - - if (info["ok"].trueValue()) { - BSONElement e = info["err"]; - if (e.eoo()) - return ""; - if (e.type() == Object) - return e.toString(); - return e.str(); - } else { - // command failure - BSONElement e = info["errmsg"]; - if (e.eoo()) - return ""; - if (e.type() == Object) - return "getLastError command failed: " + e.toString(); - return "getLastError command failed: " + e.str(); - } +void getLastError(DBClientBase* conn) { + static const auto getLastErrorCommand = fromjson(R"({"getlasterror": 1})"); + BSONObj replyObj; + conn->runCommand("admin", getLastErrorCommand, replyObj); + + // getLastError command is no longer supported and must always fails. + auto status = getStatusFromCommandResult(replyObj); + ASSERT_NOT_OK(status) << replyObj; + const auto expectedCode = conn->isMongos() ? 5739001 : 5739000; + ASSERT_EQ(status.code(), expectedCode) << replyObj; } void exerciseDeprecatedOps(DBClientBase* conn, const std::string& expectedSeverity) { @@ -256,12 +244,12 @@ void exerciseDeprecatedOps(DBClientBase* conn, const std::string& expectedSeveri // The first deprecated call after adding a suppression is still logged with elevated severity // and after it the suppression kicks in. Any deprecated op can be used to start the suppression // period, here we chose getLastError. - ASSERT_EQ("", getLastError(conn)); + getLastError(conn); ASSERT_THROWS(conn->call(opInsert, ignore), ExceptionForCat<ErrorCategory::NetworkError>); ASSERT(wasLogged(conn, "insert", expectedSeverity)); - ASSERT_EQ("", getLastError(conn)); + getLastError(conn); ASSERT(wasLogged(conn, "getLastError", expectedSeverity)); ASSERT_THROWS(conn->call(opUpdate, ignore), ExceptionForCat<ErrorCategory::NetworkError>); @@ -340,7 +328,7 @@ TEST(OpLegacy, GenericCommandViaOpQuery) { // Because we cannot link the log entries to the issued commands, limit the search window for // the query-related entry in the log by first running a different command (e.g. getLastError). - ASSERT_EQ("", getLastError(conn.get())); // will start failing soon but will still log + getLastError(conn.get()); ASSERT(wasLogged(conn.get(), "getLastError", "")); // The actual command doesn't matter, as long as it's not 'hello' or 'isMaster'. @@ -377,7 +365,7 @@ void testAllowedCommand(const char* command) { // Because we cannot link the log entries to the issued commands, limit the search window for // the query-related entry in the log by first running a different command (e.g. getLastError). - ASSERT_EQ("", getLastError(conn.get())); // will start failing soon but will still log + getLastError(conn.get()); ASSERT(wasLogged(conn.get(), "getLastError", "")); auto opQuery = makeDeprecatedQueryMessage("testOpLegacy.$cmd", diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index 5b7c317cdd3..d24d3f61e6f 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -250,7 +250,6 @@ env.Library( '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/executor/thread_pool_task_executor', '$BUILD_DIR/mongo/s/client/sharding_client', - 'cluster_last_error_info', 'sharding_router_api', ], ) @@ -317,24 +316,12 @@ env.Library( ) env.Library( - target='cluster_last_error_info', - source=[ - 'cluster_last_error_info.cpp' - ], - LIBDEPS=[ - '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/client/connection_string', - ] -) - -env.Library( target='sharding_egress_metadata_hook_for_mongos', source=[ 'sharding_egress_metadata_hook_for_mongos.cpp' ], LIBDEPS=[ '$BUILD_DIR/mongo/rpc/metadata', - 'cluster_last_error_info', 'coreshard', ] ) @@ -518,7 +505,6 @@ env.Library( '$BUILD_DIR/mongo/util/periodic_runner_factory', '$BUILD_DIR/mongo/util/signal_handlers', 'client/sharding_client', - 'cluster_last_error_info', 'commands/cluster_commands', 'committed_optime_metadata_hook', 'mongos_initializers', @@ -601,7 +587,6 @@ env.CppUnitTest( 'chunk_writes_tracker_test.cpp', 'client/shard_remote_test.cpp', 'cluster_identity_loader_test.cpp', - 'cluster_last_error_info_test.cpp', 'comparable_chunk_version_test.cpp', 'comparable_database_version_test.cpp', 'hedge_options_util_test.cpp', @@ -642,7 +627,6 @@ env.CppUnitTest( '$BUILD_DIR/mongo/util/net/network', 'catalog/sharding_catalog_client_mock', 'chunk_writes_tracker', - 'cluster_last_error_info', 'coreshard', 'mongos_topology_coordinator', 'sessions_collection_sharded', diff --git a/src/mongo/s/cluster_last_error_info.cpp b/src/mongo/s/cluster_last_error_info.cpp deleted file mode 100644 index 2fe697df461..00000000000 --- a/src/mongo/s/cluster_last_error_info.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * 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 - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * 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 Server Side 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/platform/basic.h" - -#include "mongo/s/cluster_last_error_info.h" - -#include "mongo/client/connection_string.h" -#include "mongo/db/lasterror.h" - -namespace mongo { - -const Client::Decoration<std::shared_ptr<ClusterLastErrorInfo>> ClusterLastErrorInfo::get = - Client::declareDecoration<std::shared_ptr<ClusterLastErrorInfo>>(); - -void ClusterLastErrorInfo::addShardHost(const std::string& shardHost) { - stdx::lock_guard<Latch> lock(_mutex); - _cur->shardHostsWritten.insert(shardHost); -} - -void ClusterLastErrorInfo::addHostOpTime(ConnectionString connStr, HostOpTime stat) { - stdx::lock_guard<Latch> lock(_mutex); - _cur->hostOpTimes[connStr] = stat; -} - -void ClusterLastErrorInfo::addHostOpTimes(const HostOpTimeMap& hostOpTimes) { - for (HostOpTimeMap::const_iterator it = hostOpTimes.begin(); it != hostOpTimes.end(); ++it) { - addHostOpTime(it->first, it->second); - } -} - -void ClusterLastErrorInfo::newRequest() { - stdx::lock_guard<Latch> lock(_mutex); - std::swap(_cur, _prev); - _cur->clear(); -} - -void ClusterLastErrorInfo::disableForCommand() { - stdx::lock_guard<Latch> lock(_mutex); - RequestInfo* temp = _cur; - _cur = _prev; - _prev = temp; -} - -} // namespace mongo diff --git a/src/mongo/s/cluster_last_error_info.h b/src/mongo/s/cluster_last_error_info.h deleted file mode 100644 index af13045099d..00000000000 --- a/src/mongo/s/cluster_last_error_info.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * 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 - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * 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 Server Side 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. - */ -#pragma once - -#include <set> -#include <string> - -#include "mongo/db/client.h" -#include "mongo/s/write_ops/batch_write_exec.h" - -namespace mongo { - -/** - * Client decoration that holds information needed to by mongos to process - * getLastError commands. - */ -class ClusterLastErrorInfo { -public: - static const Client::Decoration<std::shared_ptr<ClusterLastErrorInfo>> get; - - /** new request not associated (yet or ever) with a client */ - void newRequest(); - - /** - * notes that this client use this shard - * keeps track of all shards accessed this request - */ - void addShardHost(const std::string& shardHost); - - /** - * Notes that this client wrote to these particular hosts with write commands. - */ - void addHostOpTime(ConnectionString connstr, HostOpTime stat); - void addHostOpTimes(const HostOpTimeMap& hostOpTimes); - - /** - * gets shards used on the previous request - */ - std::set<std::string>* getPrevShardHosts() const { - stdx::lock_guard<Latch> lock(_mutex); - return &_prev->shardHostsWritten; - } - - /** - * Gets the shards, hosts, and opTimes the client last wrote to with write commands. - */ - const HostOpTimeMap& getPrevHostOpTimes() const { - stdx::lock_guard<Latch> lock(_mutex); - return _prev->hostOpTimes; - } - - void disableForCommand(); - -private: - struct RequestInfo { - void clear() { - shardHostsWritten.clear(); - hostOpTimes.clear(); - } - - std::set<std::string> shardHostsWritten; - HostOpTimeMap hostOpTimes; - }; - - // Protects _infos, _cur, and _prev. - mutable Mutex _mutex = MONGO_MAKE_LATCH("ClusterLastErrorInfo::_mutex"); - - // We use 2 so we can flip for getLastError type operations. - RequestInfo _infos[2]; - RequestInfo* _cur = &_infos[0]; - RequestInfo* _prev = &_infos[1]; -}; - -} // namespace mongo diff --git a/src/mongo/s/cluster_last_error_info_test.cpp b/src/mongo/s/cluster_last_error_info_test.cpp deleted file mode 100644 index 0e210078413..00000000000 --- a/src/mongo/s/cluster_last_error_info_test.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * 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 - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * 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 Server Side 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. - */ - -#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kSharding - -#include "mongo/platform/basic.h" - -#include <vector> - -#include "mongo/db/operation_context.h" -#include "mongo/rpc/metadata/sharding_metadata.h" -#include "mongo/s/cluster_last_error_info.h" -#include "mongo/s/sharding_router_test_fixture.h" -#include "mongo/stdx/future.h" -#include "mongo/util/str.h" - -namespace mongo { -namespace { - -using executor::NetworkInterfaceMock; -using executor::RemoteCommandRequest; -using executor::RemoteCommandResponse; -using executor::TaskExecutor; - -using ClusterGetLastErrorTest = ShardingTestFixture; - -TEST_F(ClusterGetLastErrorTest, - ClusterLastErrorInfoUpdatedIfInitializedAndReplyHasShardingMetadata) { - auto client = operationContext()->getClient(); - - // Ensure the clusterGLE on the Client has not yet been initialized. - ASSERT(!ClusterLastErrorInfo::get(client)); - - // Initialize the cluster last error info for the client with a new request. - ClusterLastErrorInfo::get(client) = std::make_shared<ClusterLastErrorInfo>(); - ASSERT(ClusterLastErrorInfo::get(client)); - auto clusterGLE = ClusterLastErrorInfo::get(client); - clusterGLE->newRequest(); - - // Ensure the last error info is initially empty. - ASSERT_EQUALS(0ul, clusterGLE->getPrevHostOpTimes().size()); - - // Send a request over the ShardingTaskExecutor. - HostAndPort host("fakeHost", 12345); - auto future = launchAsync([&] { - const RemoteCommandRequest request(host, - "mydb", - BSON("unusued" - << "obj"), - operationContext()); - executor() - ->scheduleRemoteCommand( - request, [=](const executor::TaskExecutor::RemoteCommandCallbackArgs) -> void {}) - .status_with_transitional_ignore(); - }); - - // Make the reply contain ShardingMetadata. - repl::OpTime opTime{Timestamp{10, 10}, 10}; - onCommandWithMetadata([&](const RemoteCommandRequest& request) { - auto bob = BSONObjBuilder(BSON("ok" << 1)); - uassertStatusOK( - rpc::ShardingMetadata(opTime, OID() /* ignored OID field */).writeToMetadata(&bob)); - return RemoteCommandResponse(bob.obj(), Milliseconds(1)); - }); - - future.default_timed_get(); - - // Ensure the last error info was updated with the contacted host and returned opTime. - - // Call newRequest() to emulate that the client then sent a getLastError() command. This is - // required so ClusterLastErrorInfo moves its '_cur' list of hostOpTimes into its '_prev' list. - clusterGLE->newRequest(); - - ASSERT_EQUALS(1ul, clusterGLE->getPrevHostOpTimes().size()); - - auto storedHostOpTimeIt = clusterGLE->getPrevHostOpTimes().begin(); - ASSERT_EQUALS(host.toString(), storedHostOpTimeIt->first.toString()); - ASSERT_EQUALS(opTime, storedHostOpTimeIt->second.opTime); -} - -TEST_F(ClusterGetLastErrorTest, ClusterLastErrorInfoNotUpdatedIfNotInitialized) { - auto client = operationContext()->getClient(); - - // Ensure the clusterGLE on the Client has not been initialized. - ASSERT(!ClusterLastErrorInfo::get(client)); - - // Send a request over the ShardingTaskExecutor. - HostAndPort host("fakeHost", 12345); - auto future = launchAsync([&] { - const RemoteCommandRequest request(host, - "mydb", - BSON("unusued" - << "obj"), - operationContext()); - executor() - ->scheduleRemoteCommand( - request, [=](const executor::TaskExecutor::RemoteCommandCallbackArgs) -> void {}) - .status_with_transitional_ignore(); - }); - - // Make the reply contain ShardingMetadata. - repl::OpTime opTime{Timestamp{10, 10}, 10}; - onCommandWithMetadata([&](const RemoteCommandRequest& request) { - auto bob = BSONObjBuilder(BSON("ok" << 1)); - uassertStatusOK( - rpc::ShardingMetadata(opTime, OID() /* ignored OID field */).writeToMetadata(&bob)); - return RemoteCommandResponse(bob.obj(), Milliseconds(1)); - }); - - future.default_timed_get(); - - // Ensure the clusterGLE on the Client has still not been initialized. - ASSERT(!ClusterLastErrorInfo::get(client)); -} - -TEST_F(ClusterGetLastErrorTest, ClusterLastErrorInfoNotUpdatedIfReplyDoesntHaveShardingMetadata) { - auto client = operationContext()->getClient(); - - // Ensure the clusterGLE on the Client has not yet been initialized. - ASSERT(!ClusterLastErrorInfo::get(client)); - - // Initialize the cluster last error info for the client with a new request. - ClusterLastErrorInfo::get(client) = std::make_shared<ClusterLastErrorInfo>(); - ASSERT(ClusterLastErrorInfo::get(client)); - auto clusterGLE = ClusterLastErrorInfo::get(client); - clusterGLE->newRequest(); - - // Ensure the last error info is initially empty. - ASSERT_EQUALS(0ul, clusterGLE->getPrevHostOpTimes().size()); - - // Send a request over the ShardingTaskExecutor. - HostAndPort host("fakeHost", 12345); - auto future = launchAsync([&] { - const RemoteCommandRequest request(host, - "mydb", - BSON("unusued" - << "obj"), - operationContext()); - executor() - ->scheduleRemoteCommand( - request, [=](const executor::TaskExecutor::RemoteCommandCallbackArgs) -> void {}) - .status_with_transitional_ignore(); - }); - - // Do not return ShardingMetadata in the reply. - repl::OpTime opTime{Timestamp{10, 10}, 10}; - onCommandWithMetadata([&](const RemoteCommandRequest& request) { - return RemoteCommandResponse(BSON("ok" << 1), Milliseconds(1)); - }); - - future.default_timed_get(); - - // Ensure the last error info was not updated. - - // Call newRequest() to emulate that the client then sent a getLastError() command. This is - // required so ClusterLastErrorInfo moves its '_cur' list of hostOpTimes into its '_prev' list. - clusterGLE->newRequest(); - - // Ensure the last error info is still empty. - ASSERT_EQUALS(0ul, clusterGLE->getPrevHostOpTimes().size()); -} - -} // namespace -} // namespace mongo diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript index f0ab2c494ab..cda5515584f 100644 --- a/src/mongo/s/commands/SConscript +++ b/src/mongo/s/commands/SConscript @@ -22,7 +22,6 @@ env.Library( env.Library( target='cluster_commands', source=[ - 'batch_downconvert.cpp', 'cluster_abort_transaction_cmd.cpp', 'cluster_add_shard_cmd.cpp', 'cluster_add_shard_to_zone_cmd.cpp', @@ -152,7 +151,6 @@ env.Library( '$BUILD_DIR/mongo/idl/server_parameter', '$BUILD_DIR/mongo/rpc/client_metadata', '$BUILD_DIR/mongo/rpc/rewrite_state_change_errors', - '$BUILD_DIR/mongo/s/cluster_last_error_info', '$BUILD_DIR/mongo/s/mongos_topology_coordinator', '$BUILD_DIR/mongo/s/query/cluster_aggregate', '$BUILD_DIR/mongo/s/query/cluster_client_cursor', diff --git a/src/mongo/s/commands/batch_downconvert.cpp b/src/mongo/s/commands/batch_downconvert.cpp deleted file mode 100644 index 4f4328fe4c6..00000000000 --- a/src/mongo/s/commands/batch_downconvert.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * 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 - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * 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 Server Side 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/platform/basic.h" - -#include "mongo/s/commands/batch_downconvert.h" - -#include "mongo/bson/bsonmisc.h" -#include "mongo/db/storage/duplicate_key_error_info.h" - -namespace mongo { - -Status extractGLEErrors(const BSONObj& gleResponse, GLEErrors* errors) { - // DRAGONS - // Parsing GLE responses is incredibly finicky. - // The order of testing here is extremely important. - - /////////////////////////////////////////////////////////////////////// - // IMPORTANT! - // Also update extractGLEErrors in batch_api.js for any changes made here. - - const bool isOK = gleResponse["ok"].trueValue(); - const std::string err = gleResponse["err"].str(); - const std::string errMsg = gleResponse["errmsg"].str(); - const std::string wNote = gleResponse["wnote"].str(); - const std::string jNote = gleResponse["jnote"].str(); - const int code = gleResponse["code"].numberInt(); - const bool timeout = gleResponse["wtimeout"].trueValue(); - - if (err == "norepl" || err == "noreplset") { - // Know this is legacy gle and the repl not enforced - write concern error in 2.4 - errors->wcError.reset(new WriteConcernErrorDetail); - std::string msg; - if (!errMsg.empty()) { - msg = errMsg; - } else if (!wNote.empty()) { - msg = wNote; - } else { - msg = err; - } - errors->wcError->setStatus({ErrorCodes::WriteConcernFailed, msg}); - } else if (timeout) { - // Know there was no write error - errors->wcError.reset(new WriteConcernErrorDetail); - std::string msg; - if (!errMsg.empty()) { - msg = errMsg; - } else { - msg = err; - } - errors->wcError->setStatus({ErrorCodes::WriteConcernFailed, msg}); - errors->wcError->setErrInfo(BSON("wtimeout" << true)); - } else if (code == 10990 /* no longer primary */ - || code == 16805 /* replicatedToNum no longer primary */ - || code == 14830 /* gle wmode changed / invalid */ - // 2.6 Error codes - || code == ErrorCodes::NotWritablePrimary || - code == ErrorCodes::UnknownReplWriteConcern || - code == ErrorCodes::WriteConcernFailed || code == ErrorCodes::PrimarySteppedDown) { - // Write concern errors that get returned as regular errors (result may not be ok: 1.0) - errors->wcError.reset(new WriteConcernErrorDetail()); - errors->wcError->setStatus({ErrorCodes::Error(code), errMsg}); - } else if (!isOK) { - // - // !!! SOME GLE ERROR OCCURRED, UNKNOWN WRITE RESULT !!! - // - - return Status(ErrorCodes::Error(code ? code : ErrorCodes::UnknownError), errMsg); - } else if (!err.empty()) { - // Write error - errors->writeError.reset(new WriteErrorDetail); - int writeErrorCode = code == 0 ? ErrorCodes::UnknownError : code; - errors->writeError->setStatus({ErrorCodes::Error(writeErrorCode), err}); - } else if (!jNote.empty()) { - // Know this is legacy gle and the journaling not enforced - write concern error in 2.4 - errors->wcError.reset(new WriteConcernErrorDetail); - errors->wcError->setStatus({ErrorCodes::WriteConcernFailed, jNote}); - } - - return Status::OK(); -} - -/** - * Suppress the "err" and "code" field if they are coming from a previous write error and - * are not related to write concern. Also removes any write stats information (e.g. "n") - * - * Also, In some cases, 2.4 GLE w/ wOpTime can give us duplicate "err" and "code" fields b/c of - * reporting a previous error. The later field is what we want - dedup and use later field. - * - * Returns the stripped GLE response. - */ -BSONObj stripNonWCInfo(const BSONObj& gleResponse) { - BSONObjIterator it(gleResponse); - BSONObjBuilder builder; - - BSONElement codeField; // eoo - BSONElement errField; // eoo - - while (it.more()) { - BSONElement el = it.next(); - StringData fieldName(el.fieldName()); - if (fieldName.compare("err") == 0) { - errField = el; - } else if (fieldName.compare("code") == 0) { - codeField = el; - } else if (fieldName.compare("n") == 0 || fieldName.compare("nModified") == 0 || - fieldName.compare("upserted") == 0 || - fieldName.compare("updatedExisting") == 0) { - // Suppress field - } else { - builder.append(el); - } - } - - if (!codeField.eoo()) { - if (!gleResponse["ok"].trueValue()) { - // The last code will be from the write concern - builder.append(codeField); - } else { - // The code is from a non-wc error on this connection - suppress it - } - } - - if (!errField.eoo()) { - std::string err = errField.str(); - if (err == "norepl" || err == "noreplset" || err == "timeout") { - // Append err if it's from a write concern issue - builder.append(errField); - } else { - // Suppress non-write concern err as null, but we need to report null err if ok - if (gleResponse["ok"].trueValue()) - builder.appendNull(errField.fieldName()); - } - } - - return builder.obj(); -} - -} // namespace mongo diff --git a/src/mongo/s/commands/batch_downconvert.h b/src/mongo/s/commands/batch_downconvert.h deleted file mode 100644 index 5f553475ea6..00000000000 --- a/src/mongo/s/commands/batch_downconvert.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * 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 - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * 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 Server Side 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. - */ - -#pragma once - -#include "mongo/base/status.h" -#include "mongo/base/string_data.h" -#include "mongo/bson/bsonobj.h" -#include "mongo/rpc/write_concern_error_detail.h" -#include "mongo/s/write_ops/write_error_detail.h" - -namespace mongo { - -// Used for reporting legacy write concern responses -struct LegacyWCResponse { - std::string shardHost; - BSONObj gleResponse; - std::string errToReport; -}; - -// -// Below exposed for testing only -// - -// Helper that acts as an auto-ptr for write and wc errors -struct GLEErrors { - std::unique_ptr<WriteErrorDetail> writeError; - std::unique_ptr<WriteConcernErrorDetail> wcError; -}; - -/** - * Given a GLE response, extracts a write error and a write concern error for the previous - * operation. - * - * Returns !OK if the GLE itself failed in an unknown way. - */ -Status extractGLEErrors(const BSONObj& gleResponse, GLEErrors* errors); - -/** - * Given a GLE response, strips out all non-write-concern related information - */ -BSONObj stripNonWCInfo(const BSONObj& gleResponse); - -} // namespace mongo diff --git a/src/mongo/s/commands/batch_downconvert_test.cpp b/src/mongo/s/commands/batch_downconvert_test.cpp deleted file mode 100644 index 8f32e9ee5e0..00000000000 --- a/src/mongo/s/commands/batch_downconvert_test.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * 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 - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * 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 Server Side 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/platform/basic.h" - -#include <deque> -#include <vector> - -#include "mongo/db/json.h" -#include "mongo/s/commands/batch_downconvert.h" -#include "mongo/unittest/unittest.h" - -namespace mongo { -namespace { - -// -// Tests for parsing GLE responses into write errors and write concern errors for write -// commands. These tests essentially document our expected 2.4 GLE behaviors. -// - -TEST(GLEParsing, Empty) { - const BSONObj gleResponse = fromjson("{ok: 1.0, err: null}"); - - GLEErrors errors; - ASSERT_OK(extractGLEErrors(gleResponse, &errors)); - ASSERT(!errors.writeError.get()); - ASSERT(!errors.wcError.get()); -} - -TEST(GLEParsing, WriteErr) { - const BSONObj gleResponse = fromjson("{ok: 1.0, err: 'message', code: 1000}"); - - GLEErrors errors; - ASSERT_OK(extractGLEErrors(gleResponse, &errors)); - ASSERT(errors.writeError.get()); - ASSERT_EQUALS(errors.writeError->toStatus().reason(), "message"); - ASSERT_EQUALS(errors.writeError->toStatus().code(), 1000); - ASSERT(!errors.wcError.get()); -} - -TEST(GLEParsing, JournalFail) { - const BSONObj gleResponse = fromjson("{ok: 1.0, err: null, jnote: 'message'}"); - - GLEErrors errors; - ASSERT_OK(extractGLEErrors(gleResponse, &errors)); - ASSERT(!errors.writeError.get()); - ASSERT(errors.wcError.get()); - ASSERT_EQUALS(errors.wcError->toStatus().reason(), "message"); - ASSERT_EQUALS(errors.wcError->toStatus().code(), ErrorCodes::WriteConcernFailed); -} - -TEST(GLEParsing, ReplErr) { - const BSONObj gleResponse = fromjson("{ok: 1.0, err: 'norepl', wnote: 'message'}"); - - GLEErrors errors; - ASSERT_OK(extractGLEErrors(gleResponse, &errors)); - ASSERT(!errors.writeError.get()); - ASSERT(errors.wcError.get()); - ASSERT_EQUALS(errors.wcError->toStatus().reason(), "message"); - ASSERT_EQUALS(errors.wcError->toStatus().code(), ErrorCodes::WriteConcernFailed); -} - -TEST(GLEParsing, ReplTimeoutErr) { - const BSONObj gleResponse = - fromjson("{ok: 1.0, err: 'timeout', errmsg: 'message', wtimeout: true}"); - - GLEErrors errors; - ASSERT_OK(extractGLEErrors(gleResponse, &errors)); - ASSERT(!errors.writeError.get()); - ASSERT(errors.wcError.get()); - ASSERT_EQUALS(errors.wcError->toStatus().reason(), - "message; Error details: { wtimeout: true }"); - ASSERT(errors.wcError->getErrInfo()["wtimeout"].trueValue()); - ASSERT_EQUALS(errors.wcError->toStatus().code(), ErrorCodes::WriteConcernFailed); -} - -TEST(GLEParsing, GLEFail) { - const BSONObj gleResponse = fromjson("{ok: 0.0, err: null, errmsg: 'message', code: 1000}"); - - GLEErrors errors; - Status status = extractGLEErrors(gleResponse, &errors); - ASSERT_NOT_OK(status); - ASSERT_EQUALS(status.reason(), "message"); - ASSERT_EQUALS(status.code(), 1000); -} - -TEST(GLEParsing, GLEFailNoCode) { - const BSONObj gleResponse = fromjson("{ok: 0.0, err: null, errmsg: 'message'}"); - - GLEErrors errors; - Status status = extractGLEErrors(gleResponse, &errors); - ASSERT_NOT_OK(status); - ASSERT_EQUALS(status.reason(), "message"); - ASSERT_EQUALS(status.code(), ErrorCodes::UnknownError); -} - -TEST(GLEParsing, NotMasterGLEFail) { - // Not master code in response - const BSONObj gleResponse = fromjson("{ok: 0.0, err: null, errmsg: 'message', code: 10990}"); - - GLEErrors errors; - ASSERT_OK(extractGLEErrors(gleResponse, &errors)); - ASSERT(!errors.writeError.get()); - ASSERT(errors.wcError.get()); - ASSERT_EQUALS(errors.wcError->toStatus().reason(), "message"); - ASSERT_EQUALS(errors.wcError->toStatus().code(), 10990); -} - -TEST(GLEParsing, WriteErrWithStats) { - const BSONObj gleResponse = fromjson("{ok: 1.0, n: 2, err: 'message', code: 1000}"); - - GLEErrors errors; - ASSERT_OK(extractGLEErrors(gleResponse, &errors)); - ASSERT(errors.writeError.get()); - ASSERT_EQUALS(errors.writeError->toStatus().reason(), "message"); - ASSERT_EQUALS(errors.writeError->toStatus().code(), 1000); - ASSERT(!errors.wcError.get()); -} - -TEST(GLEParsing, ReplTimeoutErrWithStats) { - const BSONObj gleResponse = fromjson( - "{ok: 1.0, err: 'timeout', errmsg: 'message', wtimeout: true," - " n: 1, upserted: 'abcde'}"); - - GLEErrors errors; - ASSERT_OK(extractGLEErrors(gleResponse, &errors)); - ASSERT(!errors.writeError.get()); - ASSERT(errors.wcError.get()); - ASSERT_EQUALS(errors.wcError->toStatus().reason(), - "message; Error details: { wtimeout: true }"); - ASSERT(errors.wcError->getErrInfo()["wtimeout"].trueValue()); - ASSERT_EQUALS(errors.wcError->toStatus().code(), ErrorCodes::WriteConcernFailed); -} - -// -// Tests of processing and suppressing non-WC related fields from legacy GLE responses -// - -TEST(LegacyGLESuppress, Basic) { - const BSONObj gleResponse = fromjson("{ok: 1.0, err: null}"); - - BSONObj stripped = stripNonWCInfo(gleResponse); - ASSERT_EQUALS(stripped.nFields(), 2); // with err, ok : true - ASSERT(stripped["ok"].trueValue()); -} - -TEST(LegacyGLESuppress, BasicStats) { - const BSONObj gleResponse = fromjson( - "{ok: 0.0, err: 'message'," - " n: 1, nModified: 1, upserted: 'abc', updatedExisting: true}"); - - BSONObj stripped = stripNonWCInfo(gleResponse); - ASSERT_EQUALS(stripped.nFields(), 1); - ASSERT(!stripped["ok"].trueValue()); -} - -TEST(LegacyGLESuppress, ReplError) { - const BSONObj gleResponse = fromjson("{ok: 0.0, err: 'norepl', n: 1, wcField: true}"); - - BSONObj stripped = stripNonWCInfo(gleResponse); - ASSERT_EQUALS(stripped.nFields(), 3); - ASSERT(!stripped["ok"].trueValue()); - ASSERT_EQUALS(stripped["err"].str(), "norepl"); - ASSERT(stripped["wcField"].trueValue()); -} - -TEST(LegacyGLESuppress, StripCode) { - const BSONObj gleResponse = fromjson("{ok: 1.0, err: 'message', code: 12345}"); - - BSONObj stripped = stripNonWCInfo(gleResponse); - ASSERT_EQUALS(stripped.nFields(), 2); // with err, ok : true - ASSERT(stripped["ok"].trueValue()); -} - -TEST(LegacyGLESuppress, TimeoutDupError24) { - const BSONObj gleResponse = BSON("ok" << 0.0 << "err" - << "message" - << "code" << 12345 << "err" - << "timeout" - << "code" << 56789 << "wtimeout" << true); - - BSONObj stripped = stripNonWCInfo(gleResponse); - ASSERT_EQUALS(stripped.nFields(), 4); - ASSERT(!stripped["ok"].trueValue()); - ASSERT_EQUALS(stripped["err"].str(), "timeout"); - ASSERT_EQUALS(stripped["code"].numberInt(), 56789); - ASSERT(stripped["wtimeout"].trueValue()); -} - -} // namespace -} // namespace mongo diff --git a/src/mongo/s/commands/cluster_command_test_fixture.cpp b/src/mongo/s/commands/cluster_command_test_fixture.cpp index c7573969bd7..6b1daa92292 100644 --- a/src/mongo/s/commands/cluster_command_test_fixture.cpp +++ b/src/mongo/s/commands/cluster_command_test_fixture.cpp @@ -41,7 +41,6 @@ #include "mongo/db/logical_time_validator.h" #include "mongo/db/read_write_concern_defaults.h" #include "mongo/db/vector_clock.h" -#include "mongo/s/cluster_last_error_info.h" #include "mongo/util/fail_point.h" #include "mongo/util/options_parser/startup_option_init.h" #include "mongo/util/tick_source_mock.h" @@ -127,15 +126,6 @@ DbResponse ClusterCommandTestFixture::runCommand(BSONObj cmd) { const auto opMsgRequest = OpMsgRequest::fromDBAndBody(kNss.db(), cmd); - // Ensure the clusterGLE on the Client has not yet been initialized. - ASSERT(!ClusterLastErrorInfo::get(client.get())); - - // Initialize the cluster last error info for the client with a new request. - ClusterLastErrorInfo::get(client.get()) = std::make_shared<ClusterLastErrorInfo>(); - ASSERT(ClusterLastErrorInfo::get(client.get())); - auto clusterGLE = ClusterLastErrorInfo::get(client.get()); - clusterGLE->newRequest(); - AlternativeClientRegion acr(client); auto rec = std::make_shared<RequestExecutionContext>(opCtx.get(), opMsgRequest.serialize()); return Strategy::clientCommand(std::move(rec)).get(); 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 34482d50ccb..9a1d73c015b 100644 --- a/src/mongo/s/commands/cluster_get_last_error_cmd.cpp +++ b/src/mongo/s/commands/cluster_get_last_error_cmd.cpp @@ -27,176 +27,14 @@ * it in the license file. */ -#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kSharding - #include "mongo/platform/basic.h" -#include <vector> - -#include "mongo/client/remote_command_targeter.h" -#include "mongo/db/client.h" #include "mongo/db/commands.h" -#include "mongo/db/lasterror.h" -#include "mongo/executor/task_executor_pool.h" -#include "mongo/logv2/log.h" #include "mongo/rpc/warn_deprecated_wire_ops.h" -#include "mongo/s/client/shard_registry.h" -#include "mongo/s/cluster_commands_helpers.h" -#include "mongo/s/cluster_last_error_info.h" -#include "mongo/s/commands/batch_downconvert.h" -#include "mongo/s/grid.h" -#include "mongo/s/multi_statement_transaction_requests_sender.h" namespace mongo { namespace { -using std::vector; - -// Adds a wOpTime and a wElectionId field to a set of gle options -BSONObj buildGLECmdWithOpTime(const BSONObj& gleOptions, - const repl::OpTime& opTime, - const OID& electionId) { - BSONObjBuilder builder; - BSONObjIterator it(gleOptions); - - for (int i = 0; it.more(); ++i) { - BSONElement el = it.next(); - - // Make sure first element is getLastError : 1 - if (i == 0) { - StringData elName(el.fieldName()); - if (!elName.equalCaseInsensitive("getLastError")) { - builder.append("getLastError", 1); - } - } - - builder.append(el); - } - opTime.append(&builder, "wOpTime"); - builder.appendOID("wElectionId", const_cast<OID*>(&electionId)); - return builder.obj(); -} - -/** - * Uses GLE and the shard hosts and opTimes last written by write commands to enforce a - * write concern across the previously used shards. - * - * Returns OK with the LegacyWCResponses containing only write concern error information - * Returns !OK if there was an error getting a GLE response - */ -Status enforceLegacyWriteConcern(OperationContext* opCtx, - StringData dbName, - const BSONObj& options, - const HostOpTimeMap& hostOpTimes, - std::vector<LegacyWCResponse>* legacyWCResponses) { - if (hostOpTimes.empty()) { - return Status::OK(); - } - - // Assemble requests - std::vector<AsyncRequestsSender::Request> requests; - for (HostOpTimeMap::const_iterator it = hostOpTimes.begin(); it != hostOpTimes.end(); ++it) { - const ConnectionString& shardConnStr = it->first; - const auto& hot = it->second; - const repl::OpTime& opTime = hot.opTime; - const OID& electionId = hot.electionId; - - auto swShard = Grid::get(opCtx)->shardRegistry()->getShard(opCtx, shardConnStr.toString()); - if (!swShard.isOK()) { - return swShard.getStatus(); - } - - LOGV2_DEBUG(22752, - 3, - "Enforcing write concern {options} on {shardId} at opTime " - "{opTime} with electionID {electionId}", - "Enforcing write concern on shard", - "options"_attr = options, - "shardId"_attr = swShard.getValue()->getId(), - "opTime"_attr = opTime.getTimestamp().toStringPretty(), - "electionId"_attr = electionId); - - BSONObj gleCmd = buildGLECmdWithOpTime(options, opTime, electionId); - requests.emplace_back(swShard.getValue()->getId(), gleCmd); - } - - // Send the requests. - - const ReadPreferenceSetting readPref(ReadPreference::PrimaryOnly, TagSet()); - MultiStatementTransactionRequestsSender ars( - opCtx, - Grid::get(opCtx)->getExecutorPool()->getArbitraryExecutor(), - dbName.toString(), - requests, - readPref, - Shard::RetryPolicy::kIdempotent); - - // Receive the responses. - - vector<Status> failedStatuses; - while (!ars.done()) { - // Block until a response is available. - auto response = ars.next(); - - // Return immediately if we failed to contact a shard. - if (!response.shardHostAndPort) { - invariant(!response.swResponse.isOK()); - return response.swResponse.getStatus(); - } - - // We successfully contacted the shard, but it returned some error. - if (!response.swResponse.isOK()) { - failedStatuses.push_back(std::move(response.swResponse.getStatus())); - continue; - } - - BSONObj gleResponse = stripNonWCInfo(response.swResponse.getValue().data); - - // Use the downconversion tools to determine if this GLE response is ok, a - // write concern error, or an unknown error we should immediately abort for. - GLEErrors errors; - Status extractStatus = extractGLEErrors(gleResponse, &errors); - if (!extractStatus.isOK()) { - failedStatuses.push_back(extractStatus); - continue; - } - - LegacyWCResponse wcResponse; - invariant(response.shardHostAndPort); - wcResponse.shardHost = response.shardHostAndPort->toString(); - wcResponse.gleResponse = gleResponse; - if (errors.wcError.get()) { - wcResponse.errToReport = errors.wcError->toString(); - } - - legacyWCResponses->push_back(wcResponse); - } - - if (failedStatuses.empty()) { - return Status::OK(); - } - - StringBuilder builder; - builder << "could not enforce write concern"; - - for (vector<Status>::const_iterator it = failedStatuses.begin(); it != failedStatuses.end(); - ++it) { - const Status& failedStatus = *it; - if (it == failedStatuses.begin()) { - builder << causedBy(failedStatus.toString()); - } else { - builder << ":: and ::" << failedStatus.toString(); - } - } - - if (failedStatuses.size() == 1u) { - return failedStatuses.front(); - } else { - return Status(ErrorCodes::MultipleErrorsOccurred, builder.str()); - } -} - - class GetLastErrorCmd : public BasicCommand { public: GetLastErrorCmd() : BasicCommand("getLastError", "getlasterror") {} @@ -210,7 +48,7 @@ public: } std::string help() const override { - return "check for an error on the last command executed"; + return "no longer supported"; } virtual void addRequiredPrivileges(const std::string& dbname, @@ -223,123 +61,10 @@ public: return false; } - virtual bool run(OperationContext* opCtx, - const std::string& dbname, - const BSONObj& cmdObj, - BSONObjBuilder& result) { - // Mongos GLE - finicky. - // - // To emulate mongod, we first append any write errors we had, then try to append - // write concern error if there was no write error. We need to contact the previous - // shards regardless to maintain 2.4 behavior. - // - // If there are any unexpected or connectivity errors when calling GLE, fail the - // command. - // - // Finally, report the write concern errors IF we don't already have an error. - // If we only get one write concern error back, report that, otherwise report an - // aggregated error. - // - // TODO: Do we need to contact the prev shards regardless - do we care that much - // about 2.4 behavior? - // - - LastError* le = &LastError::get(cc()); - le->disable(); - + virtual bool run(OperationContext*, const std::string&, const BSONObj&, BSONObjBuilder&) { warnDeprecation(cc(), "getLastError"); - - // Write commands always have the error stored in the mongos last error - bool errorOccurred = false; - if (le->getNPrev() == 1) { - errorOccurred = le->appendSelf(result, false); - } - - // For compatibility with 2.4 sharded GLE, we always enforce the write concern - // across all shards. - const HostOpTimeMap hostOpTimes(ClusterLastErrorInfo::get(cc())->getPrevHostOpTimes()); - - std::vector<LegacyWCResponse> wcResponses; - auto status = enforceLegacyWriteConcern( - opCtx, - dbname, - applyReadWriteConcern( - opCtx, this, CommandHelpers::filterCommandRequestForPassthrough(cmdObj)), - hostOpTimes, - &wcResponses); - - // Don't forget about our last hosts, reset the client info - ClusterLastErrorInfo::get(cc())->disableForCommand(); - - // We're now done contacting all remote servers, just report results - - if (!status.isOK()) { - // Return immediately if we failed to contact a shard, unexpected GLE issue - // Can't return code, since it may have been set above (2.4 compatibility) - result.append("errmsg", status.reason()); - return false; - } - - // Go through all the write concern responses and find errors - BSONArrayBuilder shards; - BSONObjBuilder shardRawGLE; - BSONArrayBuilder errors; - BSONArrayBuilder errorRawGLE; - - int numWCErrors = 0; - const LegacyWCResponse* lastErrResponse = nullptr; - - for (std::vector<LegacyWCResponse>::const_iterator it = wcResponses.begin(); - it != wcResponses.end(); - ++it) { - const LegacyWCResponse& wcResponse = *it; - - shards.append(wcResponse.shardHost); - shardRawGLE.append(wcResponse.shardHost, wcResponse.gleResponse); - - if (!wcResponse.errToReport.empty()) { - numWCErrors++; - lastErrResponse = &wcResponse; - errors.append(wcResponse.errToReport); - errorRawGLE.append(wcResponse.gleResponse); - } - } - - // Always report what we found to match 2.4 behavior and for debugging - if (wcResponses.size() == 1u) { - result.append("singleShard", wcResponses.front().shardHost); - } else { - result.append("shards", shards.arr()); - result.append("shardRawGLE", shardRawGLE.obj()); - } - - // Suppress write concern errors if a write error occurred, to match mongod behavior - if (errorOccurred || numWCErrors == 0) { - // Still need to return err - if (!errorOccurred) { - result.appendNull("err"); - } - - return true; - } - - if (numWCErrors == 1) { - // Return the single write concern error we found, err should be set or not - // from gle response - CommandHelpers::filterCommandReplyForPassthrough(lastErrResponse->gleResponse, &result); - return lastErrResponse->gleResponse["ok"].trueValue(); - } else { - // Return a generic combined WC error message - result.append("errs", errors.arr()); - result.append("errObjects", errorRawGLE.arr()); - - // Need to always return err - result.appendNull("err"); - - return CommandHelpers::appendCommandStatusNoThrow( - result, - Status(ErrorCodes::WriteConcernFailed, "multiple write concern errors occurred")); - } + uasserted(5739001, "getLastError command is not supported"); + return false; } } cmdGetLastError; 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 e4a0a10940b..0cb9e7530eb 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 @@ -32,7 +32,6 @@ #include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/lasterror.h" -#include "mongo/s/cluster_last_error_info.h" namespace mongo { namespace { @@ -72,7 +71,6 @@ public: BSONObjBuilder& result) { if (cmdObj["forShell"].trueValue()) { LastError::get(cc()).disable(); - ClusterLastErrorInfo::get(cc())->disableForCommand(); } errmsg = "replSetGetStatus is not supported through mongos"; diff --git a/src/mongo/s/commands/cluster_write_cmd.cpp b/src/mongo/s/commands/cluster_write_cmd.cpp index 189a7b273c3..e4d41e28f23 100644 --- a/src/mongo/s/commands/cluster_write_cmd.cpp +++ b/src/mongo/s/commands/cluster_write_cmd.cpp @@ -50,7 +50,6 @@ #include "mongo/s/client/num_hosts_targeted_metrics.h" #include "mongo/s/client/shard_registry.h" #include "mongo/s/cluster_commands_helpers.h" -#include "mongo/s/cluster_last_error_info.h" #include "mongo/s/cluster_write.h" #include "mongo/s/commands/cluster_explain.h" #include "mongo/s/commands/document_shard_key_update_util.h" @@ -547,9 +546,6 @@ private: break; } - // Save the last opTimes written on each shard for this client, to allow GLE to work - ClusterLastErrorInfo::get(opCtx->getClient())->addHostOpTimes(stats.getWriteOpTimes()); - // Record the number of shards targeted by this write. CurOp::get(opCtx)->debug().nShards = stats.getTargetedShards().size() + (updatedShardKey ? 1 : 0); diff --git a/src/mongo/s/mongos_main.cpp b/src/mongo/s/mongos_main.cpp index faa63d10d06..ef0db47b802 100644 --- a/src/mongo/s/mongos_main.cpp +++ b/src/mongo/s/mongos_main.cpp @@ -55,7 +55,6 @@ #include "mongo/db/ftdc/ftdc_mongos.h" #include "mongo/db/initialize_server_global_state.h" #include "mongo/db/kill_sessions.h" -#include "mongo/db/lasterror.h" #include "mongo/db/log_process_details.h" #include "mongo/db/logical_session_cache_impl.h" #include "mongo/db/logical_time_validator.h" diff --git a/src/mongo/s/service_entry_point_mongos.cpp b/src/mongo/s/service_entry_point_mongos.cpp index 1e9b1664ae8..298432a3c0f 100644 --- a/src/mongo/s/service_entry_point_mongos.cpp +++ b/src/mongo/s/service_entry_point_mongos.cpp @@ -48,7 +48,6 @@ #include "mongo/logv2/log.h" #include "mongo/rpc/message.h" #include "mongo/rpc/warn_deprecated_wire_ops.h" -#include "mongo/s/cluster_last_error_info.h" #include "mongo/s/commands/strategy.h" namespace mongo { @@ -74,7 +73,7 @@ struct HandleRequest : public std::enable_shared_from_this<HandleRequest> { msgId(message.header().getId()), nsString(getNamespaceString(rec->getDbMessage())) {} - // Prepares the environment for handling the request (e.g., setting up `ClusterLastErrorInfo`). + // Prepares the environment for handling the request. void setupEnvironment(); // Returns a future that does the heavy lifting of running client commands. @@ -114,10 +113,6 @@ void HandleRequest::setupEnvironment() { // Start a new LastError session. Any exceptions thrown from here onwards will be returned // to the caller (if the type of the message permits it). auto client = opCtx->getClient(); - if (!ClusterLastErrorInfo::get(client)) { - ClusterLastErrorInfo::get(client) = std::make_shared<ClusterLastErrorInfo>(); - } - ClusterLastErrorInfo::get(client)->newRequest(); LastError::get(client).startRequest(); AuthorizationSession::get(opCtx->getClient())->startRequest(opCtx); diff --git a/src/mongo/s/sharding_egress_metadata_hook_for_mongos.cpp b/src/mongo/s/sharding_egress_metadata_hook_for_mongos.cpp index a01cb4eab33..90c58dc6ec8 100644 --- a/src/mongo/s/sharding_egress_metadata_hook_for_mongos.cpp +++ b/src/mongo/s/sharding_egress_metadata_hook_for_mongos.cpp @@ -34,8 +34,6 @@ #include "mongo/s/sharding_egress_metadata_hook_for_mongos.h" #include "mongo/db/client.h" -#include "mongo/rpc/metadata/sharding_metadata.h" -#include "mongo/s/cluster_last_error_info.h" #include "mongo/s/grid.h" namespace mongo { diff --git a/src/mongo/s/sharding_task_executor.cpp b/src/mongo/s/sharding_task_executor.cpp index 00eaecec9bc..52d3838fafd 100644 --- a/src/mongo/s/sharding_task_executor.cpp +++ b/src/mongo/s/sharding_task_executor.cpp @@ -40,9 +40,7 @@ #include "mongo/executor/thread_pool_task_executor.h" #include "mongo/logv2/log.h" #include "mongo/rpc/get_status_from_command_result.h" -#include "mongo/rpc/metadata/sharding_metadata.h" #include "mongo/s/client/shard_registry.h" -#include "mongo/s/cluster_last_error_info.h" #include "mongo/s/grid.h" #include "mongo/s/is_mongos.h" #include "mongo/s/transaction_router.h" @@ -166,13 +164,7 @@ StatusWith<TaskExecutor::CallbackHandle> ShardingTaskExecutor::scheduleRemoteCom std::shared_ptr<OperationTimeTracker> timeTracker = OperationTimeTracker::get(request.opCtx); - auto clusterGLE = ClusterLastErrorInfo::get(request.opCtx->getClient()); - - auto shardingCb = [timeTracker, - clusterGLE, - cb, - grid = Grid::get(request.opCtx), - hosts = request.target]( + auto shardingCb = [timeTracker, cb, grid = Grid::get(request.opCtx), hosts = request.target]( const TaskExecutor::RemoteCommandOnAnyCallbackArgs& args) { ON_BLOCK_EXIT([&cb, &args]() { cb(args); }); @@ -237,34 +229,6 @@ StatusWith<TaskExecutor::CallbackHandle> ShardingTaskExecutor::scheduleRemoteCom invariant(operationTime.type() == BSONType::bsonTimestamp); timeTracker->updateOperationTime(LogicalTime(operationTime.timestamp())); } - - // Update getLastError info for the client if we're tracking it. - if (clusterGLE) { - auto swShardingMetadata = rpc::ShardingMetadata::readFromMetadata(args.response.data); - if (swShardingMetadata.isOK()) { - auto shardingMetadata = std::move(swShardingMetadata.getValue()); - - auto shardConn = ConnectionString::parse(target.toString()); - if (!shardConn.isOK()) { - LOGV2_ERROR(22874, - "Could not parse connection string to update getLastError stats: " - "{connectionString}", - "Could not parse connection string to update getLastError stats", - "connectionString"_attr = target); - } - - clusterGLE->addHostOpTime(shardConn.getValue(), - HostOpTime(shardingMetadata.getLastOpTime(), - shardingMetadata.getLastElectionId())); - } else if (swShardingMetadata.getStatus() != ErrorCodes::NoSuchKey) { - LOGV2_WARNING(22872, - "Got invalid sharding metadata {error} " - "metadata object was '{response}'", - "Could not parse sharding metadata from response", - "error"_attr = redact(swShardingMetadata.getStatus()), - "response"_attr = redact(args.response.data)); - } - } }; return _executor->scheduleRemoteCommandOnAny( |