/* @file db/client.h
"Client" represents a connection to the database (the server-side) and corresponds
to an open socket (or logical connection if pooling on sockets) from a client.
todo: switch to asio...this will fit nicely with that.
*/
/**
* Copyright (C) 2008 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include
#include
#include
#include "mongo/db/catalog/database.h"
#include "mongo/db/client_basic.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/lasterror.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
#include "mongo/platform/unordered_set.h"
#include "mongo/stdx/functional.h"
#include "mongo/util/concurrency/spin_lock.h"
#include "mongo/util/concurrency/threadlocal.h"
#include "mongo/util/paths.h"
namespace mongo {
class AuthenticationInfo;
class Database;
class CurOp;
class Client;
class Collection;
class AbstractMessagingPort;
class Locker;
TSP_DECLARE(Client, currentClient)
typedef long long ConnectionId;
/**
* RAII-style class, which acquires a lock on the specified database in the requested mode and
* obtains a reference to the database. Used as a shortcut for calls to dbHolder().get().
*
* It is guaranteed that locks will be released when this object goes out of scope, therefore
* the database reference returned by this class should not be retained.
*
* TODO: This should be moved outside of client.h (maybe dbhelpers.h)
*/
class AutoGetDb {
MONGO_DISALLOW_COPYING(AutoGetDb);
public:
AutoGetDb(OperationContext* txn, const StringData& ns, LockMode mode);
Database* getDb() const {
return _db;
}
private:
const Lock::DBLock _dbLock;
Database* const _db;
};
/**
* RAII-style class, which acquires a lock on the specified database in the requested mode and
* obtains a reference to the database, creating it was non-existing. Used as a shortcut for
* calls to dbHolder().openDb(), taking care of locking details. The requested mode must be
* MODE_IX or MODE_X. If the database needs to be created, the lock will automatically be
* reacquired as MODE_X.
*
* It is guaranteed that locks will be released when this object goes out of scope, therefore
* the database reference returned by this class should not be retained.
*
* TODO: This should be moved outside of client.h (maybe dbhelpers.h)
*/
class AutoGetOrCreateDb {
MONGO_DISALLOW_COPYING(AutoGetOrCreateDb);
public:
AutoGetOrCreateDb(OperationContext* txn, const StringData& ns, LockMode mode);
Database* getDb() {
return _db;
}
bool justCreated() {
return _justCreated;
}
Lock::DBLock& lock() { return _dbLock; }
private:
ScopedTransaction _transaction;
Lock::DBLock _dbLock; // not const, as we may need to relock for implicit create
Database* _db;
bool _justCreated;
};
/**
* RAII-style class, which would acquire the appropritate hierarchy of locks for obtaining
* a particular collection and would retrieve a reference to the collection.
*
* It is guaranteed that locks will be released when this object goes out of scope, therefore
* database and collection references returned by this class should not be retained.
*
* TODO: This should be moved outside of client.h (maybe dbhelpers.h)
*/
class AutoGetCollectionForRead {
MONGO_DISALLOW_COPYING(AutoGetCollectionForRead);
public:
AutoGetCollectionForRead(OperationContext* txn, const std::string& ns);
AutoGetCollectionForRead(OperationContext* txn, const NamespaceString& nss);
~AutoGetCollectionForRead();
Database* getDb() const {
return _db.getDb();
}
Collection* getCollection() const {
return _coll;
}
private:
void _init(const std::string& ns,
const StringData& coll);
const Timer _timer;
OperationContext* const _txn;
const ScopedTransaction _transaction;
const AutoGetDb _db;
const Lock::CollectionLock _collLock;
Collection* _coll;
};
typedef unordered_set ClientSet;
/** the database's concept of an outside "client" */
class Client : public ClientBasic {
public:
// A set of currently active clients along with a mutex to protect the list
static boost::mutex clientsMutex;
static ClientSet clients;
~Client();
/** each thread which does db operations has a Client object in TLS.
* call this when your thread starts.
*/
static void initThread(const char *desc, AbstractMessagingPort *mp = 0);
/**
* Inits a thread if that thread has not already been init'd, setting the thread name to
* "desc".
*/
static void initThreadIfNotAlready(const char *desc) {
if (currentClient.get())
return;
initThread(desc);
}
/**
* Inits a thread if that thread has not already been init'd, using the existing thread name
*/
static void initThreadIfNotAlready() {
if (currentClient.get())
return;
initThread(getThreadName().c_str());
}
/** this has to be called as the client goes away, but before thread termination
* @return true if anything was done
*/
bool shutdown();
std::string clientAddress(bool includePort=false) const;
CurOp* curop() const { return _curOp; }
const StringData desc() const { return _desc; }
void setLastOp( OpTime op ) { _lastOp = op; }
OpTime getLastOp() const { return _lastOp; }
// Return a reference to the Locker for this client. Client retains ownership.
Locker* getLocker() const { return _locker.get(); }
/* report what the last operation was. used by getlasterror */
void appendLastOp( BSONObjBuilder& b ) const;
void reportState(BSONObjBuilder& builder);
// Ensures stability of the client's OperationContext. When the client is locked,
// the OperationContext will not disappear.
void lock() { _lock.lock(); }
void unlock() { _lock.unlock(); }
// Changes the currently active operation context on this client. There can only be one
// active OperationContext at a time.
void setOperationContext(OperationContext* txn);
void resetOperationContext();
const OperationContext* getOperationContext() const { return _txn; }
// TODO(spencer): SERVER-10228 SERVER-14779 Remove this/move it fully into OperationContext.
bool isGod() const { return _god; } /* this is for map/reduce writes */
bool setGod(bool newVal) { const bool prev = _god; _god = newVal; return prev; }
void setRemoteID(const OID& rid) { _remoteId = rid; } // Only used for master/slave
OID getRemoteID() const { return _remoteId; } // Only used for master/slave
ConnectionId getConnectionId() const { return _connectionId; }
bool isFromUserConnection() const { return _connectionId > 0; }
private:
Client(const std::string& desc, AbstractMessagingPort *p = 0);
friend class CurOp;
// Description for the client (e.g. conn8)
const std::string _desc;
// OS id of the thread, which owns this client
const boost::thread::id _threadId;
// > 0 for things "conn", 0 otherwise
const ConnectionId _connectionId;
// Protects the contents of the Client (such as changing the OperationContext, etc)
mutable SpinLock _lock;
// Whether this client is running as DBDirectClient
bool _god;
// If != NULL, then contains the currently active OperationContext
OperationContext* _txn;
// Changes, based on what operation is running. Some of this should be in OperationContext.
CurOp* _curOp;
// By having Client, rather than the OperationContext, own the Locker, setup cost such as
// allocating OS resources can be amortized over multiple operations.
boost::scoped_ptr const _locker;
// Used by replication
OpTime _lastOp;
OID _remoteId; // Only used by master-slave
// Tracks if Client::shutdown() gets called (TODO: Is this necessary?)
bool _shutdown;
public:
/* Set database we want to use, then, restores when we finish (are out of scope)
Note this is also helpful if an exception happens as the state if fixed up.
*/
class Context {
MONGO_DISALLOW_COPYING(Context);
public:
/** this is probably what you want */
Context(OperationContext* txn, const std::string& ns, bool doVersion = true);
/**
* Below still calls _finishInit, but assumes database has already been acquired
* or just created.
*/
Context(OperationContext* txn,
const std::string& ns,
Database* db,
bool justCreated);
/**
* note: this does not call _finishInit -- i.e., does not call
* ensureShardVersionOKOrThrow for example.
* see also: reset().
*/
Context(OperationContext* txn, const std::string& ns, Database * db);
~Context();
Client* getClient() const { return _client; }
Database* db() const { return _db; }
const char * ns() const { return _ns.c_str(); }
/** @return if the db was created by this Context */
bool justCreated() const { return _justCreated; }
/** call before unlocking, so clear any non-thread safe state
* _db gets restored on the relock
*/
void unlocked() { _db = 0; }
/** call after going back into the lock, will re-establish non-thread safe stuff */
void relocked() { _finishInit(); }
private:
friend class CurOp;
void _finishInit();
void checkNotStale() const;
Client * const _client;
bool _justCreated;
bool _doVersion;
const std::string _ns;
Database * _db;
OperationContext* _txn;
Timer _timer;
}; // class Client::Context
class WriteContext : boost::noncopyable {
public:
WriteContext(OperationContext* opCtx, const std::string& ns);
Database* db() const { return _c.db(); }
Collection* getCollection() const {
return _c.db()->getCollection(_nss.ns());
}
Context& ctx() { return _c; }
private:
OperationContext* _txn;
NamespaceString _nss;
AutoGetOrCreateDb _autodb;
Lock::CollectionLock _collk;
Context _c;
Collection* _collection;
};
}; // class Client
/** get the Client object for this thread. */
inline Client& cc() {
Client * c = currentClient.get();
verify( c );
return *c;
}
inline bool haveClient() { return currentClient.get() != NULL; }
};