Skip to content

Commit

Permalink
SERVER-24611 Implement ClientMetadata class
Browse files Browse the repository at this point in the history
  • Loading branch information
markbenvenuto committed Aug 4, 2016
1 parent 931a227 commit ad27c92
Show file tree
Hide file tree
Showing 43 changed files with 1,325 additions and 65 deletions.
12 changes: 12 additions & 0 deletions jstests/core/client_metadata_ismaster.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,12 @@
// Test that verifies client metadata behavior for isMaster

(function() {
"use strict";

// Verify that a isMaster request fails if it contains client metadata, and it is not first.
// The shell sends isMaster on the first connection
var result = db.runCommand({"isMaster": 1, "client": {"application": "foobar"}});
assert.commandFailed(result);
assert.eq(result.code, ErrorCodes.ClientMetadataCannotBeMutated, tojson(result));

})();
2 changes: 2 additions & 0 deletions jstests/noPassthrough/currentop_query.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
assert.commandWorked(result); assert.commandWorked(result);


if (result.inprog.length === 1) { if (result.inprog.length === 1) {
assert.eq(result.inprog[0].appName, "MongoDB Shell", tojson(result));

return true; return true;
} }


Expand Down
4 changes: 4 additions & 0 deletions src/mongo/base/error_codes.err
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ error_code("WindowsPdhError", 179)
error_code("BadPerfCounterPath", 180) error_code("BadPerfCounterPath", 180)
error_code("AmbiguousIndexKeyPattern", 181) error_code("AmbiguousIndexKeyPattern", 181)
error_code("InvalidViewDefinition", 182); error_code("InvalidViewDefinition", 182);
error_code("ClientMetadataMissingField", 183)
error_code("ClientMetadataAppNameTooLarge", 184)
error_code("ClientMetadataDocumentTooLarge", 185)
error_code("ClientMetadataCannotBeMutated", 186)


# Non-sequential error codes (for compatibility only) # Non-sequential error codes (for compatibility only)
error_code("SocketException", 9001) error_code("SocketException", 9001)
Expand Down
2 changes: 1 addition & 1 deletion src/mongo/client/connection_pool.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ ConnectionPool::ConnectionList::iterator ConnectionPool::acquireConnection(
// the number of seconds with a fractional part. // the number of seconds with a fractional part.
conn->setSoTimeout(durationCount<Milliseconds>(timeout) / 1000.0); conn->setSoTimeout(durationCount<Milliseconds>(timeout) / 1000.0);


uassertStatusOK(conn->connect(target)); uassertStatusOK(conn->connect(target, StringData()));
conn->port().setTag(conn->port().getTag() | _messagingPortTags); conn->port().setTag(conn->port().getTag() | _messagingPortTags);


if (isInternalAuthSet()) { if (isInternalAuthSet()) {
Expand Down
4 changes: 3 additions & 1 deletion src/mongo/client/connection_string.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ class ConnectionString {
bool operator==(const ConnectionString& other) const; bool operator==(const ConnectionString& other) const;
bool operator!=(const ConnectionString& other) const; bool operator!=(const ConnectionString& other) const;


DBClientBase* connect(std::string& errmsg, double socketTimeout = 0) const; DBClientBase* connect(StringData applicationName,
std::string& errmsg,
double socketTimeout = 0) const;


static StatusWith<ConnectionString> parse(const std::string& url); static StatusWith<ConnectionString> parse(const std::string& url);


Expand Down
9 changes: 6 additions & 3 deletions src/mongo/client/connection_string_connect.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -45,21 +45,24 @@ namespace mongo {
stdx::mutex ConnectionString::_connectHookMutex; stdx::mutex ConnectionString::_connectHookMutex;
ConnectionString::ConnectionHook* ConnectionString::_connectHook = NULL; ConnectionString::ConnectionHook* ConnectionString::_connectHook = NULL;


DBClientBase* ConnectionString::connect(std::string& errmsg, double socketTimeout) const { DBClientBase* ConnectionString::connect(StringData applicationName,
std::string& errmsg,
double socketTimeout) const {
switch (_type) { switch (_type) {
case MASTER: { case MASTER: {
auto c = stdx::make_unique<DBClientConnection>(true); auto c = stdx::make_unique<DBClientConnection>(true);
c->setSoTimeout(socketTimeout); c->setSoTimeout(socketTimeout);
LOG(1) << "creating new connection to:" << _servers[0]; LOG(1) << "creating new connection to:" << _servers[0];
if (!c->connect(_servers[0], errmsg)) { if (!c->connect(_servers[0], applicationName, errmsg)) {
return 0; return 0;
} }
LOG(1) << "connected connection!"; LOG(1) << "connected connection!";
return c.release(); return c.release();
} }


case SET: { case SET: {
auto set = stdx::make_unique<DBClientReplicaSet>(_setName, _servers, socketTimeout); auto set = stdx::make_unique<DBClientReplicaSet>(
_setName, _servers, applicationName, socketTimeout);
if (!set->connect()) { if (!set->connect()) {
errmsg = "connect failed to replica set "; errmsg = "connect failed to replica set ";
errmsg += toString(); errmsg += toString();
Expand Down
4 changes: 2 additions & 2 deletions src/mongo/client/connpool.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ DBClientBase* DBConnectionPool::get(const ConnectionString& url, double socketTi
} }


string errmsg; string errmsg;
c = url.connect(errmsg, socketTimeout); c = url.connect(StringData(), errmsg, socketTimeout);
uassert(13328, _name + ": connect failed " + url.toString() + " : " + errmsg, c); uassert(13328, _name + ": connect failed " + url.toString() + " : " + errmsg, c);


return _finishCreate(url.toString(), socketTimeout, c); return _finishCreate(url.toString(), socketTimeout, c);
Expand All @@ -256,7 +256,7 @@ DBClientBase* DBConnectionPool::get(const string& host, double socketTimeout) {
const ConnectionString cs(uassertStatusOK(ConnectionString::parse(host))); const ConnectionString cs(uassertStatusOK(ConnectionString::parse(host)));


string errmsg; string errmsg;
c = cs.connect(errmsg, socketTimeout); c = cs.connect(StringData(), errmsg, socketTimeout);
if (!c) if (!c)
throw SocketException(SocketException::CONNECT_ERROR, throw SocketException(SocketException::CONNECT_ERROR,
host, host,
Expand Down
24 changes: 18 additions & 6 deletions src/mongo/client/dbclient.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "mongo/rpc/factory.h" #include "mongo/rpc/factory.h"
#include "mongo/rpc/get_status_from_command_result.h" #include "mongo/rpc/get_status_from_command_result.h"
#include "mongo/rpc/metadata.h" #include "mongo/rpc/metadata.h"
#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/rpc/reply_interface.h" #include "mongo/rpc/reply_interface.h"
#include "mongo/rpc/request_builder_interface.h" #include "mongo/rpc/request_builder_interface.h"
#include "mongo/s/stale_exception.h" // for RecvStaleConfigException #include "mongo/s/stale_exception.h" // for RecvStaleConfigException
Expand All @@ -74,6 +75,7 @@
#include "mongo/util/password_digest.h" #include "mongo/util/password_digest.h"
#include "mongo/util/represent_as.h" #include "mongo/util/represent_as.h"
#include "mongo/util/time_support.h" #include "mongo/util/time_support.h"
#include "mongo/util/version.h"


namespace mongo { namespace mongo {


Expand Down Expand Up @@ -712,7 +714,8 @@ class ScopedForceOpQuery {
/** /**
* Initializes the wire version of conn, and returns the isMaster reply. * Initializes the wire version of conn, and returns the isMaster reply.
*/ */
executor::RemoteCommandResponse initWireVersion(DBClientConnection* conn) { executor::RemoteCommandResponse initWireVersion(DBClientConnection* conn,
StringData applicationName) {
try { try {
// We need to force the usage of OP_QUERY on this command, even if we have previously // We need to force the usage of OP_QUERY on this command, even if we have previously
// detected support for OP_COMMAND on a connection. This is necessary to handle the case // detected support for OP_COMMAND on a connection. This is necessary to handle the case
Expand All @@ -731,6 +734,12 @@ executor::RemoteCommandResponse initWireVersion(DBClientConnection* conn) {
bob.append("hostInfo", sb.str()); bob.append("hostInfo", sb.str());
} }


Status serializeStatus = ClientMetadata::serialize(
"MongoDB Internal Client", mongo::versionString, applicationName, &bob);
if (!serializeStatus.isOK()) {
return serializeStatus;
}

Date_t start{Date_t::now()}; Date_t start{Date_t::now()};
auto result = auto result =
conn->runCommandWithMetadata("admin", "isMaster", rpc::makeEmptyMetadata(), bob.done()); conn->runCommandWithMetadata("admin", "isMaster", rpc::makeEmptyMetadata(), bob.done());
Expand All @@ -754,22 +763,25 @@ executor::RemoteCommandResponse initWireVersion(DBClientConnection* conn) {


} // namespace } // namespace


bool DBClientConnection::connect(const HostAndPort& server, std::string& errmsg) { bool DBClientConnection::connect(const HostAndPort& server,
auto connectStatus = connect(server); StringData applicationName,
std::string& errmsg) {
auto connectStatus = connect(server, applicationName);
if (!connectStatus.isOK()) { if (!connectStatus.isOK()) {
errmsg = connectStatus.reason(); errmsg = connectStatus.reason();
return false; return false;
} }
return true; return true;
} }


Status DBClientConnection::connect(const HostAndPort& serverAddress) { Status DBClientConnection::connect(const HostAndPort& serverAddress, StringData applicationName) {
auto connectStatus = connectSocketOnly(serverAddress); auto connectStatus = connectSocketOnly(serverAddress);
if (!connectStatus.isOK()) { if (!connectStatus.isOK()) {
return connectStatus; return connectStatus;
} }


auto swIsMasterReply = initWireVersion(this); _applicationName = applicationName.toString();
auto swIsMasterReply = initWireVersion(this, applicationName);
if (!swIsMasterReply.isOK()) { if (!swIsMasterReply.isOK()) {
_failed = true; _failed = true;
return swIsMasterReply.status; return swIsMasterReply.status;
Expand Down Expand Up @@ -903,7 +915,7 @@ void DBClientConnection::_checkConnection() {
LOG(_logLevel) << "trying reconnect to " << toString() << endl; LOG(_logLevel) << "trying reconnect to " << toString() << endl;
string errmsg; string errmsg;
_failed = false; _failed = false;
auto connectStatus = connect(_serverAddress); auto connectStatus = connect(_serverAddress, _applicationName);
if (!connectStatus.isOK()) { if (!connectStatus.isOK()) {
_failed = true; _failed = true;
LOG(_logLevel) << "reconnect " << toString() << " failed " << errmsg << endl; LOG(_logLevel) << "reconnect " << toString() << " failed " << errmsg << endl;
Expand Down
6 changes: 4 additions & 2 deletions src/mongo/client/dbclient_rs.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -135,8 +135,9 @@ bool DBClientReplicaSet::_authPooledSecondaryConn = true;


DBClientReplicaSet::DBClientReplicaSet(const string& name, DBClientReplicaSet::DBClientReplicaSet(const string& name,
const vector<HostAndPort>& servers, const vector<HostAndPort>& servers,
StringData applicationName,
double so_timeout) double so_timeout)
: _setName(name), _so_timeout(so_timeout) { : _setName(name), _applicationName(applicationName.toString()), _so_timeout(so_timeout) {
_rsm = _rsm =
ReplicaSetMonitor::createIfNeeded(name, set<HostAndPort>(servers.begin(), servers.end())); ReplicaSetMonitor::createIfNeeded(name, set<HostAndPort>(servers.begin(), servers.end()));
} }
Expand Down Expand Up @@ -303,7 +304,8 @@ DBClientConnection* DBClientReplicaSet::checkMaster() {
// Needs to perform a dynamic_cast because we need to set the replSet // Needs to perform a dynamic_cast because we need to set the replSet
// callback. We should eventually not need this after we remove the // callback. We should eventually not need this after we remove the
// callback. // callback.
newConn = dynamic_cast<DBClientConnection*>(connStr.connect(errmsg, _so_timeout)); newConn = dynamic_cast<DBClientConnection*>(
connStr.connect(_applicationName, errmsg, _so_timeout));
} catch (const AssertionException& ex) { } catch (const AssertionException& ex) {
errmsg = ex.toString(); errmsg = ex.toString();
} }
Expand Down
2 changes: 2 additions & 0 deletions src/mongo/client/dbclient_rs.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class DBClientReplicaSet : public DBClientBase {
* connections. */ * connections. */
DBClientReplicaSet(const std::string& name, DBClientReplicaSet(const std::string& name,
const std::vector<HostAndPort>& servers, const std::vector<HostAndPort>& servers,
StringData applicationName,
double so_timeout = 0); double so_timeout = 0);
virtual ~DBClientReplicaSet(); virtual ~DBClientReplicaSet();


Expand Down Expand Up @@ -295,6 +296,7 @@ class DBClientReplicaSet : public DBClientBase {
ReplicaSetMonitorPtr _getMonitor(); ReplicaSetMonitorPtr _getMonitor();


std::string _setName; std::string _setName;
std::string _applicationName;
std::shared_ptr<ReplicaSetMonitor> _rsm; std::shared_ptr<ReplicaSetMonitor> _rsm;


HostAndPort _masterHost; HostAndPort _masterHost;
Expand Down
Loading

0 comments on commit ad27c92

Please sign in to comment.