summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/clientOnly.cpp79
-rw-r--r--client/connpool.cpp459
-rw-r--r--client/connpool.h291
-rw-r--r--client/constants.h26
-rw-r--r--client/dbclient.cpp1053
-rw-r--r--client/dbclient.h983
-rw-r--r--client/dbclient_rs.cpp1097
-rw-r--r--client/dbclient_rs.h384
-rw-r--r--client/dbclientcursor.cpp319
-rw-r--r--client/dbclientcursor.h239
-rw-r--r--client/dbclientmockcursor.h40
-rw-r--r--client/distlock.cpp967
-rw-r--r--client/distlock.h244
-rw-r--r--client/distlock_test.cpp445
-rw-r--r--client/examples/authTest.cpp54
-rw-r--r--client/examples/clientTest.cpp279
-rw-r--r--client/examples/first.cpp86
-rw-r--r--client/examples/httpClientTest.cpp58
-rw-r--r--client/examples/insert_demo.cpp47
-rw-r--r--client/examples/rs.cpp118
-rw-r--r--client/examples/second.cpp56
-rwxr-xr-xclient/examples/simple_client_demo.vcxproj92
-rwxr-xr-xclient/examples/simple_client_demo.vcxproj.filters21
-rw-r--r--client/examples/tail.cpp46
-rw-r--r--client/examples/tutorial.cpp67
-rw-r--r--client/examples/whereExample.cpp69
-rw-r--r--client/gridfs.cpp245
-rw-r--r--client/gridfs.h205
-rw-r--r--client/model.cpp138
-rw-r--r--client/model.h62
-rw-r--r--client/mongo_client_lib.cpp89
-rw-r--r--client/parallel.cpp804
-rw-r--r--client/parallel.h315
-rw-r--r--client/redef_macros.h102
-rw-r--r--client/simple_client_demo.cpp54
-rw-r--r--client/syncclusterconnection.cpp407
-rw-r--r--client/syncclusterconnection.h147
-rw-r--r--client/undef_macros.h83
38 files changed, 0 insertions, 10270 deletions
diff --git a/client/clientOnly.cpp b/client/clientOnly.cpp
deleted file mode 100644
index 11890c84082..00000000000
--- a/client/clientOnly.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-// clientOnly.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "pch.h"
-#include "../client/dbclient.h"
-#include "../db/cmdline.h"
-#include "../s/shard.h"
-
-namespace mongo {
-
- CmdLine cmdLine;
-
- const char * curNs = "in client mode";
-
- bool dbexitCalled = false;
-
- void exitCleanly( ExitCode code ) {
- dbexit( code );
- }
-
- void dbexit( ExitCode returnCode, const char *whyMsg , bool tryToGetLock ) {
- dbexitCalled = true;
- out() << "dbexit called" << endl;
- if ( whyMsg )
- out() << " b/c " << whyMsg << endl;
- out() << "exiting" << endl;
- ::exit( returnCode );
- }
-
- bool inShutdown() {
- return dbexitCalled;
- }
-
- void setupSignals() {
- // maybe should do SIGPIPE here, not sure
- }
-
- string getDbContext() {
- return "in client only mode";
- }
-
- bool haveLocalShardingInfo( const string& ns ) {
- return false;
- }
-
- DBClientBase * createDirectClient() {
- uassert( 10256 , "no createDirectClient in clientOnly" , 0 );
- return 0;
- }
-
- void Shard::getAllShards( vector<Shard>& all ) {
- assert(0);
- }
-
- bool Shard::isAShardNode( const string& ident ) {
- assert(0);
- return false;
- }
-
- string prettyHostName() {
- assert(0);
- return "";
- }
-
-}
diff --git a/client/connpool.cpp b/client/connpool.cpp
deleted file mode 100644
index ca3713dbe82..00000000000
--- a/client/connpool.cpp
+++ /dev/null
@@ -1,459 +0,0 @@
-/* connpool.cpp
-*/
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// _ todo: reconnect?
-
-#include "pch.h"
-#include "connpool.h"
-#include "../db/commands.h"
-#include "syncclusterconnection.h"
-#include "../s/shard.h"
-
-namespace mongo {
-
- // ------ PoolForHost ------
-
- PoolForHost::~PoolForHost() {
- while ( ! _pool.empty() ) {
- StoredConnection sc = _pool.top();
- delete sc.conn;
- _pool.pop();
- }
- }
-
- void PoolForHost::done( DBConnectionPool * pool, DBClientBase * c ) {
- if ( _pool.size() >= _maxPerHost ) {
- pool->onDestroy( c );
- delete c;
- }
- else {
- _pool.push(c);
- }
- }
-
- DBClientBase * PoolForHost::get( DBConnectionPool * pool , double socketTimeout ) {
-
- time_t now = time(0);
-
- while ( ! _pool.empty() ) {
- StoredConnection sc = _pool.top();
- _pool.pop();
-
- if ( ! sc.ok( now ) ) {
- pool->onDestroy( sc.conn );
- delete sc.conn;
- continue;
- }
-
- assert( sc.conn->getSoTimeout() == socketTimeout );
-
- return sc.conn;
-
- }
-
- return NULL;
- }
-
- void PoolForHost::flush() {
- vector<StoredConnection> all;
- while ( ! _pool.empty() ) {
- StoredConnection c = _pool.top();
- _pool.pop();
- all.push_back( c );
- bool res;
- c.conn->isMaster( res );
- }
-
- for ( vector<StoredConnection>::iterator i=all.begin(); i != all.end(); ++i ) {
- _pool.push( *i );
- }
- }
-
- void PoolForHost::getStaleConnections( vector<DBClientBase*>& stale ) {
- time_t now = time(0);
-
- vector<StoredConnection> all;
- while ( ! _pool.empty() ) {
- StoredConnection c = _pool.top();
- _pool.pop();
-
- if ( c.ok( now ) )
- all.push_back( c );
- else
- stale.push_back( c.conn );
- }
-
- for ( size_t i=0; i<all.size(); i++ ) {
- _pool.push( all[i] );
- }
- }
-
-
- PoolForHost::StoredConnection::StoredConnection( DBClientBase * c ) {
- conn = c;
- when = time(0);
- }
-
- bool PoolForHost::StoredConnection::ok( time_t now ) {
- // if connection has been idle for 30 minutes, kill it
- return ( now - when ) < 1800;
- }
-
- void PoolForHost::createdOne( DBClientBase * base) {
- if ( _created == 0 )
- _type = base->type();
- _created++;
- }
-
- unsigned PoolForHost::_maxPerHost = 50;
-
- // ------ DBConnectionPool ------
-
- DBConnectionPool pool;
-
- DBConnectionPool::DBConnectionPool()
- : _mutex("DBConnectionPool") ,
- _name( "dbconnectionpool" ) ,
- _hooks( new list<DBConnectionHook*>() ) {
- }
-
- DBClientBase* DBConnectionPool::_get(const string& ident , double socketTimeout ) {
- assert( ! inShutdown() );
- scoped_lock L(_mutex);
- PoolForHost& p = _pools[PoolKey(ident,socketTimeout)];
- return p.get( this , socketTimeout );
- }
-
- DBClientBase* DBConnectionPool::_finishCreate( const string& host , double socketTimeout , DBClientBase* conn ) {
- {
- scoped_lock L(_mutex);
- PoolForHost& p = _pools[PoolKey(host,socketTimeout)];
- p.createdOne( conn );
- }
-
- try {
- onCreate( conn );
- onHandedOut( conn );
- }
- catch ( std::exception& e ) {
- delete conn;
- throw;
- }
-
- return conn;
- }
-
- DBClientBase* DBConnectionPool::get(const ConnectionString& url, double socketTimeout) {
- DBClientBase * c = _get( url.toString() , socketTimeout );
- if ( c ) {
- try {
- onHandedOut( c );
- }
- catch ( std::exception& e ) {
- delete c;
- throw;
- }
- return c;
- }
-
- string errmsg;
- c = url.connect( errmsg, socketTimeout );
- uassert( 13328 , _name + ": connect failed " + url.toString() + " : " + errmsg , c );
-
- return _finishCreate( url.toString() , socketTimeout , c );
- }
-
- DBClientBase* DBConnectionPool::get(const string& host, double socketTimeout) {
- DBClientBase * c = _get( host , socketTimeout );
- if ( c ) {
- try {
- onHandedOut( c );
- }
- catch ( std::exception& e ) {
- delete c;
- throw;
- }
- return c;
- }
-
- string errmsg;
- ConnectionString cs = ConnectionString::parse( host , errmsg );
- uassert( 13071 , (string)"invalid hostname [" + host + "]" + errmsg , cs.isValid() );
-
- c = cs.connect( errmsg, socketTimeout );
- if ( ! c )
- throw SocketException( SocketException::CONNECT_ERROR , host , 11002 , str::stream() << _name << " error: " << errmsg );
- return _finishCreate( host , socketTimeout , c );
- }
-
- void DBConnectionPool::release(const string& host, DBClientBase *c) {
- if ( c->isFailed() ) {
- onDestroy( c );
- delete c;
- return;
- }
- scoped_lock L(_mutex);
- _pools[PoolKey(host,c->getSoTimeout())].done(this,c);
- }
-
-
- DBConnectionPool::~DBConnectionPool() {
- // connection closing is handled by ~PoolForHost
- }
-
- void DBConnectionPool::flush() {
- scoped_lock L(_mutex);
- for ( PoolMap::iterator i = _pools.begin(); i != _pools.end(); i++ ) {
- PoolForHost& p = i->second;
- p.flush();
- }
- }
-
- void DBConnectionPool::addHook( DBConnectionHook * hook ) {
- _hooks->push_back( hook );
- }
-
- void DBConnectionPool::onCreate( DBClientBase * conn ) {
- if ( _hooks->size() == 0 )
- return;
-
- for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
- (*i)->onCreate( conn );
- }
- }
-
- void DBConnectionPool::onHandedOut( DBClientBase * conn ) {
- if ( _hooks->size() == 0 )
- return;
-
- for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
- (*i)->onHandedOut( conn );
- }
- }
-
- void DBConnectionPool::onDestroy( DBClientBase * conn ) {
- if ( _hooks->size() == 0 )
- return;
-
- for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
- (*i)->onDestroy( conn );
- }
- }
-
- void DBConnectionPool::appendInfo( BSONObjBuilder& b ) {
-
- int avail = 0;
- long long created = 0;
-
-
- map<ConnectionString::ConnectionType,long long> createdByType;
-
- set<string> replicaSets;
-
- BSONObjBuilder bb( b.subobjStart( "hosts" ) );
- {
- scoped_lock lk( _mutex );
- for ( PoolMap::iterator i=_pools.begin(); i!=_pools.end(); ++i ) {
- if ( i->second.numCreated() == 0 )
- continue;
-
- string s = str::stream() << i->first.ident << "::" << i->first.timeout;
-
- BSONObjBuilder temp( bb.subobjStart( s ) );
- temp.append( "available" , i->second.numAvailable() );
- temp.appendNumber( "created" , i->second.numCreated() );
- temp.done();
-
- avail += i->second.numAvailable();
- created += i->second.numCreated();
-
- long long& x = createdByType[i->second.type()];
- x += i->second.numCreated();
-
- {
- string setName = i->first.ident;
- if ( setName.find( "/" ) != string::npos ) {
- setName = setName.substr( 0 , setName.find( "/" ) );
- replicaSets.insert( setName );
- }
- }
- }
- }
- bb.done();
-
-
- BSONObjBuilder setBuilder( b.subobjStart( "replicaSets" ) );
- for ( set<string>::iterator i=replicaSets.begin(); i!=replicaSets.end(); ++i ) {
- string rs = *i;
- ReplicaSetMonitorPtr m = ReplicaSetMonitor::get( rs );
- if ( ! m ) {
- warning() << "no monitor for set: " << rs << endl;
- continue;
- }
-
- BSONObjBuilder temp( setBuilder.subobjStart( rs ) );
- m->appendInfo( temp );
- temp.done();
- }
- setBuilder.done();
-
- {
- BSONObjBuilder temp( bb.subobjStart( "createdByType" ) );
- for ( map<ConnectionString::ConnectionType,long long>::iterator i=createdByType.begin(); i!=createdByType.end(); ++i ) {
- temp.appendNumber( ConnectionString::typeToString( i->first ) , i->second );
- }
- temp.done();
- }
-
- b.append( "totalAvailable" , avail );
- b.appendNumber( "totalCreated" , created );
- }
-
- bool DBConnectionPool::serverNameCompare::operator()( const string& a , const string& b ) const{
- const char* ap = a.c_str();
- const char* bp = b.c_str();
-
- while (true){
- if (*ap == '\0' || *ap == '/'){
- if (*bp == '\0' || *bp == '/')
- return false; // equal strings
- else
- return true; // a is shorter
- }
-
- if (*bp == '\0' || *bp == '/')
- return false; // b is shorter
-
- if ( *ap < *bp)
- return true;
- else if (*ap > *bp)
- return false;
-
- ++ap;
- ++bp;
- }
- assert(false);
- }
-
- bool DBConnectionPool::poolKeyCompare::operator()( const PoolKey& a , const PoolKey& b ) const {
- if (DBConnectionPool::serverNameCompare()( a.ident , b.ident ))
- return true;
-
- if (DBConnectionPool::serverNameCompare()( b.ident , a.ident ))
- return false;
-
- return a.timeout < b.timeout;
- }
-
-
- void DBConnectionPool::taskDoWork() {
- vector<DBClientBase*> toDelete;
-
- {
- // we need to get the connections inside the lock
- // but we can actually delete them outside
- scoped_lock lk( _mutex );
- for ( PoolMap::iterator i=_pools.begin(); i!=_pools.end(); ++i ) {
- i->second.getStaleConnections( toDelete );
- }
- }
-
- for ( size_t i=0; i<toDelete.size(); i++ ) {
- try {
- onDestroy( toDelete[i] );
- delete toDelete[i];
- }
- catch ( ... ) {
- // we don't care if there was a socket error
- }
- }
- }
-
- // ------ ScopedDbConnection ------
-
- ScopedDbConnection * ScopedDbConnection::steal() {
- assert( _conn );
- ScopedDbConnection * n = new ScopedDbConnection( _host , _conn, _socketTimeout );
- _conn = 0;
- return n;
- }
-
- void ScopedDbConnection::_setSocketTimeout(){
- if( ! _conn ) return;
- if( _conn->type() == ConnectionString::MASTER )
- (( DBClientConnection* ) _conn)->setSoTimeout( _socketTimeout );
- else if( _conn->type() == ConnectionString::SYNC )
- (( SyncClusterConnection* ) _conn)->setAllSoTimeouts( _socketTimeout );
- }
-
- ScopedDbConnection::~ScopedDbConnection() {
- if ( _conn ) {
- if ( ! _conn->isFailed() ) {
- /* see done() comments above for why we log this line */
- log() << "scoped connection to " << _conn->getServerAddress() << " not being returned to the pool" << endl;
- }
- kill();
- }
- }
-
- ScopedDbConnection::ScopedDbConnection(const Shard& shard, double socketTimeout )
- : _host( shard.getConnString() ) , _conn( pool.get(_host, socketTimeout) ), _socketTimeout( socketTimeout ) {
- _setSocketTimeout();
- }
-
- ScopedDbConnection::ScopedDbConnection(const Shard* shard, double socketTimeout )
- : _host( shard->getConnString() ) , _conn( pool.get(_host, socketTimeout) ), _socketTimeout( socketTimeout ) {
- _setSocketTimeout();
- }
-
-
- class PoolFlushCmd : public Command {
- public:
- PoolFlushCmd() : Command( "connPoolSync" , false , "connpoolsync" ) {}
- virtual void help( stringstream &help ) const { help<<"internal"; }
- virtual LockType locktype() const { return NONE; }
- virtual bool run(const string&, mongo::BSONObj&, int, std::string&, mongo::BSONObjBuilder& result, bool) {
- pool.flush();
- return true;
- }
- virtual bool slaveOk() const {
- return true;
- }
-
- } poolFlushCmd;
-
- class PoolStats : public Command {
- public:
- PoolStats() : Command( "connPoolStats" ) {}
- virtual void help( stringstream &help ) const { help<<"stats about connection pool"; }
- virtual LockType locktype() const { return NONE; }
- virtual bool run(const string&, mongo::BSONObj&, int, std::string&, mongo::BSONObjBuilder& result, bool) {
- pool.appendInfo( result );
- result.append( "numDBClientConnection" , DBClientConnection::getNumConnections() );
- result.append( "numAScopedConnection" , AScopedConnection::getNumConnections() );
- return true;
- }
- virtual bool slaveOk() const {
- return true;
- }
-
- } poolStatsCmd;
-
- AtomicUInt AScopedConnection::_numConnections;
-
-} // namespace mongo
diff --git a/client/connpool.h b/client/connpool.h
deleted file mode 100644
index 8733abb1f90..00000000000
--- a/client/connpool.h
+++ /dev/null
@@ -1,291 +0,0 @@
-/** @file connpool.h */
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stack>
-#include "dbclient.h"
-#include "redef_macros.h"
-
-#include "../util/background.h"
-
-namespace mongo {
-
- class Shard;
- class DBConnectionPool;
-
- /**
- * not thread safe
- * thread safety is handled by DBConnectionPool
- */
- class PoolForHost {
- public:
- PoolForHost()
- : _created(0) {}
-
- PoolForHost( const PoolForHost& other ) {
- assert(other._pool.size() == 0);
- _created = other._created;
- assert( _created == 0 );
- }
-
- ~PoolForHost();
-
- int numAvailable() const { return (int)_pool.size(); }
-
- void createdOne( DBClientBase * base );
- long long numCreated() const { return _created; }
-
- ConnectionString::ConnectionType type() const { assert(_created); return _type; }
-
- /**
- * gets a connection or return NULL
- */
- DBClientBase * get( DBConnectionPool * pool , double socketTimeout );
-
- void done( DBConnectionPool * pool , DBClientBase * c );
-
- void flush();
-
- void getStaleConnections( vector<DBClientBase*>& stale );
-
- static void setMaxPerHost( unsigned max ) { _maxPerHost = max; }
- static unsigned getMaxPerHost() { return _maxPerHost; }
- private:
-
- struct StoredConnection {
- StoredConnection( DBClientBase * c );
-
- bool ok( time_t now );
-
- DBClientBase* conn;
- time_t when;
- };
-
- std::stack<StoredConnection> _pool;
-
- long long _created;
- ConnectionString::ConnectionType _type;
-
- static unsigned _maxPerHost;
- };
-
- class DBConnectionHook {
- public:
- virtual ~DBConnectionHook() {}
- virtual void onCreate( DBClientBase * conn ) {}
- virtual void onHandedOut( DBClientBase * conn ) {}
- virtual void onDestroy( DBClientBase * conn ) {}
- };
-
- /** Database connection pool.
-
- Generally, use ScopedDbConnection and do not call these directly.
-
- This class, so far, is suitable for use with unauthenticated connections.
- Support for authenticated connections requires some adjustements: please
- request...
-
- Usage:
-
- {
- ScopedDbConnection c("myserver");
- c.conn()...
- }
- */
- class DBConnectionPool : public PeriodicTask {
-
- public:
-
- DBConnectionPool();
- ~DBConnectionPool();
-
- /** right now just controls some asserts. defaults to "dbconnectionpool" */
- void setName( const string& name ) { _name = name; }
-
- void onCreate( DBClientBase * conn );
- void onHandedOut( DBClientBase * conn );
- void onDestroy( DBClientBase * conn );
-
- void flush();
-
- DBClientBase *get(const string& host, double socketTimeout = 0);
- DBClientBase *get(const ConnectionString& host, double socketTimeout = 0);
-
- void release(const string& host, DBClientBase *c);
-
- void addHook( DBConnectionHook * hook ); // we take ownership
- void appendInfo( BSONObjBuilder& b );
-
- /** compares server namees, but is smart about replica set names */
- struct serverNameCompare {
- bool operator()( const string& a , const string& b ) const;
- };
-
- virtual string taskName() const { return "DBConnectionPool-cleaner"; }
- virtual void taskDoWork();
-
- private:
- DBConnectionPool( DBConnectionPool& p );
-
- DBClientBase* _get( const string& ident , double socketTimeout );
-
- DBClientBase* _finishCreate( const string& ident , double socketTimeout, DBClientBase* conn );
-
- struct PoolKey {
- PoolKey( string i , double t ) : ident( i ) , timeout( t ) {}
- string ident;
- double timeout;
- };
-
- struct poolKeyCompare {
- bool operator()( const PoolKey& a , const PoolKey& b ) const;
- };
-
- typedef map<PoolKey,PoolForHost,poolKeyCompare> PoolMap; // servername -> pool
-
- mongo::mutex _mutex;
- string _name;
-
- PoolMap _pools;
-
- // pointers owned by me, right now they leak on shutdown
- // _hooks itself also leaks because it creates a shutdown race condition
- list<DBConnectionHook*> * _hooks;
-
- };
-
- extern DBConnectionPool pool;
-
- class AScopedConnection : boost::noncopyable {
- public:
- AScopedConnection() { _numConnections++; }
- virtual ~AScopedConnection() { _numConnections--; }
-
- virtual DBClientBase* get() = 0;
- virtual void done() = 0;
- virtual string getHost() const = 0;
-
- /**
- * @return true iff this has a connection to the db
- */
- virtual bool ok() const = 0;
-
- /**
- * @return total number of current instances of AScopedConnection
- */
- static int getNumConnections() { return _numConnections; }
-
- private:
- static AtomicUInt _numConnections;
- };
-
- /** Use to get a connection from the pool. On exceptions things
- clean up nicely (i.e. the socket gets closed automatically when the
- scopeddbconnection goes out of scope).
- */
- class ScopedDbConnection : public AScopedConnection {
- public:
- /** the main constructor you want to use
- throws UserException if can't connect
- */
- explicit ScopedDbConnection(const string& host, double socketTimeout = 0) : _host(host), _conn( pool.get(host, socketTimeout) ), _socketTimeout( socketTimeout ) {
- _setSocketTimeout();
- }
-
- ScopedDbConnection() : _host( "" ) , _conn(0), _socketTimeout( 0 ) {}
-
- /* @param conn - bind to an existing connection */
- ScopedDbConnection(const string& host, DBClientBase* conn, double socketTimeout = 0 ) : _host( host ) , _conn( conn ), _socketTimeout( socketTimeout ) {
- _setSocketTimeout();
- }
-
- /** throws UserException if can't connect */
- explicit ScopedDbConnection(const ConnectionString& url, double socketTimeout = 0 ) : _host(url.toString()), _conn( pool.get(url, socketTimeout) ), _socketTimeout( socketTimeout ) {
- _setSocketTimeout();
- }
-
- /** throws UserException if can't connect */
- explicit ScopedDbConnection(const Shard& shard, double socketTimeout = 0 );
- explicit ScopedDbConnection(const Shard* shard, double socketTimeout = 0 );
-
- ~ScopedDbConnection();
-
- /** get the associated connection object */
- DBClientBase* operator->() {
- uassert( 11004 , "connection was returned to the pool already" , _conn );
- return _conn;
- }
-
- /** get the associated connection object */
- DBClientBase& conn() {
- uassert( 11005 , "connection was returned to the pool already" , _conn );
- return *_conn;
- }
-
- /** get the associated connection object */
- DBClientBase* get() {
- uassert( 13102 , "connection was returned to the pool already" , _conn );
- return _conn;
- }
-
- bool ok() const { return _conn > 0; }
-
- string getHost() const { return _host; }
-
- /** Force closure of the connection. You should call this if you leave it in
- a bad state. Destructor will do this too, but it is verbose.
- */
- void kill() {
- delete _conn;
- _conn = 0;
- }
-
- /** Call this when you are done with the connection.
-
- If you do not call done() before this object goes out of scope,
- we can't be sure we fully read all expected data of a reply on the socket. so
- we don't try to reuse the connection in that situation.
- */
- void done() {
- if ( ! _conn )
- return;
-
- /* we could do this, but instead of assume one is using autoreconnect mode on the connection
- if ( _conn->isFailed() )
- kill();
- else
- */
- pool.release(_host, _conn);
- _conn = 0;
- }
-
- ScopedDbConnection * steal();
-
- private:
-
- void _setSocketTimeout();
-
- const string _host;
- DBClientBase *_conn;
- const double _socketTimeout;
-
- };
-
-} // namespace mongo
-
-#include "undef_macros.h"
diff --git a/client/constants.h b/client/constants.h
deleted file mode 100644
index 54f3fd216f2..00000000000
--- a/client/constants.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// constants.h
-
-#pragma once
-
-namespace mongo {
-
- /* query results include a 32 result flag word consisting of these bits */
- enum ResultFlagType {
- /* returned, with zero results, when getMore is called but the cursor id
- is not valid at the server. */
- ResultFlag_CursorNotFound = 1,
-
- /* { $err : ... } is being returned */
- ResultFlag_ErrSet = 2,
-
- /* Have to update config from the server, usually $err is also set */
- ResultFlag_ShardConfigStale = 4,
-
- /* for backward compatability: this let's us know the server supports
- the QueryOption_AwaitData option. if it doesn't, a repl slave client should sleep
- a little between getMore's.
- */
- ResultFlag_AwaitCapable = 8
- };
-
-}
diff --git a/client/dbclient.cpp b/client/dbclient.cpp
deleted file mode 100644
index 6b9631b09ee..00000000000
--- a/client/dbclient.cpp
+++ /dev/null
@@ -1,1053 +0,0 @@
-// dbclient.cpp - connect to a Mongo database as a database, from C++
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "pch.h"
-#include "../db/pdfile.h"
-#include "dbclient.h"
-#include "../bson/util/builder.h"
-#include "../db/jsobj.h"
-#include "../db/json.h"
-#include "../db/instance.h"
-#include "../util/md5.hpp"
-#include "../db/dbmessage.h"
-#include "../db/cmdline.h"
-#include "connpool.h"
-#include "../s/util.h"
-#include "syncclusterconnection.h"
-
-namespace mongo {
-
- void ConnectionString::_fillServers( string s ) {
-
- {
- string::size_type idx = s.find( '/' );
- if ( idx != string::npos ) {
- _setName = s.substr( 0 , idx );
- s = s.substr( idx + 1 );
- _type = SET;
- }
- }
-
- string::size_type idx;
- while ( ( idx = s.find( ',' ) ) != string::npos ) {
- _servers.push_back( s.substr( 0 , idx ) );
- s = s.substr( idx + 1 );
- }
- _servers.push_back( s );
-
- }
-
- void ConnectionString::_finishInit() {
- stringstream ss;
- if ( _type == SET )
- ss << _setName << "/";
- for ( unsigned i=0; i<_servers.size(); i++ ) {
- if ( i > 0 )
- ss << ",";
- ss << _servers[i].toString();
- }
- _string = ss.str();
- }
-
-
- DBClientBase* ConnectionString::connect( string& errmsg, double socketTimeout ) const {
- switch ( _type ) {
- case MASTER: {
- DBClientConnection * c = new DBClientConnection(true);
- c->setSoTimeout( socketTimeout );
- log(1) << "creating new connection to:" << _servers[0] << endl;
- if ( ! c->connect( _servers[0] , errmsg ) ) {
- delete c;
- return 0;
- }
- log(1) << "connected connection!" << endl;
- return c;
- }
-
- case PAIR:
- case SET: {
- DBClientReplicaSet * set = new DBClientReplicaSet( _setName , _servers , socketTimeout );
- if( ! set->connect() ) {
- delete set;
- errmsg = "connect failed to set ";
- errmsg += toString();
- return 0;
- }
- return set;
- }
-
- case SYNC: {
- // TODO , don't copy
- list<HostAndPort> l;
- for ( unsigned i=0; i<_servers.size(); i++ )
- l.push_back( _servers[i] );
- SyncClusterConnection* c = new SyncClusterConnection( l, socketTimeout );
- return c;
- }
-
- case INVALID:
- throw UserException( 13421 , "trying to connect to invalid ConnectionString" );
- break;
- }
-
- assert( 0 );
- return 0;
- }
-
- ConnectionString ConnectionString::parse( const string& host , string& errmsg ) {
-
- string::size_type i = host.find( '/' );
- if ( i != string::npos && i != 0) {
- // replica set
- return ConnectionString( SET , host.substr( i + 1 ) , host.substr( 0 , i ) );
- }
-
- int numCommas = str::count( host , ',' );
-
- if( numCommas == 0 )
- return ConnectionString( HostAndPort( host ) );
-
- if ( numCommas == 1 )
- return ConnectionString( PAIR , host );
-
- if ( numCommas == 2 )
- return ConnectionString( SYNC , host );
-
- errmsg = (string)"invalid hostname [" + host + "]";
- return ConnectionString(); // INVALID
- }
-
- string ConnectionString::typeToString( ConnectionType type ) {
- switch ( type ) {
- case INVALID:
- return "invalid";
- case MASTER:
- return "master";
- case PAIR:
- return "pair";
- case SET:
- return "set";
- case SYNC:
- return "sync";
- }
- assert(0);
- return "";
- }
-
-
- Query& Query::where(const string &jscode, BSONObj scope) {
- /* use where() before sort() and hint() and explain(), else this will assert. */
- assert( ! isComplex() );
- BSONObjBuilder b;
- b.appendElements(obj);
- b.appendWhere(jscode, scope);
- obj = b.obj();
- return *this;
- }
-
- void Query::makeComplex() {
- if ( isComplex() )
- return;
- BSONObjBuilder b;
- b.append( "query", obj );
- obj = b.obj();
- }
-
- Query& Query::sort(const BSONObj& s) {
- appendComplex( "orderby", s );
- return *this;
- }
-
- Query& Query::hint(BSONObj keyPattern) {
- appendComplex( "$hint", keyPattern );
- return *this;
- }
-
- Query& Query::explain() {
- appendComplex( "$explain", true );
- return *this;
- }
-
- Query& Query::snapshot() {
- appendComplex( "$snapshot", true );
- return *this;
- }
-
- Query& Query::minKey( const BSONObj &val ) {
- appendComplex( "$min", val );
- return *this;
- }
-
- Query& Query::maxKey( const BSONObj &val ) {
- appendComplex( "$max", val );
- return *this;
- }
-
- bool Query::isComplex( bool * hasDollar ) const {
- if ( obj.hasElement( "query" ) ) {
- if ( hasDollar )
- hasDollar[0] = false;
- return true;
- }
-
- if ( obj.hasElement( "$query" ) ) {
- if ( hasDollar )
- hasDollar[0] = true;
- return true;
- }
-
- return false;
- }
-
- BSONObj Query::getFilter() const {
- bool hasDollar;
- if ( ! isComplex( &hasDollar ) )
- return obj;
-
- return obj.getObjectField( hasDollar ? "$query" : "query" );
- }
- BSONObj Query::getSort() const {
- if ( ! isComplex() )
- return BSONObj();
- BSONObj ret = obj.getObjectField( "orderby" );
- if (ret.isEmpty())
- ret = obj.getObjectField( "$orderby" );
- return ret;
- }
- BSONObj Query::getHint() const {
- if ( ! isComplex() )
- return BSONObj();
- return obj.getObjectField( "$hint" );
- }
- bool Query::isExplain() const {
- return isComplex() && obj.getBoolField( "$explain" );
- }
-
- string Query::toString() const {
- return obj.toString();
- }
-
- /* --- dbclientcommands --- */
-
- bool DBClientWithCommands::isOk(const BSONObj& o) {
- return o["ok"].trueValue();
- }
-
- bool DBClientWithCommands::isNotMasterErrorString( const BSONElement& e ) {
- return e.type() == String && str::contains( e.valuestr() , "not master" );
- }
-
-
- enum QueryOptions DBClientWithCommands::availableOptions() {
- if ( !_haveCachedAvailableOptions ) {
- BSONObj ret;
- if ( runCommand( "admin", BSON( "availablequeryoptions" << 1 ), ret ) ) {
- _cachedAvailableOptions = ( enum QueryOptions )( ret.getIntField( "options" ) );
- }
- _haveCachedAvailableOptions = true;
- }
- return _cachedAvailableOptions;
- }
-
- inline bool DBClientWithCommands::runCommand(const string &dbname, const BSONObj& cmd, BSONObj &info, int options) {
- string ns = dbname + ".$cmd";
- info = findOne(ns, cmd, 0 , options);
- return isOk(info);
- }
-
- /* note - we build a bson obj here -- for something that is super common like getlasterror you
- should have that object prebuilt as that would be faster.
- */
- bool DBClientWithCommands::simpleCommand(const string &dbname, BSONObj *info, const string &command) {
- BSONObj o;
- if ( info == 0 )
- info = &o;
- BSONObjBuilder b;
- b.append(command, 1);
- return runCommand(dbname, b.done(), *info);
- }
-
- unsigned long long DBClientWithCommands::count(const string &myns, const BSONObj& query, int options, int limit, int skip ) {
- NamespaceString ns(myns);
- BSONObj cmd = _countCmd( myns , query , options , limit , skip );
- BSONObj res;
- if( !runCommand(ns.db.c_str(), cmd, res, options) )
- uasserted(11010,string("count fails:") + res.toString());
- return res["n"].numberLong();
- }
-
- BSONObj DBClientWithCommands::_countCmd(const string &myns, const BSONObj& query, int options, int limit, int skip ) {
- NamespaceString ns(myns);
- BSONObjBuilder b;
- b.append( "count" , ns.coll );
- b.append( "query" , query );
- if ( limit )
- b.append( "limit" , limit );
- if ( skip )
- b.append( "skip" , skip );
- return b.obj();
- }
-
- const BSONObj getlasterrorcmdobj = fromjson("{getlasterror:1}");
-
- BSONObj DBClientWithCommands::getLastErrorDetailed() {
- BSONObj info;
- runCommand("admin", getlasterrorcmdobj, info);
- return info;
- }
-
- string DBClientWithCommands::getLastError() {
- BSONObj info = getLastErrorDetailed();
- return getLastErrorString( info );
- }
-
- string DBClientWithCommands::getLastErrorString( const BSONObj& info ) {
- BSONElement e = info["err"];
- if( e.eoo() ) return "";
- if( e.type() == Object ) return e.toString();
- return e.str();
- }
-
- const BSONObj getpreverrorcmdobj = fromjson("{getpreverror:1}");
-
- BSONObj DBClientWithCommands::getPrevError() {
- BSONObj info;
- runCommand("admin", getpreverrorcmdobj, info);
- return info;
- }
-
- BSONObj getnoncecmdobj = fromjson("{getnonce:1}");
-
- string DBClientWithCommands::createPasswordDigest( const string & username , const string & clearTextPassword ) {
- md5digest d;
- {
- md5_state_t st;
- md5_init(&st);
- md5_append(&st, (const md5_byte_t *) username.data(), username.length());
- md5_append(&st, (const md5_byte_t *) ":mongo:", 7 );
- md5_append(&st, (const md5_byte_t *) clearTextPassword.data(), clearTextPassword.length());
- md5_finish(&st, d);
- }
- return digestToString( d );
- }
-
- bool DBClientWithCommands::auth(const string &dbname, const string &username, const string &password_text, string& errmsg, bool digestPassword) {
- string password = password_text;
- if( digestPassword )
- password = createPasswordDigest( username , password_text );
-
- BSONObj info;
- string nonce;
- if( !runCommand(dbname, getnoncecmdobj, info) ) {
- errmsg = "getnonce fails - connection problem?";
- return false;
- }
- {
- BSONElement e = info.getField("nonce");
- assert( e.type() == String );
- nonce = e.valuestr();
- }
-
- BSONObj authCmd;
- BSONObjBuilder b;
- {
-
- b << "authenticate" << 1 << "nonce" << nonce << "user" << username;
- md5digest d;
- {
- md5_state_t st;
- md5_init(&st);
- md5_append(&st, (const md5_byte_t *) nonce.c_str(), nonce.size() );
- md5_append(&st, (const md5_byte_t *) username.data(), username.length());
- md5_append(&st, (const md5_byte_t *) password.c_str(), password.size() );
- md5_finish(&st, d);
- }
- b << "key" << digestToString( d );
- authCmd = b.done();
- }
-
- if( runCommand(dbname, authCmd, info) )
- return true;
-
- errmsg = info.toString();
- return false;
- }
-
- BSONObj ismastercmdobj = fromjson("{\"ismaster\":1}");
-
- bool DBClientWithCommands::isMaster(bool& isMaster, BSONObj *info) {
- BSONObj o;
- if ( info == 0 )
- info = &o;
- bool ok = runCommand("admin", ismastercmdobj, *info);
- isMaster = info->getField("ismaster").trueValue();
- return ok;
- }
-
- bool DBClientWithCommands::createCollection(const string &ns, long long size, bool capped, int max, BSONObj *info) {
- assert(!capped||size);
- BSONObj o;
- if ( info == 0 ) info = &o;
- BSONObjBuilder b;
- string db = nsToDatabase(ns.c_str());
- b.append("create", ns.c_str() + db.length() + 1);
- if ( size ) b.append("size", size);
- if ( capped ) b.append("capped", true);
- if ( max ) b.append("max", max);
- return runCommand(db.c_str(), b.done(), *info);
- }
-
- bool DBClientWithCommands::copyDatabase(const string &fromdb, const string &todb, const string &fromhost, BSONObj *info) {
- BSONObj o;
- if ( info == 0 ) info = &o;
- BSONObjBuilder b;
- b.append("copydb", 1);
- b.append("fromhost", fromhost);
- b.append("fromdb", fromdb);
- b.append("todb", todb);
- return runCommand("admin", b.done(), *info);
- }
-
- bool DBClientWithCommands::setDbProfilingLevel(const string &dbname, ProfilingLevel level, BSONObj *info ) {
- BSONObj o;
- if ( info == 0 ) info = &o;
-
- if ( level ) {
- // Create system.profile collection. If it already exists this does nothing.
- // TODO: move this into the db instead of here so that all
- // drivers don't have to do this.
- string ns = dbname + ".system.profile";
- createCollection(ns.c_str(), 1024 * 1024, true, 0, info);
- }
-
- BSONObjBuilder b;
- b.append("profile", (int) level);
- return runCommand(dbname, b.done(), *info);
- }
-
- BSONObj getprofilingcmdobj = fromjson("{\"profile\":-1}");
-
- bool DBClientWithCommands::getDbProfilingLevel(const string &dbname, ProfilingLevel& level, BSONObj *info) {
- BSONObj o;
- if ( info == 0 ) info = &o;
- if ( runCommand(dbname, getprofilingcmdobj, *info) ) {
- level = (ProfilingLevel) info->getIntField("was");
- return true;
- }
- return false;
- }
-
- DBClientWithCommands::MROutput DBClientWithCommands::MRInline (BSON("inline" << 1));
-
- BSONObj DBClientWithCommands::mapreduce(const string &ns, const string &jsmapf, const string &jsreducef, BSONObj query, MROutput output) {
- BSONObjBuilder b;
- b.append("mapreduce", nsGetCollection(ns));
- b.appendCode("map", jsmapf);
- b.appendCode("reduce", jsreducef);
- if( !query.isEmpty() )
- b.append("query", query);
- b.append("out", output.out);
- BSONObj info;
- runCommand(nsGetDB(ns), b.done(), info);
- return info;
- }
-
- bool DBClientWithCommands::eval(const string &dbname, const string &jscode, BSONObj& info, BSONElement& retValue, BSONObj *args) {
- BSONObjBuilder b;
- b.appendCode("$eval", jscode);
- if ( args )
- b.appendArray("args", *args);
- bool ok = runCommand(dbname, b.done(), info);
- if ( ok )
- retValue = info.getField("retval");
- return ok;
- }
-
- bool DBClientWithCommands::eval(const string &dbname, const string &jscode) {
- BSONObj info;
- BSONElement retValue;
- return eval(dbname, jscode, info, retValue);
- }
-
- list<string> DBClientWithCommands::getDatabaseNames() {
- BSONObj info;
- uassert( 10005 , "listdatabases failed" , runCommand( "admin" , BSON( "listDatabases" << 1 ) , info ) );
- uassert( 10006 , "listDatabases.databases not array" , info["databases"].type() == Array );
-
- list<string> names;
-
- BSONObjIterator i( info["databases"].embeddedObjectUserCheck() );
- while ( i.more() ) {
- names.push_back( i.next().embeddedObjectUserCheck()["name"].valuestr() );
- }
-
- return names;
- }
-
- list<string> DBClientWithCommands::getCollectionNames( const string& db ) {
- list<string> names;
-
- string ns = db + ".system.namespaces";
- auto_ptr<DBClientCursor> c = query( ns.c_str() , BSONObj() );
- while ( c->more() ) {
- string name = c->next()["name"].valuestr();
- if ( name.find( "$" ) != string::npos )
- continue;
- names.push_back( name );
- }
- return names;
- }
-
- bool DBClientWithCommands::exists( const string& ns ) {
- list<string> names;
-
- string db = nsGetDB( ns ) + ".system.namespaces";
- BSONObj q = BSON( "name" << ns );
- return count( db.c_str() , q, QueryOption_SlaveOk ) != 0;
- }
-
- /* --- dbclientconnection --- */
-
- bool DBClientConnection::auth(const string &dbname, const string &username, const string &password_text, string& errmsg, bool digestPassword) {
- string password = password_text;
- if( digestPassword )
- password = createPasswordDigest( username , password_text );
-
- if( autoReconnect ) {
- /* note we remember the auth info before we attempt to auth -- if the connection is broken, we will
- then have it for the next autoreconnect attempt.
- */
- pair<string,string> p = pair<string,string>(username, password);
- authCache[dbname] = p;
- }
-
- return DBClientBase::auth(dbname, username, password.c_str(), errmsg, false);
- }
-
- /** query N objects from the database into an array. makes sense mostly when you want a small number of results. if a huge number, use
- query() and iterate the cursor.
- */
- void DBClientInterface::findN(vector<BSONObj>& out, const string& ns, Query query, int nToReturn, int nToSkip, const BSONObj *fieldsToReturn, int queryOptions) {
- out.reserve(nToReturn);
-
- auto_ptr<DBClientCursor> c =
- this->query(ns, query, nToReturn, nToSkip, fieldsToReturn, queryOptions);
-
- uassert( 10276 , str::stream() << "DBClientBase::findN: transport error: " << getServerAddress() << " query: " << query.toString(), c.get() );
-
- if ( c->hasResultFlag( ResultFlag_ShardConfigStale ) )
- throw StaleConfigException( ns , "findN stale config" );
-
- for( int i = 0; i < nToReturn; i++ ) {
- if ( !c->more() )
- break;
- out.push_back( c->nextSafe().copy() );
- }
- }
-
- BSONObj DBClientInterface::findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn, int queryOptions) {
- vector<BSONObj> v;
- findN(v, ns, query, 1, 0, fieldsToReturn, queryOptions);
- return v.empty() ? BSONObj() : v[0];
- }
-
- bool DBClientConnection::connect(const HostAndPort& server, string& errmsg) {
- _server = server;
- _serverString = _server.toString();
- return _connect( errmsg );
- }
-
- bool DBClientConnection::_connect( string& errmsg ) {
- _serverString = _server.toString();
- // we keep around SockAddr for connection life -- maybe MessagingPort
- // requires that?
- server.reset(new SockAddr(_server.host().c_str(), _server.port()));
- p.reset(new MessagingPort( _so_timeout, _logLevel ));
-
- if (server->getAddr() == "0.0.0.0") {
- _failed = true;
- return false;
- }
-
- // if( _so_timeout == 0 ){
- // printStackTrace();
- // log() << "Connecting to server " << _serverString << " timeout " << _so_timeout << endl;
- // }
- if ( !p->connect(*server) ) {
- stringstream ss;
- ss << "couldn't connect to server " << _serverString;
- errmsg = ss.str();
- _failed = true;
- return false;
- }
-
-#ifdef MONGO_SSL
- if ( cmdLine.sslOnNormalPorts ) {
- p->secure( sslManager() );
- }
-#endif
-
- return true;
- }
-
-
- inline bool DBClientConnection::runCommand(const string &dbname, const BSONObj& cmd, BSONObj &info, int options) {
- if ( DBClientWithCommands::runCommand( dbname , cmd , info , options ) )
- return true;
-
- if ( clientSet && isNotMasterErrorString( info["errmsg"] ) ) {
- clientSet->isntMaster();
- // At this point, we've probably deleted *this* object, do *not* use afterward
- }
-
- return false;
- }
-
-
- void DBClientConnection::_checkConnection() {
- if ( !_failed )
- return;
- if ( lastReconnectTry && time(0)-lastReconnectTry < 2 ) {
- // we wait a little before reconnect attempt to avoid constant hammering.
- // but we throw we don't want to try to use a connection in a bad state
- throw SocketException( SocketException::FAILED_STATE , toString() );
- }
- if ( !autoReconnect )
- throw SocketException( SocketException::FAILED_STATE , toString() );
-
- lastReconnectTry = time(0);
- log(_logLevel) << "trying reconnect to " << _serverString << endl;
- string errmsg;
- _failed = false;
- if ( ! _connect(errmsg) ) {
- _failed = true;
- log(_logLevel) << "reconnect " << _serverString << " failed " << errmsg << endl;
- throw SocketException( SocketException::CONNECT_ERROR , toString() );
- }
-
- log(_logLevel) << "reconnect " << _serverString << " ok" << endl;
- for( map< string, pair<string,string> >::iterator i = authCache.begin(); i != authCache.end(); i++ ) {
- const char *dbname = i->first.c_str();
- const char *username = i->second.first.c_str();
- const char *password = i->second.second.c_str();
- if( !DBClientBase::auth(dbname, username, password, errmsg, false) )
- log(_logLevel) << "reconnect: auth failed db:" << dbname << " user:" << username << ' ' << errmsg << '\n';
- }
- }
-
- auto_ptr<DBClientCursor> DBClientBase::query(const string &ns, Query query, int nToReturn,
- int nToSkip, const BSONObj *fieldsToReturn, int queryOptions , int batchSize ) {
- auto_ptr<DBClientCursor> c( new DBClientCursor( this,
- ns, query.obj, nToReturn, nToSkip,
- fieldsToReturn, queryOptions , batchSize ) );
- if ( c->init() )
- return c;
- return auto_ptr< DBClientCursor >( 0 );
- }
-
- auto_ptr<DBClientCursor> DBClientBase::getMore( const string &ns, long long cursorId, int nToReturn, int options ) {
- auto_ptr<DBClientCursor> c( new DBClientCursor( this, ns, cursorId, nToReturn, options ) );
- if ( c->init() )
- return c;
- return auto_ptr< DBClientCursor >( 0 );
- }
-
- struct DBClientFunConvertor {
- void operator()( DBClientCursorBatchIterator &i ) {
- while( i.moreInCurrentBatch() ) {
- _f( i.nextSafe() );
- }
- }
- boost::function<void(const BSONObj &)> _f;
- };
-
- unsigned long long DBClientConnection::query( boost::function<void(const BSONObj&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn, int queryOptions ) {
- DBClientFunConvertor fun;
- fun._f = f;
- boost::function<void(DBClientCursorBatchIterator &)> ptr( fun );
- return DBClientConnection::query( ptr, ns, query, fieldsToReturn, queryOptions );
- }
-
- unsigned long long DBClientConnection::query( boost::function<void(DBClientCursorBatchIterator &)> f, const string& ns, Query query, const BSONObj *fieldsToReturn, int queryOptions ) {
- // mask options
- queryOptions &= (int)( QueryOption_NoCursorTimeout | QueryOption_SlaveOk );
- unsigned long long n = 0;
-
- bool doExhaust = ( availableOptions() & QueryOption_Exhaust );
- if ( doExhaust ) {
- queryOptions |= (int)QueryOption_Exhaust;
- }
- auto_ptr<DBClientCursor> c( this->query(ns, query, 0, 0, fieldsToReturn, queryOptions) );
- uassert( 13386, "socket error for mapping query", c.get() );
-
- if ( !doExhaust ) {
- while( c->more() ) {
- DBClientCursorBatchIterator i( *c );
- f( i );
- n += i.n();
- }
- return n;
- }
-
- try {
- while( 1 ) {
- while( c->moreInCurrentBatch() ) {
- DBClientCursorBatchIterator i( *c );
- f( i );
- n += i.n();
- }
-
- if( c->getCursorId() == 0 )
- break;
-
- c->exhaustReceiveMore();
- }
- }
- catch(std::exception&) {
- /* connection CANNOT be used anymore as more data may be on the way from the server.
- we have to reconnect.
- */
- _failed = true;
- p->shutdown();
- throw;
- }
-
- return n;
- }
-
- void DBClientBase::insert( const string & ns , BSONObj obj , int flags) {
- Message toSend;
-
- BufBuilder b;
- b.appendNum( flags );
- b.appendStr( ns );
- obj.appendSelfToBufBuilder( b );
-
- toSend.setData( dbInsert , b.buf() , b.len() );
-
- say( toSend );
- }
-
- void DBClientBase::insert( const string & ns , const vector< BSONObj > &v , int flags) {
- Message toSend;
-
- BufBuilder b;
- b.appendNum( flags );
- b.appendStr( ns );
- for( vector< BSONObj >::const_iterator i = v.begin(); i != v.end(); ++i )
- i->appendSelfToBufBuilder( b );
-
- toSend.setData( dbInsert, b.buf(), b.len() );
-
- say( toSend );
- }
-
- void DBClientBase::remove( const string & ns , Query obj , bool justOne ) {
- Message toSend;
-
- BufBuilder b;
- int opts = 0;
- b.appendNum( opts );
- b.appendStr( ns );
-
- int flags = 0;
- if ( justOne )
- flags |= RemoveOption_JustOne;
- b.appendNum( flags );
-
- obj.obj.appendSelfToBufBuilder( b );
-
- toSend.setData( dbDelete , b.buf() , b.len() );
-
- say( toSend );
- }
-
- void DBClientBase::update( const string & ns , Query query , BSONObj obj , bool upsert , bool multi ) {
-
- BufBuilder b;
- b.appendNum( (int)0 ); // reserved
- b.appendStr( ns );
-
- int flags = 0;
- if ( upsert ) flags |= UpdateOption_Upsert;
- if ( multi ) flags |= UpdateOption_Multi;
- b.appendNum( flags );
-
- query.obj.appendSelfToBufBuilder( b );
- obj.appendSelfToBufBuilder( b );
-
- Message toSend;
- toSend.setData( dbUpdate , b.buf() , b.len() );
-
- say( toSend );
-
-
- }
-
-
-
- auto_ptr<DBClientCursor> DBClientWithCommands::getIndexes( const string &ns ) {
- return query( Namespace( ns.c_str() ).getSisterNS( "system.indexes" ).c_str() , BSON( "ns" << ns ) );
- }
-
- void DBClientWithCommands::dropIndex( const string& ns , BSONObj keys ) {
- dropIndex( ns , genIndexName( keys ) );
- }
-
-
- void DBClientWithCommands::dropIndex( const string& ns , const string& indexName ) {
- BSONObj info;
- if ( ! runCommand( nsToDatabase( ns.c_str() ) ,
- BSON( "deleteIndexes" << NamespaceString( ns ).coll << "index" << indexName ) ,
- info ) ) {
- log(_logLevel) << "dropIndex failed: " << info << endl;
- uassert( 10007 , "dropIndex failed" , 0 );
- }
- resetIndexCache();
- }
-
- void DBClientWithCommands::dropIndexes( const string& ns ) {
- BSONObj info;
- uassert( 10008 , "dropIndexes failed" , runCommand( nsToDatabase( ns.c_str() ) ,
- BSON( "deleteIndexes" << NamespaceString( ns ).coll << "index" << "*") ,
- info ) );
- resetIndexCache();
- }
-
- void DBClientWithCommands::reIndex( const string& ns ) {
- list<BSONObj> all;
- auto_ptr<DBClientCursor> i = getIndexes( ns );
- while ( i->more() ) {
- all.push_back( i->next().getOwned() );
- }
-
- dropIndexes( ns );
-
- for ( list<BSONObj>::iterator i=all.begin(); i!=all.end(); i++ ) {
- BSONObj o = *i;
- insert( Namespace( ns.c_str() ).getSisterNS( "system.indexes" ).c_str() , o );
- }
-
- }
-
-
- string DBClientWithCommands::genIndexName( const BSONObj& keys ) {
- stringstream ss;
-
- bool first = 1;
- for ( BSONObjIterator i(keys); i.more(); ) {
- BSONElement f = i.next();
-
- if ( first )
- first = 0;
- else
- ss << "_";
-
- ss << f.fieldName() << "_";
- if( f.isNumber() )
- ss << f.numberInt();
- }
- return ss.str();
- }
-
- bool DBClientWithCommands::ensureIndex( const string &ns , BSONObj keys , bool unique, const string & name , bool cache, bool background, int version ) {
- BSONObjBuilder toSave;
- toSave.append( "ns" , ns );
- toSave.append( "key" , keys );
-
- string cacheKey(ns);
- cacheKey += "--";
-
- if ( name != "" ) {
- toSave.append( "name" , name );
- cacheKey += name;
- }
- else {
- string nn = genIndexName( keys );
- toSave.append( "name" , nn );
- cacheKey += nn;
- }
-
- if( version >= 0 )
- toSave.append("v", version);
-
- if ( unique )
- toSave.appendBool( "unique", unique );
-
- if( background )
- toSave.appendBool( "background", true );
-
- if ( _seenIndexes.count( cacheKey ) )
- return 0;
-
- if ( cache )
- _seenIndexes.insert( cacheKey );
-
- insert( Namespace( ns.c_str() ).getSisterNS( "system.indexes" ).c_str() , toSave.obj() );
- return 1;
- }
-
- void DBClientWithCommands::resetIndexCache() {
- _seenIndexes.clear();
- }
-
- /* -- DBClientCursor ---------------------------------------------- */
-
-#ifdef _DEBUG
-#define CHECK_OBJECT( o , msg ) massert( 10337 , (string)"object not valid" + (msg) , (o).isValid() )
-#else
-#define CHECK_OBJECT( o , msg )
-#endif
-
- void assembleRequest( const string &ns, BSONObj query, int nToReturn, int nToSkip, const BSONObj *fieldsToReturn, int queryOptions, Message &toSend ) {
- CHECK_OBJECT( query , "assembleRequest query" );
- // see query.h for the protocol we are using here.
- BufBuilder b;
- int opts = queryOptions;
- b.appendNum(opts);
- b.appendStr(ns);
- b.appendNum(nToSkip);
- b.appendNum(nToReturn);
- query.appendSelfToBufBuilder(b);
- if ( fieldsToReturn )
- fieldsToReturn->appendSelfToBufBuilder(b);
- toSend.setData(dbQuery, b.buf(), b.len());
- }
-
- void DBClientConnection::say( Message &toSend, bool isRetry ) {
- checkConnection();
- try {
- port().say( toSend );
- }
- catch( SocketException & ) {
- _failed = true;
- throw;
- }
- }
-
- void DBClientConnection::sayPiggyBack( Message &toSend ) {
- port().piggyBack( toSend );
- }
-
- bool DBClientConnection::recv( Message &m ) {
- return port().recv(m);
- }
-
- bool DBClientConnection::call( Message &toSend, Message &response, bool assertOk , string * actualServer ) {
- /* todo: this is very ugly messagingport::call returns an error code AND can throw
- an exception. we should make it return void and just throw an exception anytime
- it fails
- */
- checkConnection();
- try {
- if ( !port().call(toSend, response) ) {
- _failed = true;
- if ( assertOk )
- uasserted( 10278 , str::stream() << "dbclient error communicating with server: " << getServerAddress() );
-
- return false;
- }
- }
- catch( SocketException & ) {
- _failed = true;
- throw;
- }
- return true;
- }
-
- BSONElement getErrField(const BSONObj& o) {
- BSONElement first = o.firstElement();
- if( strcmp(first.fieldName(), "$err") == 0 )
- return first;
-
- // temp - will be DEV only later
- /*DEV*/
- if( 1 ) {
- BSONElement e = o["$err"];
- if( !e.eoo() ) {
- wassert(false);
- }
- return e;
- }
-
- return BSONElement();
- }
-
- bool hasErrField( const BSONObj& o ){
- return ! getErrField( o ).eoo();
- }
-
- void DBClientConnection::checkResponse( const char *data, int nReturned, bool* retry, string* host ) {
- /* check for errors. the only one we really care about at
- * this stage is "not master"
- */
-
- *retry = false;
- *host = _serverString;
-
- if ( clientSet && nReturned ) {
- assert(data);
- BSONObj o(data);
- if ( isNotMasterErrorString( getErrField(o) ) ) {
- clientSet->isntMaster();
- }
- }
- }
-
- void DBClientConnection::killCursor( long long cursorId ) {
- StackBufBuilder b;
- b.appendNum( (int)0 ); // reserved
- b.appendNum( (int)1 ); // number
- b.appendNum( cursorId );
-
- Message m;
- m.setData( dbKillCursors , b.buf() , b.len() );
-
- if ( _lazyKillCursor )
- sayPiggyBack( m );
- else
- say(m);
- }
-
-#ifdef MONGO_SSL
- SSLManager* DBClientConnection::sslManager() {
- if ( _sslManager )
- return _sslManager;
-
- SSLManager* s = new SSLManager(true);
- _sslManager = s;
- return s;
- }
-
- SSLManager* DBClientConnection::_sslManager = 0;
-#endif
-
- AtomicUInt DBClientConnection::_numConnections;
- bool DBClientConnection::_lazyKillCursor = true;
-
-
- bool serverAlive( const string &uri ) {
- DBClientConnection c( false, 0, 20 ); // potentially the connection to server could fail while we're checking if it's alive - so use timeouts
- string err;
- if ( !c.connect( uri, err ) )
- return false;
- if ( !c.simpleCommand( "admin", 0, "ping" ) )
- return false;
- return true;
- }
-
-} // namespace mongo
diff --git a/client/dbclient.h b/client/dbclient.h
deleted file mode 100644
index ea55bb474af..00000000000
--- a/client/dbclient.h
+++ /dev/null
@@ -1,983 +0,0 @@
-/** @file dbclient.h
-
- Core MongoDB C++ driver interfaces are defined here.
-*/
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "../pch.h"
-#include "../util/net/message.h"
-#include "../util/net/message_port.h"
-#include "../db/jsobj.h"
-#include "../db/json.h"
-#include <stack>
-
-namespace mongo {
-
- /** the query field 'options' can have these bits set: */
- enum QueryOptions {
- /** Tailable means cursor is not closed when the last data is retrieved. rather, the cursor marks
- the final object's position. you can resume using the cursor later, from where it was located,
- if more data were received. Set on dbQuery and dbGetMore.
-
- like any "latent cursor", the cursor may become invalid at some point -- for example if that
- final object it references were deleted. Thus, you should be prepared to requery if you get back
- ResultFlag_CursorNotFound.
- */
- QueryOption_CursorTailable = 1 << 1,
-
- /** allow query of replica slave. normally these return an error except for namespace "local".
- */
- QueryOption_SlaveOk = 1 << 2,
-
- // findingStart mode is used to find the first operation of interest when
- // we are scanning through a repl log. For efficiency in the common case,
- // where the first operation of interest is closer to the tail than the head,
- // we start from the tail of the log and work backwards until we find the
- // first operation of interest. Then we scan forward from that first operation,
- // actually returning results to the client. During the findingStart phase,
- // we release the db mutex occasionally to avoid blocking the db process for
- // an extended period of time.
- QueryOption_OplogReplay = 1 << 3,
-
- /** The server normally times out idle cursors after an inactivy period to prevent excess memory uses
- Set this option to prevent that.
- */
- QueryOption_NoCursorTimeout = 1 << 4,
-
- /** Use with QueryOption_CursorTailable. If we are at the end of the data, block for a while rather
- than returning no data. After a timeout period, we do return as normal.
- */
- QueryOption_AwaitData = 1 << 5,
-
- /** Stream the data down full blast in multiple "more" packages, on the assumption that the client
- will fully read all data queried. Faster when you are pulling a lot of data and know you want to
- pull it all down. Note: it is not allowed to not read all the data unless you close the connection.
-
- Use the query( boost::function<void(const BSONObj&)> f, ... ) version of the connection's query()
- method, and it will take care of all the details for you.
- */
- QueryOption_Exhaust = 1 << 6,
-
- /** When sharded, this means its ok to return partial results
- Usually we will fail a query if all required shards aren't up
- If this is set, it'll be a partial result set
- */
- QueryOption_PartialResults = 1 << 7 ,
-
- QueryOption_AllSupported = QueryOption_CursorTailable | QueryOption_SlaveOk | QueryOption_OplogReplay | QueryOption_NoCursorTimeout | QueryOption_AwaitData | QueryOption_Exhaust | QueryOption_PartialResults
-
- };
-
- enum UpdateOptions {
- /** Upsert - that is, insert the item if no matching item is found. */
- UpdateOption_Upsert = 1 << 0,
-
- /** Update multiple documents (if multiple documents match query expression).
- (Default is update a single document and stop.) */
- UpdateOption_Multi = 1 << 1,
-
- /** flag from mongo saying this update went everywhere */
- UpdateOption_Broadcast = 1 << 2
- };
-
- enum RemoveOptions {
- /** only delete one option */
- RemoveOption_JustOne = 1 << 0,
-
- /** flag from mongo saying this update went everywhere */
- RemoveOption_Broadcast = 1 << 1
- };
-
-
- /**
- * need to put in DbMesssage::ReservedOptions as well
- */
- enum InsertOptions {
- /** With muli-insert keep processing inserts if one fails */
- InsertOption_ContinueOnError = 1 << 0
- };
-
- class DBClientBase;
-
- /**
- * ConnectionString handles parsing different ways to connect to mongo and determining method
- * samples:
- * server
- * server:port
- * foo/server:port,server:port SET
- * server,server,server SYNC
- *
- * tyipcal use
- * string errmsg,
- * ConnectionString cs = ConnectionString::parse( url , errmsg );
- * if ( ! cs.isValid() ) throw "bad: " + errmsg;
- * DBClientBase * conn = cs.connect( errmsg );
- */
- class ConnectionString {
- public:
- enum ConnectionType { INVALID , MASTER , PAIR , SET , SYNC };
-
- ConnectionString() {
- _type = INVALID;
- }
-
- ConnectionString( const HostAndPort& server ) {
- _type = MASTER;
- _servers.push_back( server );
- _finishInit();
- }
-
- ConnectionString( ConnectionType type , const string& s , const string& setName = "" ) {
- _type = type;
- _setName = setName;
- _fillServers( s );
-
- switch ( _type ) {
- case MASTER:
- assert( _servers.size() == 1 );
- break;
- case SET:
- assert( _setName.size() );
- assert( _servers.size() >= 1 ); // 1 is ok since we can derive
- break;
- case PAIR:
- assert( _servers.size() == 2 );
- break;
- default:
- assert( _servers.size() > 0 );
- }
-
- _finishInit();
- }
-
- ConnectionString( const string& s , ConnectionType favoredMultipleType ) {
- _type = INVALID;
-
- _fillServers( s );
- if ( _type != INVALID ) {
- // set already
- }
- else if ( _servers.size() == 1 ) {
- _type = MASTER;
- }
- else {
- _type = favoredMultipleType;
- assert( _type == SET || _type == SYNC );
- }
- _finishInit();
- }
-
- bool isValid() const { return _type != INVALID; }
-
- string toString() const { return _string; }
-
- DBClientBase* connect( string& errmsg, double socketTimeout = 0 ) const;
-
- string getSetName() const { return _setName; }
-
- vector<HostAndPort> getServers() const { return _servers; }
-
- ConnectionType type() const { return _type; }
-
- static ConnectionString parse( const string& url , string& errmsg );
-
- static string typeToString( ConnectionType type );
-
- private:
-
- void _fillServers( string s );
- void _finishInit();
-
- ConnectionType _type;
- vector<HostAndPort> _servers;
- string _string;
- string _setName;
- };
-
- /**
- * controls how much a clients cares about writes
- * default is NORMAL
- */
- enum WriteConcern {
- W_NONE = 0 , // TODO: not every connection type fully supports this
- W_NORMAL = 1
- // TODO SAFE = 2
- };
-
- class BSONObj;
- class ScopedDbConnection;
- class DBClientCursor;
- class DBClientCursorBatchIterator;
-
- /** Represents a Mongo query expression. Typically one uses the QUERY(...) macro to construct a Query object.
- Examples:
- QUERY( "age" << 33 << "school" << "UCLA" ).sort("name")
- QUERY( "age" << GT << 30 << LT << 50 )
- */
- class Query {
- public:
- BSONObj obj;
- Query() : obj(BSONObj()) { }
- Query(const BSONObj& b) : obj(b) { }
- Query(const string &json) :
- obj(fromjson(json)) { }
- Query(const char * json) :
- obj(fromjson(json)) { }
-
- /** Add a sort (ORDER BY) criteria to the query expression.
- @param sortPattern the sort order template. For example to order by name ascending, time descending:
- { name : 1, ts : -1 }
- i.e.
- BSON( "name" << 1 << "ts" << -1 )
- or
- fromjson(" name : 1, ts : -1 ")
- */
- Query& sort(const BSONObj& sortPattern);
-
- /** Add a sort (ORDER BY) criteria to the query expression.
- This version of sort() assumes you want to sort on a single field.
- @param asc = 1 for ascending order
- asc = -1 for descending order
- */
- Query& sort(const string &field, int asc = 1) { sort( BSON( field << asc ) ); return *this; }
-
- /** Provide a hint to the query.
- @param keyPattern Key pattern for the index to use.
- Example:
- hint("{ts:1}")
- */
- Query& hint(BSONObj keyPattern);
- Query& hint(const string &jsonKeyPatt) { return hint(fromjson(jsonKeyPatt)); }
-
- /** Provide min and/or max index limits for the query.
- min <= x < max
- */
- Query& minKey(const BSONObj &val);
- /**
- max is exclusive
- */
- Query& maxKey(const BSONObj &val);
-
- /** Return explain information about execution of this query instead of the actual query results.
- Normally it is easier to use the mongo shell to run db.find(...).explain().
- */
- Query& explain();
-
- /** Use snapshot mode for the query. Snapshot mode assures no duplicates are returned, or objects missed, which were
- present at both the start and end of the query's execution (if an object is new during the query, or deleted during
- the query, it may or may not be returned, even with snapshot mode).
-
- Note that short query responses (less than 1MB) are always effectively snapshotted.
-
- Currently, snapshot mode may not be used with sorting or explicit hints.
- */
- Query& snapshot();
-
- /** Queries to the Mongo database support a $where parameter option which contains
- a javascript function that is evaluated to see whether objects being queried match
- its criteria. Use this helper to append such a function to a query object.
- Your query may also contain other traditional Mongo query terms.
-
- @param jscode The javascript function to evaluate against each potential object
- match. The function must return true for matched objects. Use the this
- variable to inspect the current object.
- @param scope SavedContext for the javascript object. List in a BSON object any
- variables you would like defined when the jscode executes. One can think
- of these as "bind variables".
-
- Examples:
- conn.findOne("test.coll", Query("{a:3}").where("this.b == 2 || this.c == 3"));
- Query badBalance = Query().where("this.debits - this.credits < 0");
- */
- Query& where(const string &jscode, BSONObj scope);
- Query& where(const string &jscode) { return where(jscode, BSONObj()); }
-
- /**
- * @return true if this query has an orderby, hint, or some other field
- */
- bool isComplex( bool * hasDollar = 0 ) const;
-
- BSONObj getFilter() const;
- BSONObj getSort() const;
- BSONObj getHint() const;
- bool isExplain() const;
-
- string toString() const;
- operator string() const { return toString(); }
- private:
- void makeComplex();
- template< class T >
- void appendComplex( const char *fieldName, const T& val ) {
- makeComplex();
- BSONObjBuilder b;
- b.appendElements(obj);
- b.append(fieldName, val);
- obj = b.obj();
- }
- };
-
- /** Typically one uses the QUERY(...) macro to construct a Query object.
- Example: QUERY( "age" << 33 << "school" << "UCLA" )
- */
-#define QUERY(x) mongo::Query( BSON(x) )
-
- /**
- interface that handles communication with the db
- */
- class DBConnector {
- public:
- virtual ~DBConnector() {}
- /** actualServer is set to the actual server where they call went if there was a choice (SlaveOk) */
- virtual bool call( Message &toSend, Message &response, bool assertOk=true , string * actualServer = 0 ) = 0;
- virtual void say( Message &toSend, bool isRetry = false ) = 0;
- virtual void sayPiggyBack( Message &toSend ) = 0;
- /* used by QueryOption_Exhaust. To use that your subclass must implement this. */
- virtual bool recv( Message& m ) { assert(false); return false; }
- // In general, for lazy queries, we'll need to say, recv, then checkResponse
- virtual void checkResponse( const char* data, int nReturned, bool* retry = NULL, string* targetHost = NULL ) {
- if( retry ) *retry = false; if( targetHost ) *targetHost = "";
- }
- virtual bool lazySupported() const = 0;
- };
-
- /**
- The interface that any db connection should implement
- */
- class DBClientInterface : boost::noncopyable {
- public:
- virtual auto_ptr<DBClientCursor> query(const string &ns, Query query, int nToReturn = 0, int nToSkip = 0,
- const BSONObj *fieldsToReturn = 0, int queryOptions = 0 , int batchSize = 0 ) = 0;
-
- virtual void insert( const string &ns, BSONObj obj , int flags=0) = 0;
-
- virtual void insert( const string &ns, const vector< BSONObj >& v , int flags=0) = 0;
-
- virtual void remove( const string &ns , Query query, bool justOne = 0 ) = 0;
-
- virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = 0 , bool multi = 0 ) = 0;
-
- virtual ~DBClientInterface() { }
-
- /**
- @return a single object that matches the query. if none do, then the object is empty
- @throws AssertionException
- */
- virtual BSONObj findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
-
- /** query N objects from the database into an array. makes sense mostly when you want a small number of results. if a huge number, use
- query() and iterate the cursor.
- */
- void findN(vector<BSONObj>& out, const string&ns, Query query, int nToReturn, int nToSkip = 0, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
-
- virtual string getServerAddress() const = 0;
-
- /** don't use this - called automatically by DBClientCursor for you */
- virtual auto_ptr<DBClientCursor> getMore( const string &ns, long long cursorId, int nToReturn = 0, int options = 0 ) = 0;
- };
-
- /**
- DB "commands"
- Basically just invocations of connection.$cmd.findOne({...});
- */
- class DBClientWithCommands : public DBClientInterface {
- set<string> _seenIndexes;
- public:
- /** controls how chatty the client is about network errors & such. See log.h */
- int _logLevel;
-
- DBClientWithCommands() : _logLevel(0), _cachedAvailableOptions( (enum QueryOptions)0 ), _haveCachedAvailableOptions(false) { }
-
- /** helper function. run a simple command where the command expression is simply
- { command : 1 }
- @param info -- where to put result object. may be null if caller doesn't need that info
- @param command -- command name
- @return true if the command returned "ok".
- */
- bool simpleCommand(const string &dbname, BSONObj *info, const string &command);
-
- /** Run a database command. Database commands are represented as BSON objects. Common database
- commands have prebuilt helper functions -- see below. If a helper is not available you can
- directly call runCommand.
-
- @param dbname database name. Use "admin" for global administrative commands.
- @param cmd the command object to execute. For example, { ismaster : 1 }
- @param info the result object the database returns. Typically has { ok : ..., errmsg : ... } fields
- set.
- @param options see enum QueryOptions - normally not needed to run a command
- @return true if the command returned "ok".
- */
- virtual bool runCommand(const string &dbname, const BSONObj& cmd, BSONObj &info, int options=0);
-
- /** Authorize access to a particular database.
- Authentication is separate for each database on the server -- you may authenticate for any
- number of databases on a single connection.
- The "admin" database is special and once authenticated provides access to all databases on the
- server.
- @param digestPassword if password is plain text, set this to true. otherwise assumed to be pre-digested
- @return true if successful
- */
- virtual bool auth(const string &dbname, const string &username, const string &pwd, string& errmsg, bool digestPassword = true);
-
- /** count number of objects in collection ns that match the query criteria specified
- throws UserAssertion if database returns an error
- */
- virtual unsigned long long count(const string &ns, const BSONObj& query = BSONObj(), int options=0, int limit=0, int skip=0 );
-
- string createPasswordDigest( const string &username , const string &clearTextPassword );
-
- /** returns true in isMaster parm if this db is the current master
- of a replica pair.
-
- pass in info for more details e.g.:
- { "ismaster" : 1.0 , "msg" : "not paired" , "ok" : 1.0 }
-
- returns true if command invoked successfully.
- */
- virtual bool isMaster(bool& isMaster, BSONObj *info=0);
-
- /**
- Create a new collection in the database. Normally, collection creation is automatic. You would
- use this function if you wish to specify special options on creation.
-
- If the collection already exists, no action occurs.
-
- @param ns fully qualified collection name
- @param size desired initial extent size for the collection.
- Must be <= 1000000000 for normal collections.
- For fixed size (capped) collections, this size is the total/max size of the
- collection.
- @param capped if true, this is a fixed size collection (where old data rolls out).
- @param max maximum number of objects if capped (optional).
-
- returns true if successful.
- */
- bool createCollection(const string &ns, long long size = 0, bool capped = false, int max = 0, BSONObj *info = 0);
-
- /** Get error result from the last write operation (insert/update/delete) on this connection.
- @return error message text, or empty string if no error.
- */
- string getLastError();
-
- /** Get error result from the last write operation (insert/update/delete) on this connection.
- @return full error object.
- */
- virtual BSONObj getLastErrorDetailed();
-
- /** Can be called with the returned value from getLastErrorDetailed to extract an error string.
- If all you need is the string, just call getLastError() instead.
- */
- static string getLastErrorString( const BSONObj& res );
-
- /** Return the last error which has occurred, even if not the very last operation.
-
- @return { err : <error message>, nPrev : <how_many_ops_back_occurred>, ok : 1 }
-
- result.err will be null if no error has occurred.
- */
- BSONObj getPrevError();
-
- /** Reset the previous error state for this connection (accessed via getLastError and
- getPrevError). Useful when performing several operations at once and then checking
- for an error after attempting all operations.
- */
- bool resetError() { return simpleCommand("admin", 0, "reseterror"); }
-
- /** Delete the specified collection. */
- virtual bool dropCollection( const string &ns ) {
- string db = nsGetDB( ns );
- string coll = nsGetCollection( ns );
- uassert( 10011 , "no collection name", coll.size() );
-
- BSONObj info;
-
- bool res = runCommand( db.c_str() , BSON( "drop" << coll ) , info );
- resetIndexCache();
- return res;
- }
-
- /** Perform a repair and compaction of the specified database. May take a long time to run. Disk space
- must be available equal to the size of the database while repairing.
- */
- bool repairDatabase(const string &dbname, BSONObj *info = 0) {
- return simpleCommand(dbname, info, "repairDatabase");
- }
-
- /** Copy database from one server or name to another server or name.
-
- Generally, you should dropDatabase() first as otherwise the copied information will MERGE
- into whatever data is already present in this database.
-
- For security reasons this function only works when you are authorized to access the "admin" db. However,
- if you have access to said db, you can copy any database from one place to another.
- TODO: this needs enhancement to be more flexible in terms of security.
-
- This method provides a way to "rename" a database by copying it to a new db name and
- location. The copy is "repaired" and compacted.
-
- fromdb database name from which to copy.
- todb database name to copy to.
- fromhost hostname of the database (and optionally, ":port") from which to
- copy the data. copies from self if "".
-
- returns true if successful
- */
- bool copyDatabase(const string &fromdb, const string &todb, const string &fromhost = "", BSONObj *info = 0);
-
- /** The Mongo database provides built-in performance profiling capabilities. Uset setDbProfilingLevel()
- to enable. Profiling information is then written to the system.profiling collection, which one can
- then query.
- */
- enum ProfilingLevel {
- ProfileOff = 0,
- ProfileSlow = 1, // log very slow (>100ms) operations
- ProfileAll = 2
-
- };
- bool setDbProfilingLevel(const string &dbname, ProfilingLevel level, BSONObj *info = 0);
- bool getDbProfilingLevel(const string &dbname, ProfilingLevel& level, BSONObj *info = 0);
-
-
- /** This implicitly converts from char*, string, and BSONObj to be an argument to mapreduce
- You shouldn't need to explicitly construct this
- */
- struct MROutput {
- MROutput(const char* collection) : out(BSON("replace" << collection)) {}
- MROutput(const string& collection) : out(BSON("replace" << collection)) {}
- MROutput(const BSONObj& obj) : out(obj) {}
-
- BSONObj out;
- };
- static MROutput MRInline;
-
- /** Run a map/reduce job on the server.
-
- See http://www.mongodb.org/display/DOCS/MapReduce
-
- ns namespace (db+collection name) of input data
- jsmapf javascript map function code
- jsreducef javascript reduce function code.
- query optional query filter for the input
- output either a string collection name or an object representing output type
- if not specified uses inline output type
-
- returns a result object which contains:
- { result : <collection_name>,
- numObjects : <number_of_objects_scanned>,
- timeMillis : <job_time>,
- ok : <1_if_ok>,
- [, err : <errmsg_if_error>]
- }
-
- For example one might call:
- result.getField("ok").trueValue()
- on the result to check if ok.
- */
- BSONObj mapreduce(const string &ns, const string &jsmapf, const string &jsreducef, BSONObj query = BSONObj(), MROutput output = MRInline);
-
- /** Run javascript code on the database server.
- dbname database SavedContext in which the code runs. The javascript variable 'db' will be assigned
- to this database when the function is invoked.
- jscode source code for a javascript function.
- info the command object which contains any information on the invocation result including
- the return value and other information. If an error occurs running the jscode, error
- information will be in info. (try "out() << info.toString()")
- retValue return value from the jscode function.
- args args to pass to the jscode function. when invoked, the 'args' variable will be defined
- for use by the jscode.
-
- returns true if runs ok.
-
- See testDbEval() in dbclient.cpp for an example of usage.
- */
- bool eval(const string &dbname, const string &jscode, BSONObj& info, BSONElement& retValue, BSONObj *args = 0);
-
- /** validate a collection, checking for errors and reporting back statistics.
- this operation is slow and blocking.
- */
- bool validate( const string &ns , bool scandata=true ) {
- BSONObj cmd = BSON( "validate" << nsGetCollection( ns ) << "scandata" << scandata );
- BSONObj info;
- return runCommand( nsGetDB( ns ).c_str() , cmd , info );
- }
-
- /* The following helpers are simply more convenient forms of eval() for certain common cases */
-
- /* invocation with no return value of interest -- with or without one simple parameter */
- bool eval(const string &dbname, const string &jscode);
- template< class T >
- bool eval(const string &dbname, const string &jscode, T parm1) {
- BSONObj info;
- BSONElement retValue;
- BSONObjBuilder b;
- b.append("0", parm1);
- BSONObj args = b.done();
- return eval(dbname, jscode, info, retValue, &args);
- }
-
- /** eval invocation with one parm to server and one numeric field (either int or double) returned */
- template< class T, class NumType >
- bool eval(const string &dbname, const string &jscode, T parm1, NumType& ret) {
- BSONObj info;
- BSONElement retValue;
- BSONObjBuilder b;
- b.append("0", parm1);
- BSONObj args = b.done();
- if ( !eval(dbname, jscode, info, retValue, &args) )
- return false;
- ret = (NumType) retValue.number();
- return true;
- }
-
- /**
- get a list of all the current databases
- uses the { listDatabases : 1 } command.
- throws on error
- */
- list<string> getDatabaseNames();
-
- /**
- get a list of all the current collections in db
- */
- list<string> getCollectionNames( const string& db );
-
- bool exists( const string& ns );
-
- /** Create an index if it does not already exist.
- ensureIndex calls are remembered so it is safe/fast to call this function many
- times in your code.
- @param ns collection to be indexed
- @param keys the "key pattern" for the index. e.g., { name : 1 }
- @param unique if true, indicates that key uniqueness should be enforced for this index
- @param name if not specified, it will be created from the keys automatically (which is recommended)
- @param cache if set to false, the index cache for the connection won't remember this call
- @param background build index in the background (see mongodb docs/wiki for details)
- @param v index version. leave at default value. (unit tests set this parameter.)
- @return whether or not sent message to db.
- should be true on first call, false on subsequent unless resetIndexCache was called
- */
- virtual bool ensureIndex( const string &ns , BSONObj keys , bool unique = false, const string &name = "",
- bool cache = true, bool background = false, int v = -1 );
-
- /**
- clears the index cache, so the subsequent call to ensureIndex for any index will go to the server
- */
- virtual void resetIndexCache();
-
- virtual auto_ptr<DBClientCursor> getIndexes( const string &ns );
-
- virtual void dropIndex( const string& ns , BSONObj keys );
- virtual void dropIndex( const string& ns , const string& indexName );
-
- /**
- drops all indexes for the collection
- */
- virtual void dropIndexes( const string& ns );
-
- virtual void reIndex( const string& ns );
-
- string genIndexName( const BSONObj& keys );
-
- /** Erase / drop an entire database */
- virtual bool dropDatabase(const string &dbname, BSONObj *info = 0) {
- bool ret = simpleCommand(dbname, info, "dropDatabase");
- resetIndexCache();
- return ret;
- }
-
- virtual string toString() = 0;
-
- /** @return the database name portion of an ns string */
- string nsGetDB( const string &ns ) {
- string::size_type pos = ns.find( "." );
- if ( pos == string::npos )
- return ns;
-
- return ns.substr( 0 , pos );
- }
-
- /** @return the collection name portion of an ns string */
- string nsGetCollection( const string &ns ) {
- string::size_type pos = ns.find( "." );
- if ( pos == string::npos )
- return "";
-
- return ns.substr( pos + 1 );
- }
-
- protected:
- /** if the result of a command is ok*/
- bool isOk(const BSONObj&);
-
- /** if the element contains a not master error */
- bool isNotMasterErrorString( const BSONElement& e );
-
- BSONObj _countCmd(const string &ns, const BSONObj& query, int options, int limit, int skip );
-
- enum QueryOptions availableOptions();
-
- private:
- enum QueryOptions _cachedAvailableOptions;
- bool _haveCachedAvailableOptions;
- };
-
- /**
- abstract class that implements the core db operations
- */
- class DBClientBase : public DBClientWithCommands, public DBConnector {
- protected:
- WriteConcern _writeConcern;
-
- public:
- DBClientBase() {
- _writeConcern = W_NORMAL;
- }
-
- WriteConcern getWriteConcern() const { return _writeConcern; }
- void setWriteConcern( WriteConcern w ) { _writeConcern = w; }
-
- /** send a query to the database.
- @param ns namespace to query, format is <dbname>.<collectname>[.<collectname>]*
- @param query query to perform on the collection. this is a BSONObj (binary JSON)
- You may format as
- { query: { ... }, orderby: { ... } }
- to specify a sort order.
- @param nToReturn n to return (i.e., limit). 0 = unlimited
- @param nToSkip start with the nth item
- @param fieldsToReturn optional template of which fields to select. if unspecified, returns all fields
- @param queryOptions see options enum at top of this file
-
- @return cursor. 0 if error (connection failure)
- @throws AssertionException
- */
- virtual auto_ptr<DBClientCursor> query(const string &ns, Query query, int nToReturn = 0, int nToSkip = 0,
- const BSONObj *fieldsToReturn = 0, int queryOptions = 0 , int batchSize = 0 );
-
- /** don't use this - called automatically by DBClientCursor for you
- @param cursorId id of cursor to retrieve
- @return an handle to a previously allocated cursor
- @throws AssertionException
- */
- virtual auto_ptr<DBClientCursor> getMore( const string &ns, long long cursorId, int nToReturn = 0, int options = 0 );
-
- /**
- insert an object into the database
- */
- virtual void insert( const string &ns , BSONObj obj , int flags=0);
-
- /**
- insert a vector of objects into the database
- */
- virtual void insert( const string &ns, const vector< BSONObj >& v , int flags=0);
-
- /**
- remove matching objects from the database
- @param justOne if this true, then once a single match is found will stop
- */
- virtual void remove( const string &ns , Query q , bool justOne = 0 );
-
- /**
- updates objects matching query
- */
- virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = false , bool multi = false );
-
- virtual bool isFailed() const = 0;
-
- virtual void killCursor( long long cursorID ) = 0;
-
- virtual bool callRead( Message& toSend , Message& response ) = 0;
- // virtual bool callWrite( Message& toSend , Message& response ) = 0; // TODO: add this if needed
-
- virtual ConnectionString::ConnectionType type() const = 0;
-
- virtual double getSoTimeout() const = 0;
-
- }; // DBClientBase
-
- class DBClientReplicaSet;
-
- class ConnectException : public UserException {
- public:
- ConnectException(string msg) : UserException(9000,msg) { }
- };
-
- /**
- A basic connection to the database.
- This is the main entry point for talking to a simple Mongo setup
- */
- class DBClientConnection : public DBClientBase {
- public:
- /**
- @param _autoReconnect if true, automatically reconnect on a connection failure
- @param cp used by DBClientReplicaSet. You do not need to specify this parameter
- @param timeout tcp timeout in seconds - this is for read/write, not connect.
- Connect timeout is fixed, but short, at 5 seconds.
- */
- DBClientConnection(bool _autoReconnect=false, DBClientReplicaSet* cp=0, double so_timeout=0) :
- clientSet(cp), _failed(false), autoReconnect(_autoReconnect), lastReconnectTry(0), _so_timeout(so_timeout) {
- _numConnections++;
- }
-
- virtual ~DBClientConnection() {
- _numConnections--;
- }
-
- /** Connect to a Mongo database server.
-
- If autoReconnect is true, you can try to use the DBClientConnection even when
- false was returned -- it will try to connect again.
-
- @param serverHostname host to connect to. can include port number ( 127.0.0.1 , 127.0.0.1:5555 )
- If you use IPv6 you must add a port number ( ::1:27017 )
- @param errmsg any relevant error message will appended to the string
- @deprecated please use HostAndPort
- @return false if fails to connect.
- */
- virtual bool connect(const char * hostname, string& errmsg) {
- // TODO: remove this method
- HostAndPort t( hostname );
- return connect( t , errmsg );
- }
-
- /** Connect to a Mongo database server.
-
- If autoReconnect is true, you can try to use the DBClientConnection even when
- false was returned -- it will try to connect again.
-
- @param server server to connect to.
- @param errmsg any relevant error message will appended to the string
- @return false if fails to connect.
- */
- virtual bool connect(const HostAndPort& server, string& errmsg);
-
- /** Connect to a Mongo database server. Exception throwing version.
- Throws a UserException if cannot connect.
-
- If autoReconnect is true, you can try to use the DBClientConnection even when
- false was returned -- it will try to connect again.
-
- @param serverHostname host to connect to. can include port number ( 127.0.0.1 , 127.0.0.1:5555 )
- */
- void connect(const string& serverHostname) {
- string errmsg;
- if( !connect(HostAndPort(serverHostname), errmsg) )
- throw ConnectException(string("can't connect ") + errmsg);
- }
-
- virtual bool auth(const string &dbname, const string &username, const string &pwd, string& errmsg, bool digestPassword = true);
-
- virtual auto_ptr<DBClientCursor> query(const string &ns, Query query=Query(), int nToReturn = 0, int nToSkip = 0,
- const BSONObj *fieldsToReturn = 0, int queryOptions = 0 , int batchSize = 0 ) {
- checkConnection();
- return DBClientBase::query( ns, query, nToReturn, nToSkip, fieldsToReturn, queryOptions , batchSize );
- }
-
- /** Uses QueryOption_Exhaust
- Exhaust mode sends back all data queries as fast as possible, with no back-and-for for OP_GETMORE. If you are certain
- you will exhaust the query, it could be useful.
-
- Use DBClientCursorBatchIterator version if you want to do items in large blocks, perhaps to avoid granular locking and such.
- */
- unsigned long long query( boost::function<void(const BSONObj&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
- unsigned long long query( boost::function<void(DBClientCursorBatchIterator&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
-
- virtual bool runCommand(const string &dbname, const BSONObj& cmd, BSONObj &info, int options=0);
-
- /**
- @return true if this connection is currently in a failed state. When autoreconnect is on,
- a connection will transition back to an ok state after reconnecting.
- */
- bool isFailed() const { return _failed; }
-
- MessagingPort& port() { assert(p); return *p; }
-
- string toStringLong() const {
- stringstream ss;
- ss << _serverString;
- if ( _failed ) ss << " failed";
- return ss.str();
- }
-
- /** Returns the address of the server */
- string toString() { return _serverString; }
-
- string getServerAddress() const { return _serverString; }
-
- virtual void killCursor( long long cursorID );
- virtual bool callRead( Message& toSend , Message& response ) { return call( toSend , response ); }
- virtual void say( Message &toSend, bool isRetry = false );
- virtual bool recv( Message& m );
- virtual void checkResponse( const char *data, int nReturned, bool* retry = NULL, string* host = NULL );
- virtual bool call( Message &toSend, Message &response, bool assertOk = true , string * actualServer = 0 );
- virtual ConnectionString::ConnectionType type() const { return ConnectionString::MASTER; }
- void setSoTimeout(double to) { _so_timeout = to; }
- double getSoTimeout() const { return _so_timeout; }
-
- virtual bool lazySupported() const { return true; }
-
- static int getNumConnections() {
- return _numConnections;
- }
-
- static void setLazyKillCursor( bool lazy ) { _lazyKillCursor = lazy; }
- static bool getLazyKillCursor() { return _lazyKillCursor; }
-
- protected:
- friend class SyncClusterConnection;
- virtual void sayPiggyBack( Message &toSend );
-
- DBClientReplicaSet *clientSet;
- boost::scoped_ptr<MessagingPort> p;
- boost::scoped_ptr<SockAddr> server;
- bool _failed;
- const bool autoReconnect;
- time_t lastReconnectTry;
- HostAndPort _server; // remember for reconnects
- string _serverString;
- void _checkConnection();
-
- // throws SocketException if in failed state and not reconnecting or if waiting to reconnect
- void checkConnection() { if( _failed ) _checkConnection(); }
-
- map< string, pair<string,string> > authCache;
- double _so_timeout;
- bool _connect( string& errmsg );
-
- static AtomicUInt _numConnections;
- static bool _lazyKillCursor; // lazy means we piggy back kill cursors on next op
-
-#ifdef MONGO_SSL
- static SSLManager* sslManager();
- static SSLManager* _sslManager;
-#endif
- };
-
- /** pings server to check if it's up
- */
- bool serverAlive( const string &uri );
-
- DBClientBase * createDirectClient();
-
- BSONElement getErrField( const BSONObj& result );
- bool hasErrField( const BSONObj& result );
-
-} // namespace mongo
-
-#include "dbclientcursor.h"
-#include "dbclient_rs.h"
-#include "undef_macros.h"
diff --git a/client/dbclient_rs.cpp b/client/dbclient_rs.cpp
deleted file mode 100644
index be66bb451c4..00000000000
--- a/client/dbclient_rs.cpp
+++ /dev/null
@@ -1,1097 +0,0 @@
-// dbclient.cpp - connect to a Mongo database as a database, from C++
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "pch.h"
-#include "dbclient.h"
-#include "../bson/util/builder.h"
-#include "../db/jsobj.h"
-#include "../db/json.h"
-#include "../db/dbmessage.h"
-#include "connpool.h"
-#include "dbclient_rs.h"
-#include "../util/background.h"
-
-namespace mongo {
-
- // --------------------------------
- // ----- ReplicaSetMonitor ---------
- // --------------------------------
-
- // global background job responsible for checking every X amount of time
- class ReplicaSetMonitorWatcher : public BackgroundJob {
- public:
- ReplicaSetMonitorWatcher() : _safego("ReplicaSetMonitorWatcher::_safego") , _started(false) {}
-
- virtual string name() const { return "ReplicaSetMonitorWatcher"; }
-
- void safeGo() {
- // check outside of lock for speed
- if ( _started )
- return;
-
- scoped_lock lk( _safego );
- if ( _started )
- return;
- _started = true;
-
- go();
- }
- protected:
- void run() {
- log() << "starting" << endl;
- while ( ! inShutdown() ) {
- sleepsecs( 10 );
- try {
- ReplicaSetMonitor::checkAll( true );
- }
- catch ( std::exception& e ) {
- error() << "check failed: " << e.what() << endl;
- }
- catch ( ... ) {
- error() << "unkown error" << endl;
- }
- }
- }
-
- mongo::mutex _safego;
- bool _started;
-
- } replicaSetMonitorWatcher;
-
- string seedString( const vector<HostAndPort>& servers ){
- string seedStr;
- for ( unsigned i = 0; i < servers.size(); i++ ){
- seedStr += servers[i].toString();
- if( i < servers.size() - 1 ) seedStr += ",";
- }
-
- return seedStr;
- }
-
- ReplicaSetMonitor::ReplicaSetMonitor( const string& name , const vector<HostAndPort>& servers )
- : _lock( "ReplicaSetMonitor instance" ) , _checkConnectionLock( "ReplicaSetMonitor check connection lock" ), _name( name ) , _master(-1), _nextSlave(0) {
-
- uassert( 13642 , "need at least 1 node for a replica set" , servers.size() > 0 );
-
- if ( _name.size() == 0 ) {
- warning() << "replica set name empty, first node: " << servers[0] << endl;
- }
-
- log() << "starting new replica set monitor for replica set " << _name << " with seed of " << seedString( servers ) << endl;
-
- string errmsg;
- for ( unsigned i = 0; i < servers.size(); i++ ) {
-
- // Don't check servers we have already
- if( _find_inlock( servers[i] ) >= 0 ) continue;
-
- auto_ptr<DBClientConnection> conn( new DBClientConnection( true , 0, 5.0 ) );
- try{
- if( ! conn->connect( servers[i] , errmsg ) ){
- throw DBException( errmsg, 15928 );
- }
- log() << "successfully connected to seed " << servers[i] << " for replica set " << this->_name << endl;
- }
- catch( DBException& e ){
- log() << "error connecting to seed " << servers[i] << causedBy( e ) << endl;
- // skip seeds that don't work
- continue;
- }
-
- string maybePrimary;
- _checkConnection( conn.get(), maybePrimary, false, -1 );
- }
-
- // Check everything to get the first data
- _check( true );
-
- log() << "replica set monitor for replica set " << _name << " started, address is " << getServerAddress() << endl;
-
- }
-
- ReplicaSetMonitor::~ReplicaSetMonitor() {
- _nodes.clear();
- _master = -1;
- }
-
- ReplicaSetMonitorPtr ReplicaSetMonitor::get( const string& name , const vector<HostAndPort>& servers ) {
- scoped_lock lk( _setsLock );
- ReplicaSetMonitorPtr& m = _sets[name];
- if ( ! m )
- m.reset( new ReplicaSetMonitor( name , servers ) );
-
- replicaSetMonitorWatcher.safeGo();
-
- return m;
- }
-
- ReplicaSetMonitorPtr ReplicaSetMonitor::get( const string& name ) {
- scoped_lock lk( _setsLock );
- map<string,ReplicaSetMonitorPtr>::const_iterator i = _sets.find( name );
- if ( i == _sets.end() )
- return ReplicaSetMonitorPtr();
- return i->second;
- }
-
-
- void ReplicaSetMonitor::checkAll( bool checkAllSecondaries ) {
- set<string> seen;
-
- while ( true ) {
- ReplicaSetMonitorPtr m;
- {
- scoped_lock lk( _setsLock );
- for ( map<string,ReplicaSetMonitorPtr>::iterator i=_sets.begin(); i!=_sets.end(); ++i ) {
- string name = i->first;
- if ( seen.count( name ) )
- continue;
- LOG(1) << "checking replica set: " << name << endl;
- seen.insert( name );
- m = i->second;
- break;
- }
- }
-
- if ( ! m )
- break;
-
- m->check( checkAllSecondaries );
- }
-
-
- }
-
- void ReplicaSetMonitor::setConfigChangeHook( ConfigChangeHook hook ) {
- massert( 13610 , "ConfigChangeHook already specified" , _hook == 0 );
- _hook = hook;
- }
-
- string ReplicaSetMonitor::getServerAddress() const {
- scoped_lock lk( _lock );
- return _getServerAddress_inlock();
- }
-
- string ReplicaSetMonitor::_getServerAddress_inlock() const {
- StringBuilder ss;
- if ( _name.size() )
- ss << _name << "/";
-
- for ( unsigned i=0; i<_nodes.size(); i++ ) {
- if ( i > 0 )
- ss << ",";
- ss << _nodes[i].addr.toString();
- }
-
- return ss.str();
- }
-
- bool ReplicaSetMonitor::contains( const string& server ) const {
- scoped_lock lk( _lock );
- for ( unsigned i=0; i<_nodes.size(); i++ ) {
- if ( _nodes[i].addr == server )
- return true;
- }
- return false;
- }
-
-
- void ReplicaSetMonitor::notifyFailure( const HostAndPort& server ) {
- scoped_lock lk( _lock );
-
- if ( _master >= 0 && _master < (int)_nodes.size() ) {
- if ( server == _nodes[_master].addr ) {
- _nodes[_master].ok = false;
- _master = -1;
- }
- }
- }
-
-
-
- HostAndPort ReplicaSetMonitor::getMaster() {
- {
- scoped_lock lk( _lock );
- assert(_master < static_cast<int>(_nodes.size()));
- if ( _master >= 0 && _nodes[_master].ok )
- return _nodes[_master].addr;
- }
-
- _check( false );
-
- scoped_lock lk( _lock );
- uassert( 10009 , str::stream() << "ReplicaSetMonitor no master found for set: " << _name , _master >= 0 );
- assert(_master < static_cast<int>(_nodes.size()));
- return _nodes[_master].addr;
- }
-
- HostAndPort ReplicaSetMonitor::getSlave( const HostAndPort& prev ) {
- // make sure its valid
-
- bool wasFound = false;
-
- // This is always true, since checked in port()
- assert( prev.port() >= 0 );
- if( prev.host().size() ){
- scoped_lock lk( _lock );
- for ( unsigned i=0; i<_nodes.size(); i++ ) {
- if ( prev != _nodes[i].addr )
- continue;
-
- wasFound = true;
-
- if ( _nodes[i].okForSecondaryQueries() )
- return prev;
-
- break;
- }
- }
-
- if( prev.host().size() ){
- if( wasFound ){ LOG(1) << "slave '" << prev << "' is no longer ok to use" << endl; }
- else{ LOG(1) << "slave '" << prev << "' was not found in the replica set" << endl; }
- }
- else LOG(1) << "slave '" << prev << "' is not initialized or invalid" << endl;
-
- return getSlave();
- }
-
- HostAndPort ReplicaSetMonitor::getSlave() {
- LOG(2) << "dbclient_rs getSlave " << getServerAddress() << endl;
-
- scoped_lock lk( _lock );
-
- for ( unsigned ii = 0; ii < _nodes.size(); ii++ ) {
- _nextSlave = ( _nextSlave + 1 ) % _nodes.size();
- if ( _nextSlave != _master ) {
- if ( _nodes[ _nextSlave ].okForSecondaryQueries() )
- return _nodes[ _nextSlave ].addr;
- LOG(2) << "dbclient_rs getSlave not selecting " << _nodes[_nextSlave] << ", not currently okForSecondaryQueries" << endl;
- }
- }
-
- if( _master >= 0 ) {
- assert( static_cast<unsigned>(_master) < _nodes.size() );
- LOG(2) << "dbclient_rs getSlave no member in secondary state found, returning primary " << _nodes[ _master ] << endl;
- return _nodes[_master].addr;
- }
-
- LOG(2) << "dbclient_rs getSlave no suitable member found, returning first node " << _nodes[ 0 ] << endl;
- assert( _nodes.size() > 0 );
- return _nodes[0].addr;
- }
-
- /**
- * notify the monitor that server has failed
- */
- void ReplicaSetMonitor::notifySlaveFailure( const HostAndPort& server ) {
- int x = _find( server );
- if ( x >= 0 ) {
- scoped_lock lk( _lock );
- _nodes[x].ok = false;
- }
- }
-
- void ReplicaSetMonitor::_checkStatus( const string& hostAddr ) {
- BSONObj status;
-
- /* replSetGetStatus requires admin auth so use a connection from the pool,
- * which are authenticated with the keyFile credentials.
- */
- ScopedDbConnection authenticatedConn( hostAddr );
-
- if ( !authenticatedConn->runCommand( "admin", BSON( "replSetGetStatus" << 1 ), status )) {
- LOG(1) << "dbclient_rs replSetGetStatus failed" << endl;
- authenticatedConn.done(); // connection worked properly, but we got an error from server
- return;
- }
-
- // Make sure we return when finished
- authenticatedConn.done();
-
- if( !status.hasField("members") ) {
- log() << "dbclient_rs error expected members field in replSetGetStatus result" << endl;
- return;
- }
- if( status["members"].type() != Array) {
- log() << "dbclient_rs error expected members field in replSetGetStatus result to be an array" << endl;
- return;
- }
-
- BSONObjIterator hi(status["members"].Obj());
- while (hi.more()) {
- BSONObj member = hi.next().Obj();
- string host = member["name"].String();
-
- int m = -1;
- if ((m = _find(host)) < 0) {
- continue;
- }
-
- double state = member["state"].Number();
- if (member["health"].Number() == 1 && (state == 1 || state == 2)) {
- scoped_lock lk( _lock );
- _nodes[m].ok = true;
- }
- else {
- scoped_lock lk( _lock );
- _nodes[m].ok = false;
- }
- }
- }
-
- NodeDiff ReplicaSetMonitor::_getHostDiff_inlock( const BSONObj& hostList ){
-
- NodeDiff diff;
- set<int> nodesFound;
-
- int index = 0;
- BSONObjIterator hi( hostList );
- while( hi.more() ){
-
- string toCheck = hi.next().String();
- int nodeIndex = _find_inlock( toCheck );
-
- // Node-to-add
- if( nodeIndex < 0 ) diff.first.insert( toCheck );
- else nodesFound.insert( nodeIndex );
-
- index++;
- }
-
- for( size_t i = 0; i < _nodes.size(); i++ ){
- if( nodesFound.find( static_cast<int>(i) ) == nodesFound.end() ) diff.second.insert( static_cast<int>(i) );
- }
-
- return diff;
- }
-
- bool ReplicaSetMonitor::_shouldChangeHosts( const BSONObj& hostList, bool inlock ){
-
- int origHosts = 0;
- if( ! inlock ){
- scoped_lock lk( _lock );
- origHosts = _nodes.size();
- }
- else origHosts = _nodes.size();
- int numHosts = 0;
- bool changed = false;
-
- BSONObjIterator hi(hostList);
- while ( hi.more() ) {
- string toCheck = hi.next().String();
-
- numHosts++;
- int index = 0;
- if( ! inlock ) index = _find( toCheck );
- else index = _find_inlock( toCheck );
-
- if ( index >= 0 ) continue;
-
- changed = true;
- break;
- }
-
- return changed || origHosts != numHosts;
-
- }
-
- void ReplicaSetMonitor::_checkHosts( const BSONObj& hostList, bool& changed ) {
-
- // Fast path, still requires intermittent locking
- if( ! _shouldChangeHosts( hostList, false ) ){
- changed = false;
- return;
- }
-
- // Slow path, double-checked though
- scoped_lock lk( _lock );
-
- // Our host list may have changed while waiting for another thread in the meantime,
- // so double-check here
- // TODO: Do we really need this much protection, this should be pretty rare and not
- // triggered from lots of threads, duping old behavior for safety
- if( ! _shouldChangeHosts( hostList, true ) ){
- changed = false;
- return;
- }
-
- // LogLevel can be pretty low, since replica set reconfiguration should be pretty rare and
- // we want to record our changes
- log() << "changing hosts to " << hostList << " from " << _getServerAddress_inlock() << endl;
-
- NodeDiff diff = _getHostDiff_inlock( hostList );
- set<string> added = diff.first;
- set<int> removed = diff.second;
-
- assert( added.size() > 0 || removed.size() > 0 );
- changed = true;
-
- // Delete from the end so we don't invalidate as we delete, delete indices are ascending
- for( set<int>::reverse_iterator i = removed.rbegin(), end = removed.rend(); i != end; ++i ){
-
- log() << "erasing host " << _nodes[ *i ] << " from replica set " << this->_name << endl;
- _nodes.erase( _nodes.begin() + *i );
- }
-
- // Add new nodes
- for( set<string>::iterator i = added.begin(), end = added.end(); i != end; ++i ){
-
- log() << "trying to add new host " << *i << " to replica set " << this->_name << endl;
-
- // Connect to new node
- HostAndPort h( *i );
- DBClientConnection * newConn = new DBClientConnection( true, 0, 5.0 );
-
- string errmsg;
- try{
- if( ! newConn->connect( h , errmsg ) ){
- throw DBException( errmsg, 15927 );
- }
- log() << "successfully connected to new host " << *i << " in replica set " << this->_name << endl;
- }
- catch( DBException& e ){
- warning() << "cannot connect to new host " << *i << " to replica set " << this->_name << causedBy( e ) << endl;
- }
-
- _nodes.push_back( Node( h , newConn ) );
- }
-
- // Invalidate the cached _master index since the _nodes structure has
- // already been modified.
- _master = -1;
- }
-
-
- bool ReplicaSetMonitor::_checkConnection( DBClientConnection* conn,
- string& maybePrimary, bool verbose, int nodesOffset ) {
-
- assert( conn );
-
- scoped_lock lk( _checkConnectionLock );
- bool isMaster = false;
- bool changed = false;
- bool errorOccured = false;
-
- if ( nodesOffset >= 0 ){
- scoped_lock lk( _lock );
- if ( !_checkConnMatch_inlock( conn, nodesOffset )) {
- /* Another thread modified _nodes -> invariant broken.
- * This also implies that another thread just passed
- * through here and refreshed _nodes. So no need to do
- * duplicate work.
- */
- return false;
- }
- }
-
- try {
- Timer t;
- BSONObj o;
- conn->isMaster( isMaster, &o );
-
- if ( o["setName"].type() != String || o["setName"].String() != _name ) {
- warning() << "node: " << conn->getServerAddress()
- << " isn't a part of set: " << _name
- << " ismaster: " << o << endl;
-
- if ( nodesOffset >= 0 ) {
- scoped_lock lk( _lock );
- _nodes[nodesOffset].ok = false;
- }
-
- return false;
- }
-
- if ( nodesOffset >= 0 ) {
- scoped_lock lk( _lock );
-
- _nodes[nodesOffset].pingTimeMillis = t.millis();
- _nodes[nodesOffset].hidden = o["hidden"].trueValue();
- _nodes[nodesOffset].secondary = o["secondary"].trueValue();
- _nodes[nodesOffset].ismaster = o["ismaster"].trueValue();
-
- _nodes[nodesOffset].lastIsMaster = o.copy();
- }
-
- log( ! verbose ) << "ReplicaSetMonitor::_checkConnection: " << conn->toString()
- << ' ' << o << endl;
-
- // add other nodes
- BSONArrayBuilder b;
- if ( o["hosts"].type() == Array ) {
- if ( o["primary"].type() == String )
- maybePrimary = o["primary"].String();
-
- BSONObjIterator it( o["hosts"].Obj() );
- while( it.more() ) b.append( it.next() );
- }
-
- if (o.hasField("passives") && o["passives"].type() == Array) {
- BSONObjIterator it( o["passives"].Obj() );
- while( it.more() ) b.append( it.next() );
- }
-
- _checkHosts( b.arr(), changed);
- _checkStatus( conn->getServerAddress() );
-
- }
- catch ( std::exception& e ) {
- log( ! verbose ) << "ReplicaSetMonitor::_checkConnection: caught exception "
- << conn->toString() << ' ' << e.what() << endl;
-
- errorOccured = true;
- }
-
- if ( errorOccured && nodesOffset >= 0 ) {
- scoped_lock lk( _lock );
-
- if (_checkConnMatch_inlock(conn, nodesOffset)) {
- // Make sure _checkHosts didn't modify the _nodes structure
- _nodes[nodesOffset].ok = false;
- }
- }
-
- if ( changed && _hook )
- _hook( this );
-
- return isMaster;
- }
-
- void ReplicaSetMonitor::_check( bool checkAllSecondaries ) {
- LOG(1) << "_check : " << getServerAddress() << endl;
-
- int newMaster = -1;
- shared_ptr<DBClientConnection> nodeConn;
-
- for ( int retry = 0; retry < 2; retry++ ) {
- bool triedQuickCheck = false;
-
- if ( !checkAllSecondaries ) {
- scoped_lock lk( _lock );
- assert(_master < static_cast<int>(_nodes.size()));
- if ( _master >= 0 ) {
- /* Nothing else to do since another thread already
- * found the _master
- */
- return;
- }
- }
-
- for ( unsigned i = 0; /* should not check while outside of lock! */ ; i++ ) {
- {
- scoped_lock lk( _lock );
- if ( i >= _nodes.size() ) break;
- nodeConn = _nodes[i].conn;
- }
-
- string maybePrimary;
- if ( _checkConnection( nodeConn.get(), maybePrimary, retry, i ) ) {
- scoped_lock lk( _lock );
- if ( _checkConnMatch_inlock( nodeConn.get(), i )) {
- _master = i;
- newMaster = i;
-
- if ( !checkAllSecondaries )
- return;
- }
- else {
- /*
- * Somebody modified _nodes and most likely set the new
- * _master, so try again.
- */
- break;
- }
- }
-
-
- if ( ! triedQuickCheck && ! maybePrimary.empty() ) {
- int probablePrimaryIdx = -1;
- shared_ptr<DBClientConnection> probablePrimaryConn;
-
- {
- scoped_lock lk( _lock );
- probablePrimaryIdx = _find_inlock( maybePrimary );
- probablePrimaryConn = _nodes[probablePrimaryIdx].conn;
- }
-
- if ( probablePrimaryIdx >= 0 ) {
- triedQuickCheck = true;
-
- string dummy;
- if ( _checkConnection( probablePrimaryConn.get(), dummy,
- false, probablePrimaryIdx ) ) {
-
- scoped_lock lk( _lock );
-
- if ( _checkConnMatch_inlock( probablePrimaryConn.get(),
- probablePrimaryIdx )) {
-
- _master = probablePrimaryIdx;
- newMaster = probablePrimaryIdx;
-
- if ( ! checkAllSecondaries )
- return;
- }
- else {
- /*
- * Somebody modified _nodes and most likely set the
- * new _master, so try again.
- */
- break;
- }
- }
- }
- }
- }
-
- if ( newMaster >= 0 )
- return;
-
- sleepsecs( 1 );
- }
- }
-
- void ReplicaSetMonitor::check( bool checkAllSecondaries ) {
- shared_ptr<DBClientConnection> masterConn;
-
- {
- scoped_lock lk( _lock );
-
- // first see if the current master is fine
- if ( _master >= 0 ) {
- assert(_master < static_cast<int>(_nodes.size()));
- masterConn = _nodes[_master].conn;
- }
- }
-
- if ( masterConn.get() != NULL ) {
- string temp;
-
- if ( _checkConnection( masterConn.get(), temp, false, _master )) {
- if ( ! checkAllSecondaries ) {
- // current master is fine, so we're done
- return;
- }
- }
- }
-
- // we either have no master, or the current is dead
- _check( checkAllSecondaries );
- }
-
- int ReplicaSetMonitor::_find( const string& server ) const {
- scoped_lock lk( _lock );
- return _find_inlock( server );
- }
-
- int ReplicaSetMonitor::_find_inlock( const string& server ) const {
- const size_t size = _nodes.size();
-
- for ( unsigned i = 0; i < size; i++ ) {
- if ( _nodes[i].addr == server ) {
- return i;
- }
- }
-
- return -1;
- }
-
- void ReplicaSetMonitor::appendInfo( BSONObjBuilder& b ) const {
- scoped_lock lk( _lock );
- BSONArrayBuilder hosts( b.subarrayStart( "hosts" ) );
- for ( unsigned i=0; i<_nodes.size(); i++ ) {
- hosts.append( BSON( "addr" << _nodes[i].addr <<
- // "lastIsMaster" << _nodes[i].lastIsMaster << // this is a potential race, so only used when debugging
- "ok" << _nodes[i].ok <<
- "ismaster" << _nodes[i].ismaster <<
- "hidden" << _nodes[i].hidden <<
- "secondary" << _nodes[i].secondary <<
- "pingTimeMillis" << _nodes[i].pingTimeMillis ) );
-
- }
- hosts.done();
-
- b.append( "master" , _master );
- b.append( "nextSlave" , _nextSlave );
- }
-
- bool ReplicaSetMonitor::_checkConnMatch_inlock( DBClientConnection* conn,
- size_t nodeOffset ) const {
-
- return ( nodeOffset < _nodes.size() &&
- conn->getServerAddress() == _nodes[nodeOffset].conn->getServerAddress() );
- }
-
-
- mongo::mutex ReplicaSetMonitor::_setsLock( "ReplicaSetMonitor" );
- map<string,ReplicaSetMonitorPtr> ReplicaSetMonitor::_sets;
- ReplicaSetMonitor::ConfigChangeHook ReplicaSetMonitor::_hook;
- // --------------------------------
- // ----- DBClientReplicaSet ---------
- // --------------------------------
-
- DBClientReplicaSet::DBClientReplicaSet( const string& name , const vector<HostAndPort>& servers, double so_timeout )
- : _monitor( ReplicaSetMonitor::get( name , servers ) ),
- _so_timeout( so_timeout ) {
- }
-
- DBClientReplicaSet::~DBClientReplicaSet() {
- }
-
- DBClientConnection * DBClientReplicaSet::checkMaster() {
- HostAndPort h = _monitor->getMaster();
-
- if ( h == _masterHost && _master ) {
- // a master is selected. let's just make sure connection didn't die
- if ( ! _master->isFailed() )
- return _master.get();
-
- _monitor->notifyFailure( _masterHost );
- }
-
- _masterHost = _monitor->getMaster();
- _master.reset( new DBClientConnection( true , this , _so_timeout ) );
- string errmsg;
- if ( ! _master->connect( _masterHost , errmsg ) ) {
- _monitor->notifyFailure( _masterHost );
- uasserted( 13639 , str::stream() << "can't connect to new replica set master [" << _masterHost.toString() << "] err: " << errmsg );
- }
- _auth( _master.get() );
- return _master.get();
- }
-
- DBClientConnection * DBClientReplicaSet::checkSlave() {
- HostAndPort h = _monitor->getSlave( _slaveHost );
-
- if ( h == _slaveHost && _slave ) {
- if ( ! _slave->isFailed() )
- return _slave.get();
- _monitor->notifySlaveFailure( _slaveHost );
- _slaveHost = _monitor->getSlave();
- }
- else {
- _slaveHost = h;
- }
-
- _slave.reset( new DBClientConnection( true , this , _so_timeout ) );
- _slave->connect( _slaveHost );
- _auth( _slave.get() );
- return _slave.get();
- }
-
-
- void DBClientReplicaSet::_auth( DBClientConnection * conn ) {
- for ( list<AuthInfo>::iterator i=_auths.begin(); i!=_auths.end(); ++i ) {
- const AuthInfo& a = *i;
- string errmsg;
- if ( ! conn->auth( a.dbname , a.username , a.pwd , errmsg, a.digestPassword ) )
- warning() << "cached auth failed for set: " << _monitor->getName() << " db: " << a.dbname << " user: " << a.username << endl;
-
- }
- }
-
- DBClientConnection& DBClientReplicaSet::masterConn() {
- return *checkMaster();
- }
-
- DBClientConnection& DBClientReplicaSet::slaveConn() {
- return *checkSlave();
- }
-
- bool DBClientReplicaSet::connect() {
- try {
- checkMaster();
- }
- catch (AssertionException&) {
- if (_master && _monitor) {
- _monitor->notifyFailure(_masterHost);
- }
- return false;
- }
- return true;
- }
-
- bool DBClientReplicaSet::auth(const string &dbname, const string &username, const string &pwd, string& errmsg, bool digestPassword ) {
- DBClientConnection * m = checkMaster();
-
- // first make sure it actually works
- if( ! m->auth(dbname, username, pwd, errmsg, digestPassword ) )
- return false;
-
- // now that it does, we should save so that for a new node we can auth
- _auths.push_back( AuthInfo( dbname , username , pwd , digestPassword ) );
- return true;
- }
-
- // ------------- simple functions -----------------
-
- void DBClientReplicaSet::insert( const string &ns , BSONObj obj , int flags) {
- checkMaster()->insert(ns, obj, flags);
- }
-
- void DBClientReplicaSet::insert( const string &ns, const vector< BSONObj >& v , int flags) {
- checkMaster()->insert(ns, v, flags);
- }
-
- void DBClientReplicaSet::remove( const string &ns , Query obj , bool justOne ) {
- checkMaster()->remove(ns, obj, justOne);
- }
-
- void DBClientReplicaSet::update( const string &ns , Query query , BSONObj obj , bool upsert , bool multi ) {
- return checkMaster()->update(ns, query, obj, upsert,multi);
- }
-
- auto_ptr<DBClientCursor> DBClientReplicaSet::query(const string &ns, Query query, int nToReturn, int nToSkip,
- const BSONObj *fieldsToReturn, int queryOptions, int batchSize) {
-
- if ( queryOptions & QueryOption_SlaveOk ) {
- // we're ok sending to a slave
- // we'll try 2 slaves before just using master
- // checkSlave will try a different slave automatically after a failure
- for ( int i=0; i<3; i++ ) {
- try {
- return checkSlaveQueryResult( checkSlave()->query(ns,query,nToReturn,nToSkip,fieldsToReturn,queryOptions,batchSize) );
- }
- catch ( DBException &e ) {
- LOG(1) << "can't query replica set slave " << i << " : " << _slaveHost << causedBy( e ) << endl;
- }
- }
- }
-
- return checkMaster()->query(ns,query,nToReturn,nToSkip,fieldsToReturn,queryOptions,batchSize);
- }
-
- BSONObj DBClientReplicaSet::findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn, int queryOptions) {
- if ( queryOptions & QueryOption_SlaveOk ) {
- // we're ok sending to a slave
- // we'll try 2 slaves before just using master
- // checkSlave will try a different slave automatically after a failure
- for ( int i=0; i<3; i++ ) {
- try {
- return checkSlave()->findOne(ns,query,fieldsToReturn,queryOptions);
- }
- catch ( DBException &e ) {
- LOG(1) << "can't findone replica set slave " << i << " : " << _slaveHost << causedBy( e ) << endl;
- }
- }
- }
-
- return checkMaster()->findOne(ns,query,fieldsToReturn,queryOptions);
- }
-
- void DBClientReplicaSet::killCursor( long long cursorID ) {
- // we should neve call killCursor on a replica set conncetion
- // since we don't know which server it belongs to
- // can't assume master because of slave ok
- // and can have a cursor survive a master change
- assert(0);
- }
-
- void DBClientReplicaSet::isntMaster() {
- log() << "got not master for: " << _masterHost << endl;
- _monitor->notifyFailure( _masterHost );
- _master.reset();
- }
-
- auto_ptr<DBClientCursor> DBClientReplicaSet::checkSlaveQueryResult( auto_ptr<DBClientCursor> result ){
- if ( result.get() == NULL ) return result;
-
- BSONObj error;
- bool isError = result->peekError( &error );
- if( ! isError ) return result;
-
- // We only check for "not master or secondary" errors here
-
- // If the error code here ever changes, we need to change this code also
- BSONElement code = error["code"];
- if( code.isNumber() && code.Int() == 13436 /* not master or secondary */ ){
- isntSecondary();
- throw DBException( str::stream() << "slave " << _slaveHost.toString() << " is no longer secondary", 14812 );
- }
-
- return result;
- }
-
- void DBClientReplicaSet::isntSecondary() {
- log() << "slave no longer has secondary status: " << _slaveHost << endl;
- // Failover to next slave
- _monitor->notifySlaveFailure( _slaveHost );
- _slave.reset();
- }
-
- void DBClientReplicaSet::say( Message& toSend, bool isRetry ) {
-
- if( ! isRetry )
- _lazyState = LazyState();
-
- int lastOp = -1;
- bool slaveOk = false;
-
- if ( ( lastOp = toSend.operation() ) == dbQuery ) {
- // TODO: might be possible to do this faster by changing api
- DbMessage dm( toSend );
- QueryMessage qm( dm );
- if ( ( slaveOk = ( qm.queryOptions & QueryOption_SlaveOk ) ) ) {
-
- for ( int i = _lazyState._retries; i < 3; i++ ) {
- try {
- DBClientConnection* slave = checkSlave();
- slave->say( toSend );
-
- _lazyState._lastOp = lastOp;
- _lazyState._slaveOk = slaveOk;
- _lazyState._retries = i;
- _lazyState._lastClient = slave;
- return;
- }
- catch ( DBException &e ) {
- LOG(1) << "can't callLazy replica set slave " << i << " : " << _slaveHost << causedBy( e ) << endl;
- }
- }
- }
- }
-
- DBClientConnection* master = checkMaster();
- master->say( toSend );
-
- _lazyState._lastOp = lastOp;
- _lazyState._slaveOk = slaveOk;
- _lazyState._retries = 3;
- _lazyState._lastClient = master;
- return;
- }
-
- bool DBClientReplicaSet::recv( Message& m ) {
-
- assert( _lazyState._lastClient );
-
- // TODO: It would be nice if we could easily wrap a conn error as a result error
- try {
- return _lazyState._lastClient->recv( m );
- }
- catch( DBException& e ){
- log() << "could not receive data from " << _lazyState._lastClient << causedBy( e ) << endl;
- return false;
- }
- }
-
- void DBClientReplicaSet::checkResponse( const char* data, int nReturned, bool* retry, string* targetHost ){
-
- // For now, do exactly as we did before, so as not to break things. In general though, we
- // should fix this so checkResponse has a more consistent contract.
- if( ! retry ){
- if( _lazyState._lastClient )
- return _lazyState._lastClient->checkResponse( data, nReturned );
- else
- return checkMaster()->checkResponse( data, nReturned );
- }
-
- *retry = false;
- if( targetHost && _lazyState._lastClient ) *targetHost = _lazyState._lastClient->getServerAddress();
- else if (targetHost) *targetHost = "";
-
- if( ! _lazyState._lastClient ) return;
- if( nReturned != 1 && nReturned != -1 ) return;
-
- BSONObj dataObj;
- if( nReturned == 1 ) dataObj = BSONObj( data );
-
- // Check if we should retry here
- if( _lazyState._lastOp == dbQuery && _lazyState._slaveOk ){
-
- // Check the error code for a slave not secondary error
- if( nReturned == -1 ||
- ( hasErrField( dataObj ) && ! dataObj["code"].eoo() && dataObj["code"].Int() == 13436 ) ){
-
- bool wasMaster = false;
- if( _lazyState._lastClient == _slave.get() ){
- isntSecondary();
- }
- else if( _lazyState._lastClient == _master.get() ){
- wasMaster = true;
- isntMaster();
- }
- else
- warning() << "passed " << dataObj << " but last rs client " << _lazyState._lastClient->toString() << " is not master or secondary" << endl;
-
- if( _lazyState._retries < 3 ){
- _lazyState._retries++;
- *retry = true;
- }
- else{
- (void)wasMaster; // silence set-but-not-used warning
- // assert( wasMaster );
- // printStackTrace();
- log() << "too many retries (" << _lazyState._retries << "), could not get data from replica set" << endl;
- }
- }
- }
- }
-
-
- bool DBClientReplicaSet::call( Message &toSend, Message &response, bool assertOk , string * actualServer ) {
- const char * ns = 0;
-
- if ( toSend.operation() == dbQuery ) {
- // TODO: might be possible to do this faster by changing api
- DbMessage dm( toSend );
- QueryMessage qm( dm );
- ns = qm.ns;
-
- if ( qm.queryOptions & QueryOption_SlaveOk ) {
- for ( int i=0; i<3; i++ ) {
- try {
- DBClientConnection* s = checkSlave();
- if ( actualServer )
- *actualServer = s->getServerAddress();
- return s->call( toSend , response , assertOk );
- }
- catch ( DBException &e ) {
- LOG(1) << "can't call replica set slave " << i << " : " << _slaveHost << causedBy( e ) << endl;
- if ( actualServer )
- *actualServer = "";
- }
- }
- }
- }
-
- DBClientConnection* m = checkMaster();
- if ( actualServer )
- *actualServer = m->getServerAddress();
-
- if ( ! m->call( toSend , response , assertOk ) )
- return false;
-
- if ( ns ) {
- QueryResult * res = (QueryResult*)response.singleData();
- if ( res->nReturned == 1 ) {
- BSONObj x(res->data() );
- if ( str::contains( ns , "$cmd" ) ) {
- if ( isNotMasterErrorString( x["errmsg"] ) )
- isntMaster();
- }
- else {
- if ( isNotMasterErrorString( getErrField( x ) ) )
- isntMaster();
- }
- }
- }
-
- return true;
- }
-
-}
diff --git a/client/dbclient_rs.h b/client/dbclient_rs.h
deleted file mode 100644
index 318b3cf913d..00000000000
--- a/client/dbclient_rs.h
+++ /dev/null
@@ -1,384 +0,0 @@
-/** @file dbclient_rs.h Connect to a Replica Set, from C++ */
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "../pch.h"
-#include "dbclient.h"
-
-namespace mongo {
-
- class ReplicaSetMonitor;
- typedef shared_ptr<ReplicaSetMonitor> ReplicaSetMonitorPtr;
- typedef pair<set<string>,set<int> > NodeDiff;
-
- /**
- * manages state about a replica set for client
- * keeps tabs on whose master and what slaves are up
- * can hand a slave to someone for SLAVE_OK
- * one instace per process per replica set
- * TODO: we might be able to use a regular Node * to avoid _lock
- */
- class ReplicaSetMonitor {
- public:
-
- typedef boost::function1<void,const ReplicaSetMonitor*> ConfigChangeHook;
-
- /**
- * gets a cached Monitor per name or will create if doesn't exist
- */
- static ReplicaSetMonitorPtr get( const string& name , const vector<HostAndPort>& servers );
-
- /**
- * gets a cached Monitor per name or will return none if it doesn't exist
- */
- static ReplicaSetMonitorPtr get( const string& name );
-
-
- /**
- * checks all sets for current master and new secondaries
- * usually only called from a BackgroundJob
- */
- static void checkAll( bool checkAllSecondaries );
-
- /**
- * this is called whenever the config of any repclia set changes
- * currently only 1 globally
- * asserts if one already exists
- * ownership passes to ReplicaSetMonitor and the hook will actually never be deleted
- */
- static void setConfigChangeHook( ConfigChangeHook hook );
-
- ~ReplicaSetMonitor();
-
- /** @return HostAndPort or throws an exception */
- HostAndPort getMaster();
-
- /**
- * notify the monitor that server has faild
- */
- void notifyFailure( const HostAndPort& server );
-
- /** @return prev if its still ok, and if not returns a random slave that is ok for reads */
- HostAndPort getSlave( const HostAndPort& prev );
-
- /** @return a random slave that is ok for reads */
- HostAndPort getSlave();
-
-
- /**
- * notify the monitor that server has faild
- */
- void notifySlaveFailure( const HostAndPort& server );
-
- /**
- * checks for current master and new secondaries
- */
- void check( bool checkAllSecondaries );
-
- string getName() const { return _name; }
-
- string getServerAddress() const;
-
- bool contains( const string& server ) const;
-
- void appendInfo( BSONObjBuilder& b ) const;
-
- private:
- /**
- * This populates a list of hosts from the list of seeds (discarding the
- * seed list).
- * @param name set name
- * @param servers seeds
- */
- ReplicaSetMonitor( const string& name , const vector<HostAndPort>& servers );
-
- /**
- * Checks all connections from the host list and sets the current
- * master.
- *
- * @param checkAllSecondaries if set to false, stop immediately when
- * the master is found or when _master is not -1.
- */
- void _check( bool checkAllSecondaries );
-
- /**
- * Use replSetGetStatus command to make sure hosts in host list are up
- * and readable. Sets Node::ok appropriately.
- */
- void _checkStatus( const string& hostAddr );
-
- /**
- * Add array of hosts to host list. Doesn't do anything if hosts are
- * already in host list.
- * @param hostList the list of hosts to add
- * @param changed if new hosts were added
- */
- void _checkHosts(const BSONObj& hostList, bool& changed);
-
- /**
- * Updates host list.
- * Invariant: if nodesOffset is >= 0, _nodes[nodesOffset].conn should be
- * equal to conn.
- *
- * @param conn the connection to check
- * @param maybePrimary OUT
- * @param verbose
- * @param nodesOffset - offset into _nodes array, -1 for not in it
- *
- * @return true if the connection is good or false if invariant
- * is broken
- */
- bool _checkConnection( DBClientConnection* conn, string& maybePrimary,
- bool verbose, int nodesOffset );
-
- string _getServerAddress_inlock() const;
-
- NodeDiff _getHostDiff_inlock( const BSONObj& hostList );
- bool _shouldChangeHosts( const BSONObj& hostList, bool inlock );
-
- /**
- * @return the index to _nodes corresponding to the server address.
- */
- int _find( const string& server ) const ;
- int _find_inlock( const string& server ) const ;
-
- /**
- * Checks whether the given connection matches the connection stored in _nodes.
- * Mainly used for sanity checking to confirm that nodeOffset still
- * refers to the right connection after releasing and reacquiring
- * a mutex.
- */
- bool _checkConnMatch_inlock( DBClientConnection* conn, size_t nodeOffset ) const;
-
- // protects _nodes and indices pointing to it (_master & _nextSlave)
- mutable mongo::mutex _lock;
-
- /**
- * "Synchronizes" the _checkConnection method. Should ideally be one mutex per
- * connection object being used. The purpose of this lock is to make sure that
- * the reply from the connection the lock holder got is the actual response
- * to what it sent.
- *
- * Deadlock WARNING: never acquire this while holding _lock
- */
- mutable mongo::mutex _checkConnectionLock;
-
- string _name;
- struct Node {
- Node( const HostAndPort& a , DBClientConnection* c )
- : addr( a ) , conn(c) , ok( c != NULL ),
- ismaster(false), secondary( false ) , hidden( false ) , pingTimeMillis(0) {
- }
-
- bool okForSecondaryQueries() const {
- return ok && secondary && ! hidden;
- }
-
- BSONObj toBSON() const {
- return BSON( "addr" << addr.toString() <<
- "isMaster" << ismaster <<
- "secondary" << secondary <<
- "hidden" << hidden <<
- "ok" << ok );
- }
-
- string toString() const {
- return toBSON().toString();
- }
-
- HostAndPort addr;
- shared_ptr<DBClientConnection> conn;
-
- // if this node is in a failure state
- // used for slave routing
- // this is too simple, should make it better
- bool ok;
-
- // as reported by ismaster
- BSONObj lastIsMaster;
-
- bool ismaster;
- bool secondary;
- bool hidden;
-
- int pingTimeMillis;
-
- };
-
- /**
- * Host list.
- */
- vector<Node> _nodes;
-
- int _master; // which node is the current master. -1 means no master is known
- int _nextSlave; // which node is the current slave
-
- static mongo::mutex _setsLock; // protects _sets
- static map<string,ReplicaSetMonitorPtr> _sets; // set name to Monitor
-
- static ConfigChangeHook _hook;
- };
-
- /** Use this class to connect to a replica set of servers. The class will manage
- checking for which server in a replica set is master, and do failover automatically.
-
- This can also be used to connect to replica pairs since pairs are a subset of sets
-
- On a failover situation, expect at least one operation to return an error (throw
- an exception) before the failover is complete. Operations are not retried.
- */
- class DBClientReplicaSet : public DBClientBase {
-
- public:
- /** Call connect() after constructing. autoReconnect is always on for DBClientReplicaSet connections. */
- DBClientReplicaSet( const string& name , const vector<HostAndPort>& servers, double so_timeout=0 );
- virtual ~DBClientReplicaSet();
-
- /** Returns false if nomember of the set were reachable, or neither is
- * master, although,
- * when false returned, you can still try to use this connection object, it will
- * try reconnects.
- */
- bool connect();
-
- /** Authorize. Authorizes all nodes as needed
- */
- virtual bool auth(const string &dbname, const string &username, const string &pwd, string& errmsg, bool digestPassword = true );
-
- // ----------- simple functions --------------
-
- /** throws userassertion "no master found" */
- virtual auto_ptr<DBClientCursor> query(const string &ns, Query query, int nToReturn = 0, int nToSkip = 0,
- const BSONObj *fieldsToReturn = 0, int queryOptions = 0 , int batchSize = 0 );
-
- /** throws userassertion "no master found" */
- virtual BSONObj findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
-
- virtual void insert( const string &ns , BSONObj obj , int flags=0);
-
- /** insert multiple objects. Note that single object insert is asynchronous, so this version
- is only nominally faster and not worth a special effort to try to use. */
- virtual void insert( const string &ns, const vector< BSONObj >& v , int flags=0);
-
- virtual void remove( const string &ns , Query obj , bool justOne = 0 );
-
- virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = 0 , bool multi = 0 );
-
- virtual void killCursor( long long cursorID );
-
- // ---- access raw connections ----
-
- DBClientConnection& masterConn();
- DBClientConnection& slaveConn();
-
- // ---- callback pieces -------
-
- virtual void say( Message &toSend, bool isRetry = false );
- virtual bool recv( Message &toRecv );
- virtual void checkResponse( const char* data, int nReturned, bool* retry = NULL, string* targetHost = NULL );
-
- /* this is the callback from our underlying connections to notify us that we got a "not master" error.
- */
- void isntMaster();
-
- /* this is used to indicate we got a "not master or secondary" error from a secondary.
- */
- void isntSecondary();
-
- // ----- status ------
-
- virtual bool isFailed() const { return ! _master || _master->isFailed(); }
-
- // ----- informational ----
-
- double getSoTimeout() const { return _so_timeout; }
-
- string toString() { return getServerAddress(); }
-
- string getServerAddress() const { return _monitor->getServerAddress(); }
-
- virtual ConnectionString::ConnectionType type() const { return ConnectionString::SET; }
- virtual bool lazySupported() const { return true; }
-
- // ---- low level ------
-
- virtual bool call( Message &toSend, Message &response, bool assertOk=true , string * actualServer = 0 );
- virtual bool callRead( Message& toSend , Message& response ) { return checkMaster()->callRead( toSend , response ); }
-
-
- protected:
- virtual void sayPiggyBack( Message &toSend ) { checkMaster()->say( toSend ); }
-
- private:
-
- // Used to simplify slave-handling logic on errors
- auto_ptr<DBClientCursor> checkSlaveQueryResult( auto_ptr<DBClientCursor> result );
-
- DBClientConnection * checkMaster();
- DBClientConnection * checkSlave();
-
- void _auth( DBClientConnection * conn );
-
- ReplicaSetMonitorPtr _monitor;
-
- HostAndPort _masterHost;
- scoped_ptr<DBClientConnection> _master;
-
- HostAndPort _slaveHost;
- scoped_ptr<DBClientConnection> _slave;
-
- double _so_timeout;
-
- /**
- * for storing authentication info
- * fields are exactly for DBClientConnection::auth
- */
- struct AuthInfo {
- AuthInfo( string d , string u , string p , bool di )
- : dbname( d ) , username( u ) , pwd( p ) , digestPassword( di ) {}
- string dbname;
- string username;
- string pwd;
- bool digestPassword;
- };
-
- // we need to store so that when we connect to a new node on failure
- // we can re-auth
- // this could be a security issue, as the password is stored in memory
- // not sure if/how we should handle
- list<AuthInfo> _auths;
-
- protected:
-
- /**
- * for storing (non-threadsafe) information between lazy calls
- */
- class LazyState {
- public:
- LazyState() : _lastClient( NULL ), _lastOp( -1 ), _slaveOk( false ), _retries( 0 ) {}
- DBClientConnection* _lastClient;
- int _lastOp;
- bool _slaveOk;
- int _retries;
-
- } _lazyState;
-
- };
-
-
-}
diff --git a/client/dbclientcursor.cpp b/client/dbclientcursor.cpp
deleted file mode 100644
index 9da45e5c32d..00000000000
--- a/client/dbclientcursor.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-// dbclient.cpp - connect to a Mongo database as a database, from C++
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "pch.h"
-#include "dbclient.h"
-#include "../db/dbmessage.h"
-#include "../db/cmdline.h"
-#include "connpool.h"
-#include "../s/shard.h"
-
-namespace mongo {
-
- void assembleRequest( const string &ns, BSONObj query, int nToReturn, int nToSkip, const BSONObj *fieldsToReturn, int queryOptions, Message &toSend );
-
- int DBClientCursor::nextBatchSize() {
-
- if ( nToReturn == 0 )
- return batchSize;
-
- if ( batchSize == 0 )
- return nToReturn;
-
- return batchSize < nToReturn ? batchSize : nToReturn;
- }
-
- void DBClientCursor::_assembleInit( Message& toSend ) {
- if ( !cursorId ) {
- assembleRequest( ns, query, nextBatchSize() , nToSkip, fieldsToReturn, opts, toSend );
- }
- else {
- BufBuilder b;
- b.appendNum( opts );
- b.appendStr( ns );
- b.appendNum( nToReturn );
- b.appendNum( cursorId );
- toSend.setData( dbGetMore, b.buf(), b.len() );
- }
- }
-
- bool DBClientCursor::init() {
- Message toSend;
- _assembleInit( toSend );
-
- if ( !_client->call( toSend, *b.m, false ) ) {
- // log msg temp?
- log() << "DBClientCursor::init call() failed" << endl;
- return false;
- }
- if ( b.m->empty() ) {
- // log msg temp?
- log() << "DBClientCursor::init message from call() was empty" << endl;
- return false;
- }
- dataReceived();
- return true;
- }
-
- void DBClientCursor::initLazy( bool isRetry ) {
- verify( 15875 , _client->lazySupported() );
- Message toSend;
- _assembleInit( toSend );
- _client->say( toSend, isRetry );
- }
-
- bool DBClientCursor::initLazyFinish( bool& retry ) {
-
- bool recvd = _client->recv( *b.m );
-
- // If we get a bad response, return false
- if ( ! recvd || b.m->empty() ) {
-
- if( !recvd )
- log() << "DBClientCursor::init lazy say() failed" << endl;
- if( b.m->empty() )
- log() << "DBClientCursor::init message from say() was empty" << endl;
-
- _client->checkResponse( NULL, -1, &retry, &_lazyHost );
-
- return false;
-
- }
-
- dataReceived( retry, _lazyHost );
- return ! retry;
- }
-
- void DBClientCursor::requestMore() {
- assert( cursorId && b.pos == b.nReturned );
-
- if (haveLimit) {
- nToReturn -= b.nReturned;
- assert(nToReturn > 0);
- }
- BufBuilder b;
- b.appendNum(opts);
- b.appendStr(ns);
- b.appendNum(nextBatchSize());
- b.appendNum(cursorId);
-
- Message toSend;
- toSend.setData(dbGetMore, b.buf(), b.len());
- auto_ptr<Message> response(new Message());
-
- if ( _client ) {
- _client->call( toSend, *response );
- this->b.m = response;
- dataReceived();
- }
- else {
- assert( _scopedHost.size() );
- ScopedDbConnection conn( _scopedHost );
- conn->call( toSend , *response );
- _client = conn.get();
- this->b.m = response;
- dataReceived();
- _client = 0;
- conn.done();
- }
- }
-
- /** with QueryOption_Exhaust, the server just blasts data at us (marked at end with cursorid==0). */
- void DBClientCursor::exhaustReceiveMore() {
- assert( cursorId && b.pos == b.nReturned );
- assert( !haveLimit );
- auto_ptr<Message> response(new Message());
- assert( _client );
- if ( _client->recv(*response) ) {
- b.m = response;
- dataReceived();
- }
- }
-
- void DBClientCursor::dataReceived( bool& retry, string& host ) {
-
- QueryResult *qr = (QueryResult *) b.m->singleData();
- resultFlags = qr->resultFlags();
-
- if ( qr->resultFlags() & ResultFlag_ErrSet ) {
- wasError = true;
- }
-
- if ( qr->resultFlags() & ResultFlag_CursorNotFound ) {
- // cursor id no longer valid at the server.
- assert( qr->cursorId == 0 );
- cursorId = 0; // 0 indicates no longer valid (dead)
- if ( ! ( opts & QueryOption_CursorTailable ) )
- throw UserException( 13127 , "getMore: cursor didn't exist on server, possible restart or timeout?" );
- }
-
- if ( cursorId == 0 || ! ( opts & QueryOption_CursorTailable ) ) {
- // only set initially: we don't want to kill it on end of data
- // if it's a tailable cursor
- cursorId = qr->cursorId;
- }
-
- b.nReturned = qr->nReturned;
- b.pos = 0;
- b.data = qr->data();
-
- _client->checkResponse( b.data, b.nReturned, &retry, &host ); // watches for "not master"
-
- /* this assert would fire the way we currently work:
- assert( nReturned || cursorId == 0 );
- */
- }
-
- /** If true, safe to call next(). Requests more from server if necessary. */
- bool DBClientCursor::more() {
- _assertIfNull();
-
- if ( !_putBack.empty() )
- return true;
-
- if (haveLimit && b.pos >= nToReturn)
- return false;
-
- if ( b.pos < b.nReturned )
- return true;
-
- if ( cursorId == 0 )
- return false;
-
- requestMore();
- return b.pos < b.nReturned;
- }
-
- BSONObj DBClientCursor::next() {
- DEV _assertIfNull();
- if ( !_putBack.empty() ) {
- BSONObj ret = _putBack.top();
- _putBack.pop();
- return ret;
- }
-
- uassert(13422, "DBClientCursor next() called but more() is false", b.pos < b.nReturned);
-
- b.pos++;
- BSONObj o(b.data);
- b.data += o.objsize();
- /* todo would be good to make data null at end of batch for safety */
- return o;
- }
-
- void DBClientCursor::peek(vector<BSONObj>& v, int atMost) {
- int m = atMost;
-
- /*
- for( stack<BSONObj>::iterator i = _putBack.begin(); i != _putBack.end(); i++ ) {
- if( m == 0 )
- return;
- v.push_back(*i);
- m--;
- n++;
- }
- */
-
- int p = b.pos;
- const char *d = b.data;
- while( m && p < b.nReturned ) {
- BSONObj o(d);
- d += o.objsize();
- p++;
- m--;
- v.push_back(o);
- }
- }
-
- bool DBClientCursor::peekError(BSONObj* error){
- if( ! wasError ) return false;
-
- vector<BSONObj> v;
- peek(v, 1);
-
- assert( v.size() == 1 );
- assert( hasErrField( v[0] ) );
-
- if( error ) *error = v[0].getOwned();
- return true;
- }
-
- void DBClientCursor::attach( AScopedConnection * conn ) {
- assert( _scopedHost.size() == 0 );
- assert( conn );
- assert( conn->get() );
-
- if ( conn->get()->type() == ConnectionString::SET ||
- conn->get()->type() == ConnectionString::SYNC ) {
- if( _lazyHost.size() > 0 )
- _scopedHost = _lazyHost;
- else if( _client )
- _scopedHost = _client->getServerAddress();
- else
- massert(14821, "No client or lazy client specified, cannot store multi-host connection.", false);
- }
- else {
- _scopedHost = conn->getHost();
- }
-
- conn->done();
- _client = 0;
- _lazyHost = "";
- }
-
- DBClientCursor::~DBClientCursor() {
- if (!this)
- return;
-
- DESTRUCTOR_GUARD (
-
- if ( cursorId && _ownCursor && ! inShutdown() ) {
- BufBuilder b;
- b.appendNum( (int)0 ); // reserved
- b.appendNum( (int)1 ); // number
- b.appendNum( cursorId );
-
- Message m;
- m.setData( dbKillCursors , b.buf() , b.len() );
-
- if ( _client ) {
-
- // Kill the cursor the same way the connection itself would. Usually, non-lazily
- if( DBClientConnection::getLazyKillCursor() )
- _client->sayPiggyBack( m );
- else
- _client->say( m );
-
- }
- else {
- assert( _scopedHost.size() );
- ScopedDbConnection conn( _scopedHost );
-
- if( DBClientConnection::getLazyKillCursor() )
- conn->sayPiggyBack( m );
- else
- conn->say( m );
-
- conn.done();
- }
- }
-
- );
- }
-
-
-} // namespace mongo
diff --git a/client/dbclientcursor.h b/client/dbclientcursor.h
deleted file mode 100644
index 977bd30561e..00000000000
--- a/client/dbclientcursor.h
+++ /dev/null
@@ -1,239 +0,0 @@
-// file dbclientcursor.h
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "../pch.h"
-#include "../util/net/message.h"
-#include "../db/jsobj.h"
-#include "../db/json.h"
-#include <stack>
-
-namespace mongo {
-
- class AScopedConnection;
-
- /** for mock purposes only -- do not create variants of DBClientCursor, nor hang code here */
- class DBClientCursorInterface {
- public:
- virtual ~DBClientCursorInterface() {}
-
- virtual bool more() = 0;
- virtual BSONObj next() = 0;
-
- // TODO bring more of the DBClientCursor interface to here
-
- protected:
- DBClientCursorInterface() {}
- };
-
- /** Queries return a cursor object */
- class DBClientCursor : public DBClientCursorInterface {
- public:
- /** If true, safe to call next(). Requests more from server if necessary. */
- bool more();
-
- /** If true, there is more in our local buffers to be fetched via next(). Returns
- false when a getMore request back to server would be required. You can use this
- if you want to exhaust whatever data has been fetched to the client already but
- then perhaps stop.
- */
- int objsLeftInBatch() const { _assertIfNull(); return _putBack.size() + b.nReturned - b.pos; }
- bool moreInCurrentBatch() { return objsLeftInBatch() > 0; }
-
- /** next
- @return next object in the result cursor.
- on an error at the remote server, you will get back:
- { $err: <string> }
- if you do not want to handle that yourself, call nextSafe().
- */
- BSONObj next();
-
- /**
- restore an object previously returned by next() to the cursor
- */
- void putBack( const BSONObj &o ) { _putBack.push( o.getOwned() ); }
-
- /** throws AssertionException if get back { $err : ... } */
- BSONObj nextSafe() {
- BSONObj o = next();
- if( strcmp(o.firstElementFieldName(), "$err") == 0 ) {
- string s = "nextSafe(): " + o.toString();
- if( logLevel >= 5 )
- log() << s << endl;
- uasserted(13106, s);
- }
- return o;
- }
-
- /** peek ahead at items buffered for future next() calls.
- never requests new data from the server. so peek only effective
- with what is already buffered.
- WARNING: no support for _putBack yet!
- */
- void peek(vector<BSONObj>&, int atMost);
-
- /**
- * peek ahead and see if an error occurred, and get the error if so.
- */
- bool peekError(BSONObj* error = NULL);
-
- /**
- iterate the rest of the cursor and return the number if items
- */
- int itcount() {
- int c = 0;
- while ( more() ) {
- next();
- c++;
- }
- return c;
- }
-
- /** cursor no longer valid -- use with tailable cursors.
- note you should only rely on this once more() returns false;
- 'dead' may be preset yet some data still queued and locally
- available from the dbclientcursor.
- */
- bool isDead() const { return !this || cursorId == 0; }
-
- bool tailable() const { return (opts & QueryOption_CursorTailable) != 0; }
-
- /** see ResultFlagType (constants.h) for flag values
- mostly these flags are for internal purposes -
- ResultFlag_ErrSet is the possible exception to that
- */
- bool hasResultFlag( int flag ) {
- _assertIfNull();
- return (resultFlags & flag) != 0;
- }
-
- DBClientCursor( DBClientBase* client, const string &_ns, BSONObj _query, int _nToReturn,
- int _nToSkip, const BSONObj *_fieldsToReturn, int queryOptions , int bs ) :
- _client(client),
- ns(_ns),
- query(_query),
- nToReturn(_nToReturn),
- haveLimit( _nToReturn > 0 && !(queryOptions & QueryOption_CursorTailable)),
- nToSkip(_nToSkip),
- fieldsToReturn(_fieldsToReturn),
- opts(queryOptions),
- batchSize(bs==1?2:bs),
- cursorId(),
- _ownCursor( true ),
- wasError( false ) {
- }
-
- DBClientCursor( DBClientBase* client, const string &_ns, long long _cursorId, int _nToReturn, int options ) :
- _client(client),
- ns(_ns),
- nToReturn( _nToReturn ),
- haveLimit( _nToReturn > 0 && !(options & QueryOption_CursorTailable)),
- opts( options ),
- cursorId(_cursorId),
- _ownCursor( true ) {
- }
-
- virtual ~DBClientCursor();
-
- long long getCursorId() const { return cursorId; }
-
- /** by default we "own" the cursor and will send the server a KillCursor
- message when ~DBClientCursor() is called. This function overrides that.
- */
- void decouple() { _ownCursor = false; }
-
- void attach( AScopedConnection * conn );
-
- /**
- * actually does the query
- */
- bool init();
-
- void initLazy( bool isRetry = false );
- bool initLazyFinish( bool& retry );
-
- class Batch : boost::noncopyable {
- friend class DBClientCursor;
- auto_ptr<Message> m;
- int nReturned;
- int pos;
- const char *data;
- public:
- Batch() : m( new Message() ), nReturned(), pos(), data() { }
- };
-
- private:
- friend class DBClientBase;
- friend class DBClientConnection;
-
- int nextBatchSize();
-
- Batch b;
- DBClientBase* _client;
- string ns;
- BSONObj query;
- int nToReturn;
- bool haveLimit;
- int nToSkip;
- const BSONObj *fieldsToReturn;
- int opts;
- int batchSize;
- stack< BSONObj > _putBack;
- int resultFlags;
- long long cursorId;
- bool _ownCursor; // see decouple()
- string _scopedHost;
- string _lazyHost;
- bool wasError;
-
- void dataReceived() { bool retry; string lazyHost; dataReceived( retry, lazyHost ); }
- void dataReceived( bool& retry, string& lazyHost );
- void requestMore();
- void exhaustReceiveMore(); // for exhaust
-
- // Don't call from a virtual function
- void _assertIfNull() const { uassert(13348, "connection died", this); }
-
- // non-copyable , non-assignable
- DBClientCursor( const DBClientCursor& );
- DBClientCursor& operator=( const DBClientCursor& );
-
- // init pieces
- void _assembleInit( Message& toSend );
- };
-
- /** iterate over objects in current batch only - will not cause a network call
- */
- class DBClientCursorBatchIterator {
- public:
- DBClientCursorBatchIterator( DBClientCursor &c ) : _c( c ), _n() {}
- bool moreInCurrentBatch() { return _c.moreInCurrentBatch(); }
- BSONObj nextSafe() {
- massert( 13383, "BatchIterator empty", moreInCurrentBatch() );
- ++_n;
- return _c.nextSafe();
- }
- int n() const { return _n; }
- private:
- DBClientCursor &_c;
- int _n;
- };
-
-} // namespace mongo
-
-#include "undef_macros.h"
diff --git a/client/dbclientmockcursor.h b/client/dbclientmockcursor.h
deleted file mode 100644
index 8d85ff5ad2e..00000000000
--- a/client/dbclientmockcursor.h
+++ /dev/null
@@ -1,40 +0,0 @@
-//@file dbclientmockcursor.h
-
-/* Copyright 2010 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "dbclientcursor.h"
-
-namespace mongo {
-
- class DBClientMockCursor : public DBClientCursorInterface {
- public:
- DBClientMockCursor( const BSONArray& mockCollection ) : _iter( mockCollection ) {}
- virtual ~DBClientMockCursor() {}
-
- bool more() { return _iter.more(); }
- BSONObj next() { return _iter.next().Obj(); }
-
- private:
- BSONObjIterator _iter;
-
- // non-copyable , non-assignable
- DBClientMockCursor( const DBClientMockCursor& );
- DBClientMockCursor& operator=( const DBClientMockCursor& );
- };
-
-} // namespace mongo
diff --git a/client/distlock.cpp b/client/distlock.cpp
deleted file mode 100644
index d31e304aba4..00000000000
--- a/client/distlock.cpp
+++ /dev/null
@@ -1,967 +0,0 @@
-// @file distlock.h
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "pch.h"
-#include "dbclient.h"
-#include "distlock.h"
-
-namespace mongo {
-
- LabeledLevel DistributedLock::logLvl( 1 );
- DistributedLock::LastPings DistributedLock::lastPings;
-
- ThreadLocalValue<string> distLockIds("");
-
- /* ==================
- * Module initialization
- */
-
- boost::once_flag _init = BOOST_ONCE_INIT;
- static string* _cachedProcessString = NULL;
-
- static void initModule() {
- // cache process string
- stringstream ss;
- ss << getHostName() << ":" << cmdLine.port << ":" << time(0) << ":" << rand();
- _cachedProcessString = new string( ss.str() );
- }
-
- /* =================== */
-
- string getDistLockProcess() {
- boost::call_once( initModule, _init );
- assert( _cachedProcessString );
- return *_cachedProcessString;
- }
-
- string getDistLockId() {
- string s = distLockIds.get();
- if ( s.empty() ) {
- stringstream ss;
- ss << getDistLockProcess() << ":" << getThreadName() << ":" << rand();
- s = ss.str();
- distLockIds.set( s );
- }
- return s;
- }
-
-
- class DistributedLockPinger {
- public:
-
- DistributedLockPinger()
- : _mutex( "DistributedLockPinger" ) {
- }
-
- void _distLockPingThread( ConnectionString addr, string process, unsigned long long sleepTime ) {
-
- setThreadName( "LockPinger" );
-
- string pingId = pingThreadId( addr, process );
-
- log( DistributedLock::logLvl - 1 ) << "creating distributed lock ping thread for " << addr
- << " and process " << process
- << " (sleeping for " << sleepTime << "ms)" << endl;
-
- static int loops = 0;
- while( ! inShutdown() && ! shouldKill( addr, process ) ) {
-
- log( DistributedLock::logLvl + 2 ) << "distributed lock pinger '" << pingId << "' about to ping." << endl;
-
- Date_t pingTime;
-
- try {
- ScopedDbConnection conn( addr, 30.0 );
-
- pingTime = jsTime();
-
- // refresh the entry corresponding to this process in the lockpings collection
- conn->update( DistributedLock::lockPingNS ,
- BSON( "_id" << process ) ,
- BSON( "$set" << BSON( "ping" << pingTime ) ) ,
- true );
-
- string err = conn->getLastError();
- if ( ! err.empty() ) {
- warning() << "pinging failed for distributed lock pinger '" << pingId << "'."
- << causedBy( err ) << endl;
- conn.done();
-
- // Sleep for normal ping time
- sleepmillis(sleepTime);
- continue;
- }
-
- // remove really old entries from the lockpings collection if they're not holding a lock
- // (this may happen if an instance of a process was taken down and no new instance came up to
- // replace it for a quite a while)
- // if the lock is taken, the take-over mechanism should handle the situation
- auto_ptr<DBClientCursor> c = conn->query( DistributedLock::locksNS , BSONObj() );
- // TODO: Would be good to make clear whether query throws or returns empty on errors
- uassert( 16060, str::stream() << "cannot query locks collection on config server " << conn.getHost(), c.get() );
-
- set<string> pids;
- while ( c->more() ) {
- BSONObj lock = c->next();
- if ( ! lock["process"].eoo() ) {
- pids.insert( lock["process"].valuestrsafe() );
- }
- }
-
- Date_t fourDays = pingTime - ( 4 * 86400 * 1000 ); // 4 days
- conn->remove( DistributedLock::lockPingNS , BSON( "_id" << BSON( "$nin" << pids ) << "ping" << LT << fourDays ) );
- err = conn->getLastError();
- if ( ! err.empty() ) {
- warning() << "ping cleanup for distributed lock pinger '" << pingId << " failed."
- << causedBy( err ) << endl;
- conn.done();
-
- // Sleep for normal ping time
- sleepmillis(sleepTime);
- continue;
- }
-
- // create index so remove is fast even with a lot of servers
- if ( loops++ == 0 ) {
- conn->ensureIndex( DistributedLock::lockPingNS , BSON( "ping" << 1 ) );
- }
-
- log( DistributedLock::logLvl - ( loops % 10 == 0 ? 1 : 0 ) ) << "cluster " << addr << " pinged successfully at " << pingTime
- << " by distributed lock pinger '" << pingId
- << "', sleeping for " << sleepTime << "ms" << endl;
-
- // Remove old locks, if possible
- // Make sure no one else is adding to this list at the same time
- scoped_lock lk( _mutex );
-
- int numOldLocks = _oldLockOIDs.size();
- if( numOldLocks > 0 )
- log( DistributedLock::logLvl - 1 ) << "trying to delete " << _oldLockOIDs.size() << " old lock entries for process " << process << endl;
-
- bool removed = false;
- for( list<OID>::iterator i = _oldLockOIDs.begin(); i != _oldLockOIDs.end();
- i = ( removed ? _oldLockOIDs.erase( i ) : ++i ) ) {
- removed = false;
- try {
- // Got OID from lock with id, so we don't need to specify id again
- conn->update( DistributedLock::locksNS ,
- BSON( "ts" << *i ),
- BSON( "$set" << BSON( "state" << 0 ) ) );
-
- // Either the update went through or it didn't, either way we're done trying to
- // unlock
- log( DistributedLock::logLvl - 1 ) << "handled late remove of old distributed lock with ts " << *i << endl;
- removed = true;
- }
- catch( UpdateNotTheSame& ) {
- log( DistributedLock::logLvl - 1 ) << "partially removed old distributed lock with ts " << *i << endl;
- removed = true;
- }
- catch ( std::exception& e) {
- warning() << "could not remove old distributed lock with ts " << *i
- << causedBy( e ) << endl;
- }
-
- }
-
- if( numOldLocks > 0 && _oldLockOIDs.size() > 0 ){
- log( DistributedLock::logLvl - 1 ) << "not all old lock entries could be removed for process " << process << endl;
- }
-
- conn.done();
-
- }
- catch ( std::exception& e ) {
- warning() << "distributed lock pinger '" << pingId << "' detected an exception while pinging."
- << causedBy( e ) << endl;
- }
-
- sleepmillis(sleepTime);
- }
-
- warning() << "removing distributed lock ping thread '" << pingId << "'" << endl;
-
-
- if( shouldKill( addr, process ) )
- finishKill( addr, process );
-
- }
-
- void distLockPingThread( ConnectionString addr, long long clockSkew, string processId, unsigned long long sleepTime ) {
- try {
- jsTimeVirtualThreadSkew( clockSkew );
- _distLockPingThread( addr, processId, sleepTime );
- }
- catch ( std::exception& e ) {
- error() << "unexpected error while running distributed lock pinger for " << addr << ", process " << processId << causedBy( e ) << endl;
- }
- catch ( ... ) {
- error() << "unknown error while running distributed lock pinger for " << addr << ", process " << processId << endl;
- }
- }
-
- string pingThreadId( const ConnectionString& conn, const string& processId ) {
- return conn.toString() + "/" + processId;
- }
-
- string got( DistributedLock& lock, unsigned long long sleepTime ) {
-
- // Make sure we don't start multiple threads for a process id
- scoped_lock lk( _mutex );
-
- const ConnectionString& conn = lock.getRemoteConnection();
- const string& processId = lock.getProcessId();
- string s = pingThreadId( conn, processId );
-
- // Ignore if we already have a pinging thread for this process.
- if ( _seen.count( s ) > 0 ) return s;
-
- // Check our clock skew
- try {
- if( lock.isRemoteTimeSkewed() ) {
- throw LockException( str::stream() << "clock skew of the cluster " << conn.toString() << " is too far out of bounds to allow distributed locking." , 13650 );
- }
- }
- catch( LockException& e) {
- throw LockException( str::stream() << "error checking clock skew of cluster " << conn.toString() << causedBy( e ) , 13651);
- }
-
- boost::thread t( boost::bind( &DistributedLockPinger::distLockPingThread, this, conn, getJSTimeVirtualThreadSkew(), processId, sleepTime) );
-
- _seen.insert( s );
-
- return s;
- }
-
- void addUnlockOID( const OID& oid ) {
- // Modifying the lock from some other thread
- scoped_lock lk( _mutex );
- _oldLockOIDs.push_back( oid );
- }
-
- bool willUnlockOID( const OID& oid ) {
- scoped_lock lk( _mutex );
- return find( _oldLockOIDs.begin(), _oldLockOIDs.end(), oid ) != _oldLockOIDs.end();
- }
-
- void kill( const ConnectionString& conn, const string& processId ) {
- // Make sure we're in a consistent state before other threads can see us
- scoped_lock lk( _mutex );
-
- string pingId = pingThreadId( conn, processId );
-
- assert( _seen.count( pingId ) > 0 );
- _kill.insert( pingId );
-
- }
-
- bool shouldKill( const ConnectionString& conn, const string& processId ) {
- return _kill.count( pingThreadId( conn, processId ) ) > 0;
- }
-
- void finishKill( const ConnectionString& conn, const string& processId ) {
- // Make sure we're in a consistent state before other threads can see us
- scoped_lock lk( _mutex );
-
- string pingId = pingThreadId( conn, processId );
-
- _kill.erase( pingId );
- _seen.erase( pingId );
-
- }
-
- set<string> _kill;
- set<string> _seen;
- mongo::mutex _mutex;
- list<OID> _oldLockOIDs;
-
- } distLockPinger;
-
-
- const string DistributedLock::lockPingNS = "config.lockpings";
- const string DistributedLock::locksNS = "config.locks";
-
- /**
- * Create a new distributed lock, potentially with a custom sleep and takeover time. If a custom sleep time is
- * specified (time between pings)
- */
- DistributedLock::DistributedLock( const ConnectionString& conn , const string& name , unsigned long long lockTimeout, bool asProcess )
- : _conn(conn) , _name(name) , _id( BSON( "_id" << name ) ), _processId( asProcess ? getDistLockId() : getDistLockProcess() ),
- _lockTimeout( lockTimeout == 0 ? LOCK_TIMEOUT : lockTimeout ), _maxClockSkew( _lockTimeout / LOCK_SKEW_FACTOR ), _maxNetSkew( _maxClockSkew ), _lockPing( _maxClockSkew ),
- _mutex( "DistributedLock" )
- {
- log( logLvl ) << "created new distributed lock for " << name << " on " << conn
- << " ( lock timeout : " << _lockTimeout
- << ", ping interval : " << _lockPing << ", process : " << asProcess << " )"
- << endl;
-
-
- }
-
- DistributedLock::PingData DistributedLock::LastPings::getLastPing( const ConnectionString& conn, const string& lockName ){
- scoped_lock lock( _mutex );
- return _lastPings[ std::pair< string, string >( conn.toString(), lockName ) ];
- }
-
- void DistributedLock::LastPings::setLastPing( const ConnectionString& conn, const string& lockName, const PingData& pd ){
- scoped_lock lock( _mutex );
- _lastPings[ std::pair< string, string >( conn.toString(), lockName ) ] = pd;
- }
-
- Date_t DistributedLock::getRemoteTime() {
- return DistributedLock::remoteTime( _conn, _maxNetSkew );
- }
-
- bool DistributedLock::isRemoteTimeSkewed() {
- return !DistributedLock::checkSkew( _conn, NUM_LOCK_SKEW_CHECKS, _maxClockSkew, _maxNetSkew );
- }
-
- const ConnectionString& DistributedLock::getRemoteConnection() {
- return _conn;
- }
-
- const string& DistributedLock::getProcessId() {
- return _processId;
- }
-
- /**
- * Returns the remote time as reported by the cluster or server. The maximum difference between the reported time
- * and the actual time on the remote server (at the completion of the function) is the maxNetSkew
- */
- Date_t DistributedLock::remoteTime( const ConnectionString& cluster, unsigned long long maxNetSkew ) {
-
- ConnectionString server( *cluster.getServers().begin() );
- ScopedDbConnection conn( server );
-
- BSONObj result;
- long long delay;
-
- try {
- Date_t then = jsTime();
- bool success = conn->runCommand( string("admin"), BSON( "serverStatus" << 1 ), result );
- delay = jsTime() - then;
-
- if( !success )
- throw TimeNotFoundException( str::stream() << "could not get status from server "
- << server.toString() << " in cluster " << cluster.toString()
- << " to check time", 13647 );
-
- // Make sure that our delay is not more than 2x our maximum network skew, since this is the max our remote
- // time value can be off by if we assume a response in the middle of the delay.
- if( delay > (long long) (maxNetSkew * 2) )
- throw TimeNotFoundException( str::stream() << "server " << server.toString()
- << " in cluster " << cluster.toString()
- << " did not respond within max network delay of "
- << maxNetSkew << "ms", 13648 );
- }
- catch(...) {
- conn.done();
- throw;
- }
-
- conn.done();
-
- return result["localTime"].Date() - (delay / 2);
-
- }
-
- bool DistributedLock::checkSkew( const ConnectionString& cluster, unsigned skewChecks, unsigned long long maxClockSkew, unsigned long long maxNetSkew ) {
-
- vector<HostAndPort> servers = cluster.getServers();
-
- if(servers.size() < 1) return true;
-
- vector<long long> avgSkews;
-
- for(unsigned i = 0; i < skewChecks; i++) {
-
- // Find the average skew for each server
- unsigned s = 0;
- for(vector<HostAndPort>::iterator si = servers.begin(); si != servers.end(); ++si,s++) {
-
- if(i == 0) avgSkews.push_back(0);
-
- // Could check if this is self, but shouldn't matter since local network connection should be fast.
- ConnectionString server( *si );
-
- vector<long long> skew;
-
- BSONObj result;
-
- Date_t remote = remoteTime( server, maxNetSkew );
- Date_t local = jsTime();
-
- // Remote time can be delayed by at most MAX_NET_SKEW
-
- // Skew is how much time we'd have to add to local to get to remote
- avgSkews[s] += (long long) (remote - local);
-
- log( logLvl + 1 ) << "skew from remote server " << server << " found: " << (long long) (remote - local) << endl;
-
- }
- }
-
- // Analyze skews
-
- long long serverMaxSkew = 0;
- long long serverMinSkew = 0;
-
- for(unsigned s = 0; s < avgSkews.size(); s++) {
-
- long long avgSkew = (avgSkews[s] /= skewChecks);
-
- // Keep track of max and min skews
- if(s == 0) {
- serverMaxSkew = avgSkew;
- serverMinSkew = avgSkew;
- }
- else {
- if(avgSkew > serverMaxSkew)
- serverMaxSkew = avgSkew;
- if(avgSkew < serverMinSkew)
- serverMinSkew = avgSkew;
- }
-
- }
-
- long long totalSkew = serverMaxSkew - serverMinSkew;
-
- // Make sure our max skew is not more than our pre-set limit
- if(totalSkew > (long long) maxClockSkew) {
- log( logLvl + 1 ) << "total clock skew of " << totalSkew << "ms for servers " << cluster << " is out of " << maxClockSkew << "ms bounds." << endl;
- return false;
- }
-
- log( logLvl + 1 ) << "total clock skew of " << totalSkew << "ms for servers " << cluster << " is in " << maxClockSkew << "ms bounds." << endl;
- return true;
- }
-
- // For use in testing, ping thread should run indefinitely in practice.
- bool DistributedLock::killPinger( DistributedLock& lock ) {
- if( lock._threadId == "") return false;
-
- distLockPinger.kill( lock._conn, lock._processId );
- return true;
- }
-
- // Semantics of this method are basically that if the lock cannot be acquired, returns false, can be retried.
- // If the lock should not be tried again (some unexpected error) a LockException is thrown.
- // If we are only trying to re-enter a currently held lock, reenter should be true.
- // Note: reenter doesn't actually make this lock re-entrant in the normal sense, since it can still only
- // be unlocked once, instead it is used to verify that the lock is already held.
- bool DistributedLock::lock_try( const string& why , bool reenter, BSONObj * other ) {
-
- // TODO: Start pinging only when we actually get the lock?
- // If we don't have a thread pinger, make sure we shouldn't have one
- if( _threadId == "" ){
- scoped_lock lk( _mutex );
- _threadId = distLockPinger.got( *this, _lockPing );
- }
-
- // This should always be true, if not, we are using the lock incorrectly.
- assert( _name != "" );
-
- log( logLvl ) << "trying to acquire new distributed lock for " << _name << " on " << _conn
- << " ( lock timeout : " << _lockTimeout
- << ", ping interval : " << _lockPing << ", process : " << _processId << " )"
- << endl;
-
- // write to dummy if 'other' is null
- BSONObj dummyOther;
- if ( other == NULL )
- other = &dummyOther;
-
- ScopedDbConnection conn( _conn );
-
- BSONObjBuilder queryBuilder;
- queryBuilder.appendElements( _id );
- queryBuilder.append( "state" , 0 );
-
- {
- // make sure its there so we can use simple update logic below
- BSONObj o = conn->findOne( locksNS , _id ).getOwned();
-
- // Case 1: No locks
- if ( o.isEmpty() ) {
- try {
- log( logLvl ) << "inserting initial doc in " << locksNS << " for lock " << _name << endl;
- conn->insert( locksNS , BSON( "_id" << _name << "state" << 0 << "who" << "" ) );
- }
- catch ( UserException& e ) {
- warning() << "could not insert initial doc for distributed lock " << _name << causedBy( e ) << endl;
- }
- }
-
- // Case 2: A set lock that we might be able to force
- else if ( o["state"].numberInt() > 0 ) {
-
- string lockName = o["_id"].String() + string("/") + o["process"].String();
-
- bool canReenter = reenter && o["process"].String() == _processId && ! distLockPinger.willUnlockOID( o["ts"].OID() ) && o["state"].numberInt() == 2;
- if( reenter && ! canReenter ) {
- log( logLvl - 1 ) << "not re-entering distributed lock " << lockName;
- if( o["process"].String() != _processId ) log( logLvl - 1 ) << ", different process " << _processId << endl;
- else if( o["state"].numberInt() == 2 ) log( logLvl - 1 ) << ", state not finalized" << endl;
- else log( logLvl - 1 ) << ", ts " << o["ts"].OID() << " scheduled for late unlock" << endl;
-
- // reset since we've been bounced by a previous lock not being where we thought it was,
- // and should go through full forcing process if required.
- // (in theory we should never see a ping here if used correctly)
- *other = o; other->getOwned(); conn.done(); resetLastPing();
- return false;
- }
-
- BSONObj lastPing = conn->findOne( lockPingNS , o["process"].wrap( "_id" ) );
- if ( lastPing.isEmpty() ) {
- log( logLvl ) << "empty ping found for process in lock '" << lockName << "'" << endl;
- // TODO: Using 0 as a "no time found" value Will fail if dates roll over, but then, so will a lot.
- lastPing = BSON( "_id" << o["process"].String() << "ping" << (Date_t) 0 );
- }
-
- unsigned long long elapsed = 0;
- unsigned long long takeover = _lockTimeout;
- PingData _lastPingCheck = getLastPing();
-
- log( logLvl ) << "checking last ping for lock '" << lockName << "'" << " against process " << _lastPingCheck.get<0>() << " and ping " << _lastPingCheck.get<1>() << endl;
-
- try {
-
- Date_t remote = remoteTime( _conn );
-
- // Timeout the elapsed time using comparisons of remote clock
- // For non-finalized locks, timeout 15 minutes since last seen (ts)
- // For finalized locks, timeout 15 minutes since last ping
- bool recPingChange = o["state"].numberInt() == 2 && ( _lastPingCheck.get<0>() != lastPing["_id"].String() || _lastPingCheck.get<1>() != lastPing["ping"].Date() );
- bool recTSChange = _lastPingCheck.get<3>() != o["ts"].OID();
-
- if( recPingChange || recTSChange ) {
- // If the ping has changed since we last checked, mark the current date and time
- setLastPing( PingData( lastPing["_id"].String().c_str(), lastPing["ping"].Date(), remote, o["ts"].OID() ) );
- }
- else {
-
- // GOTCHA! Due to network issues, it is possible that the current time
- // is less than the remote time. We *have* to check this here, otherwise
- // we overflow and our lock breaks.
- if(_lastPingCheck.get<2>() >= remote)
- elapsed = 0;
- else
- elapsed = remote - _lastPingCheck.get<2>();
- }
- }
- catch( LockException& e ) {
-
- // Remote server cannot be found / is not responsive
- warning() << "Could not get remote time from " << _conn << causedBy( e );
- // If our config server is having issues, forget all the pings until we can see it again
- resetLastPing();
-
- }
-
- if ( elapsed <= takeover && ! canReenter ) {
- log( logLvl ) << "could not force lock '" << lockName << "' because elapsed time " << elapsed << " <= takeover time " << takeover << endl;
- *other = o; other->getOwned(); conn.done();
- return false;
- }
- else if( elapsed > takeover && canReenter ) {
- log( logLvl - 1 ) << "not re-entering distributed lock " << lockName << "' because elapsed time " << elapsed << " > takeover time " << takeover << endl;
- *other = o; other->getOwned(); conn.done();
- return false;
- }
-
- log( logLvl - 1 ) << ( canReenter ? "re-entering" : "forcing" ) << " lock '" << lockName << "' because "
- << ( canReenter ? "re-entering is allowed, " : "" )
- << "elapsed time " << elapsed << " > takeover time " << takeover << endl;
-
- if( elapsed > takeover ) {
-
- // Lock may forced, reset our timer if succeeds or fails
- // Ensures that another timeout must happen if something borks up here, and resets our pristine
- // ping state if acquired.
- resetLastPing();
-
- try {
-
- // Check the clock skew again. If we check this before we get a lock
- // and after the lock times out, we can be pretty sure the time is
- // increasing at the same rate on all servers and therefore our
- // timeout is accurate
- uassert( 14023, str::stream() << "remote time in cluster " << _conn.toString() << " is now skewed, cannot force lock.", !isRemoteTimeSkewed() );
-
- // Make sure we break the lock with the correct "ts" (OID) value, otherwise
- // we can overwrite a new lock inserted in the meantime.
- conn->update( locksNS , BSON( "_id" << _id["_id"].String() << "state" << o["state"].numberInt() << "ts" << o["ts"] ),
- BSON( "$set" << BSON( "state" << 0 ) ) );
-
- BSONObj err = conn->getLastErrorDetailed();
- string errMsg = DBClientWithCommands::getLastErrorString(err);
-
- // TODO: Clean up all the extra code to exit this method, probably with a refactor
- if ( !errMsg.empty() || !err["n"].type() || err["n"].numberInt() < 1 ) {
- ( errMsg.empty() ? log( logLvl - 1 ) : warning() ) << "Could not force lock '" << lockName << "' "
- << ( !errMsg.empty() ? causedBy(errMsg) : string("(another force won)") ) << endl;
- *other = o; other->getOwned(); conn.done();
- return false;
- }
-
- }
- catch( UpdateNotTheSame& ) {
- // Ok to continue since we know we forced at least one lock document, and all lock docs
- // are required for a lock to be held.
- warning() << "lock forcing " << lockName << " inconsistent" << endl;
- }
- catch( std::exception& e ) {
- conn.done();
- throw LockException( str::stream() << "exception forcing distributed lock "
- << lockName << causedBy( e ), 13660);
- }
-
- }
- else {
-
- assert( canReenter );
-
- // Lock may be re-entered, reset our timer if succeeds or fails
- // Not strictly necessary, but helpful for small timeouts where thread scheduling is significant.
- // This ensures that two attempts are still required for a force if not acquired, and resets our
- // state if we are acquired.
- resetLastPing();
-
- // Test that the lock is held by trying to update the finalized state of the lock to the same state
- // if it does not update or does not update on all servers, we can't re-enter.
- try {
-
- // Test the lock with the correct "ts" (OID) value
- conn->update( locksNS , BSON( "_id" << _id["_id"].String() << "state" << 2 << "ts" << o["ts"] ),
- BSON( "$set" << BSON( "state" << 2 ) ) );
-
- BSONObj err = conn->getLastErrorDetailed();
- string errMsg = DBClientWithCommands::getLastErrorString(err);
-
- // TODO: Clean up all the extra code to exit this method, probably with a refactor
- if ( ! errMsg.empty() || ! err["n"].type() || err["n"].numberInt() < 1 ) {
- ( errMsg.empty() ? log( logLvl - 1 ) : warning() ) << "Could not re-enter lock '" << lockName << "' "
- << ( !errMsg.empty() ? causedBy(errMsg) : string("(not sure lock is held)") )
- << " gle: " << err
- << endl;
- *other = o; other->getOwned(); conn.done();
- return false;
- }
-
- }
- catch( UpdateNotTheSame& ) {
- // NOT ok to continue since our lock isn't held by all servers, so isn't valid.
- warning() << "inconsistent state re-entering lock, lock " << lockName << " not held" << endl;
- *other = o; other->getOwned(); conn.done();
- return false;
- }
- catch( std::exception& e ) {
- conn.done();
- throw LockException( str::stream() << "exception re-entering distributed lock "
- << lockName << causedBy( e ), 13660);
- }
-
- log( logLvl - 1 ) << "re-entered distributed lock '" << lockName << "'" << endl;
- *other = o; other->getOwned(); conn.done();
- return true;
-
- }
-
- log( logLvl - 1 ) << "lock '" << lockName << "' successfully forced" << endl;
-
- // We don't need the ts value in the query, since we will only ever replace locks with state=0.
- }
- // Case 3: We have an expired lock
- else if ( o["ts"].type() ) {
- queryBuilder.append( o["ts"] );
- }
- }
-
- // Always reset our ping if we're trying to get a lock, since getting a lock implies the lock state is open
- // and no locks need to be forced. If anything goes wrong, we don't want to remember an old lock.
- resetLastPing();
-
- bool gotLock = false;
- BSONObj currLock;
-
- BSONObj lockDetails = BSON( "state" << 1 << "who" << getDistLockId() << "process" << _processId <<
- "when" << jsTime() << "why" << why << "ts" << OID::gen() );
- BSONObj whatIWant = BSON( "$set" << lockDetails );
-
- BSONObj query = queryBuilder.obj();
-
- string lockName = _name + string("/") + _processId;
-
- try {
-
- // Main codepath to acquire lock
-
- log( logLvl ) << "about to acquire distributed lock '" << lockName << ":\n"
- << lockDetails.jsonString(Strict, true) << "\n"
- << query.jsonString(Strict, true) << endl;
-
- conn->update( locksNS , query , whatIWant );
-
- BSONObj err = conn->getLastErrorDetailed();
- string errMsg = DBClientWithCommands::getLastErrorString(err);
-
- currLock = conn->findOne( locksNS , _id );
-
- if ( !errMsg.empty() || !err["n"].type() || err["n"].numberInt() < 1 ) {
- ( errMsg.empty() ? log( logLvl - 1 ) : warning() ) << "could not acquire lock '" << lockName << "' "
- << ( !errMsg.empty() ? causedBy( errMsg ) : string("(another update won)") ) << endl;
- *other = currLock;
- other->getOwned();
- gotLock = false;
- }
- else {
- gotLock = true;
- }
-
- }
- catch ( UpdateNotTheSame& up ) {
-
- // this means our update got through on some, but not others
- warning() << "distributed lock '" << lockName << " did not propagate properly." << causedBy( up ) << endl;
-
- // Overall protection derives from:
- // All unlocking updates use the ts value when setting state to 0
- // This ensures that during locking, we can override all smaller ts locks with
- // our own safe ts value and not be unlocked afterward.
- for ( unsigned i = 0; i < up.size(); i++ ) {
-
- ScopedDbConnection indDB( up[i].first );
- BSONObj indUpdate;
-
- try {
-
- indUpdate = indDB->findOne( locksNS , _id );
-
- // If we override this lock in any way, grab and protect it.
- // We assume/ensure that if a process does not have all lock documents, it is no longer
- // holding the lock.
- // Note - finalized locks may compete too, but we know they've won already if competing
- // in this round. Cleanup of crashes during finalizing may take a few tries.
- if( indUpdate["ts"] < lockDetails["ts"] || indUpdate["state"].numberInt() == 0 ) {
-
- BSONObj grabQuery = BSON( "_id" << _id["_id"].String() << "ts" << indUpdate["ts"].OID() );
-
- // Change ts so we won't be forced, state so we won't be relocked
- BSONObj grabChanges = BSON( "ts" << lockDetails["ts"].OID() << "state" << 1 );
-
- // Either our update will succeed, and we'll grab the lock, or it will fail b/c some other
- // process grabbed the lock (which will change the ts), but the lock will be set until forcing
- indDB->update( locksNS, grabQuery, BSON( "$set" << grabChanges ) );
-
- indUpdate = indDB->findOne( locksNS, _id );
-
- // Our lock should now be set until forcing.
- assert( indUpdate["state"].numberInt() == 1 );
-
- }
- // else our lock is the same, in which case we're safe, or it's a bigger lock,
- // in which case we won't need to protect anything since we won't have the lock.
-
- }
- catch( std::exception& e ) {
- conn.done();
- throw LockException( str::stream() << "distributed lock " << lockName
- << " had errors communicating with individual server "
- << up[1].first << causedBy( e ), 13661 );
- }
-
- assert( !indUpdate.isEmpty() );
-
- // Find max TS value
- if ( currLock.isEmpty() || currLock["ts"] < indUpdate["ts"] ) {
- currLock = indUpdate.getOwned();
- }
-
- indDB.done();
-
- }
-
- // Locks on all servers are now set and safe until forcing
-
- if ( currLock["ts"] == lockDetails["ts"] ) {
- log( logLvl - 1 ) << "lock update won, completing lock propagation for '" << lockName << "'" << endl;
- gotLock = true;
- }
- else {
- log( logLvl - 1 ) << "lock update lost, lock '" << lockName << "' not propagated." << endl;
-
- // Register the lock for deletion, to speed up failover
- // Not strictly necessary, but helpful
- distLockPinger.addUnlockOID( lockDetails["ts"].OID() );
-
- gotLock = false;
- }
- }
- catch( std::exception& e ) {
- conn.done();
- throw LockException( str::stream() << "exception creating distributed lock "
- << lockName << causedBy( e ), 13663 );
- }
-
- // Complete lock propagation
- if( gotLock ) {
-
- // This is now safe, since we know that no new locks will be placed on top of the ones we've checked for at
- // least 15 minutes. Sets the state = 2, so that future clients can determine that the lock is truly set.
- // The invariant for rollbacks is that we will never force locks with state = 2 and active pings, since that
- // indicates the lock is active, but this means the process creating/destroying them must explicitly poll
- // when something goes wrong.
- try {
-
- BSONObjBuilder finalLockDetails;
- BSONObjIterator bi( lockDetails );
- while( bi.more() ) {
- BSONElement el = bi.next();
- if( (string) ( el.fieldName() ) == "state" )
- finalLockDetails.append( "state", 2 );
- else finalLockDetails.append( el );
- }
-
- conn->update( locksNS , _id , BSON( "$set" << finalLockDetails.obj() ) );
-
- BSONObj err = conn->getLastErrorDetailed();
- string errMsg = DBClientWithCommands::getLastErrorString(err);
-
- currLock = conn->findOne( locksNS , _id );
-
- if ( !errMsg.empty() || !err["n"].type() || err["n"].numberInt() < 1 ) {
- warning() << "could not finalize winning lock " << lockName
- << ( !errMsg.empty() ? causedBy( errMsg ) : " (did not update lock) " ) << endl;
- gotLock = false;
- }
- else {
- // SUCCESS!
- gotLock = true;
- }
-
- }
- catch( std::exception& e ) {
- conn.done();
-
- // Register the bad final lock for deletion, in case it exists
- distLockPinger.addUnlockOID( lockDetails["ts"].OID() );
-
- throw LockException( str::stream() << "exception finalizing winning lock"
- << causedBy( e ), 13662 );
- }
-
- }
-
- *other = currLock;
- other->getOwned();
-
- // Log our lock results
- if(gotLock)
- log( logLvl - 1 ) << "distributed lock '" << lockName << "' acquired, ts : " << currLock["ts"].OID() << endl;
- else
- log( logLvl - 1 ) << "distributed lock '" << lockName << "' was not acquired." << endl;
-
- conn.done();
-
- return gotLock;
- }
-
- // Unlock now takes an optional pointer to the lock, so you can be specific about which
- // particular lock you want to unlock. This is required when the config server is down,
- // and so cannot tell you what lock ts you should try later.
- void DistributedLock::unlock( BSONObj* oldLockPtr ) {
-
- assert( _name != "" );
-
- string lockName = _name + string("/") + _processId;
-
- const int maxAttempts = 3;
- int attempted = 0;
-
- BSONObj oldLock;
- if( oldLockPtr ) oldLock = *oldLockPtr;
-
- while ( ++attempted <= maxAttempts ) {
-
- ScopedDbConnection conn( _conn );
-
- try {
-
- if( oldLock.isEmpty() )
- oldLock = conn->findOne( locksNS, _id );
-
- if( oldLock["state"].eoo() || oldLock["state"].numberInt() != 2 || oldLock["ts"].eoo() ) {
- warning() << "cannot unlock invalid distributed lock " << oldLock << endl;
- conn.done();
- break;
- }
-
- // Use ts when updating lock, so that new locks can be sure they won't get trampled.
- conn->update( locksNS ,
- BSON( "_id" << _id["_id"].String() << "ts" << oldLock["ts"].OID() ),
- BSON( "$set" << BSON( "state" << 0 ) ) );
-
- // Check that the lock was actually unlocked... if not, try again
- BSONObj err = conn->getLastErrorDetailed();
- string errMsg = DBClientWithCommands::getLastErrorString(err);
-
- if ( !errMsg.empty() || !err["n"].type() || err["n"].numberInt() < 1 ){
- warning() << "distributed lock unlock update failed, retrying "
- << ( errMsg.empty() ? causedBy( "( update not registered )" ) : causedBy( errMsg ) ) << endl;
- conn.done();
- continue;
- }
-
- log( logLvl - 1 ) << "distributed lock '" << lockName << "' unlocked. " << endl;
- conn.done();
- return;
- }
- catch( UpdateNotTheSame& ) {
- log( logLvl - 1 ) << "distributed lock '" << lockName << "' unlocked (messily). " << endl;
- conn.done();
- break;
- }
- catch ( std::exception& e) {
- warning() << "distributed lock '" << lockName << "' failed unlock attempt."
- << causedBy( e ) << endl;
-
- conn.done();
- // TODO: If our lock timeout is small, sleeping this long may be unsafe.
- if( attempted != maxAttempts) sleepsecs(1 << attempted);
- }
- }
-
- if( attempted > maxAttempts && ! oldLock.isEmpty() && ! oldLock["ts"].eoo() ) {
-
- log( logLvl - 1 ) << "could not unlock distributed lock with ts " << oldLock["ts"].OID()
- << ", will attempt again later" << endl;
-
- // We couldn't unlock the lock at all, so try again later in the pinging thread...
- distLockPinger.addUnlockOID( oldLock["ts"].OID() );
- }
- else if( attempted > maxAttempts ) {
- warning() << "could not unlock untracked distributed lock, a manual force may be required" << endl;
- }
-
- warning() << "distributed lock '" << lockName << "' couldn't consummate unlock request. "
- << "lock may be taken over after " << ( _lockTimeout / (60 * 1000) )
- << " minutes timeout." << endl;
- }
-
-
-
-}
diff --git a/client/distlock.h b/client/distlock.h
deleted file mode 100644
index 106a5d00001..00000000000
--- a/client/distlock.h
+++ /dev/null
@@ -1,244 +0,0 @@
-// distlock.h
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "../pch.h"
-#include "dbclient.h"
-#include "connpool.h"
-#include "redef_macros.h"
-#include "syncclusterconnection.h"
-
-#define LOCK_TIMEOUT (15 * 60 * 1000)
-#define LOCK_SKEW_FACTOR (30)
-#define LOCK_PING (LOCK_TIMEOUT / LOCK_SKEW_FACTOR)
-#define MAX_LOCK_NET_SKEW (LOCK_TIMEOUT / LOCK_SKEW_FACTOR)
-#define MAX_LOCK_CLOCK_SKEW (LOCK_TIMEOUT / LOCK_SKEW_FACTOR)
-#define NUM_LOCK_SKEW_CHECKS (3)
-
-// The maximum clock skew we need to handle between config servers is
-// 2 * MAX_LOCK_NET_SKEW + MAX_LOCK_CLOCK_SKEW.
-
-// Net effect of *this* clock being slow is effectively a multiplier on the max net skew
-// and a linear increase or decrease of the max clock skew.
-
-namespace mongo {
-
- /**
- * Exception class to encapsulate exceptions while managing distributed locks
- */
- class LockException : public DBException {
- public:
- LockException( const char * msg , int code ) : DBException( msg, code ) {}
- LockException( const string& msg, int code ) : DBException( msg, code ) {}
- virtual ~LockException() throw() { }
- };
-
- /**
- * Indicates an error in retrieving time values from remote servers.
- */
- class TimeNotFoundException : public LockException {
- public:
- TimeNotFoundException( const char * msg , int code ) : LockException( msg, code ) {}
- TimeNotFoundException( const string& msg, int code ) : LockException( msg, code ) {}
- virtual ~TimeNotFoundException() throw() { }
- };
-
- /**
- * The distributed lock is a configdb backed way of synchronizing system-wide tasks. A task must be identified by a
- * unique name across the system (e.g., "balancer"). A lock is taken by writing a document in the configdb's locks
- * collection with that name.
- *
- * To be maintained, each taken lock needs to be revalidaded ("pinged") within a pre-established amount of time. This
- * class does this maintenance automatically once a DistributedLock object was constructed.
- */
- class DistributedLock {
- public:
-
- static LabeledLevel logLvl;
-
- typedef boost::tuple<string, Date_t, Date_t, OID> PingData;
-
- class LastPings {
- public:
- LastPings() : _mutex( "DistributedLock::LastPings" ) {}
- ~LastPings(){}
-
- PingData getLastPing( const ConnectionString& conn, const string& lockName );
- void setLastPing( const ConnectionString& conn, const string& lockName, const PingData& pd );
-
- mongo::mutex _mutex;
- map< std::pair<string, string>, PingData > _lastPings;
- };
-
- static LastPings lastPings;
-
- /**
- * The constructor does not connect to the configdb yet and constructing does not mean the lock was acquired.
- * Construction does trigger a lock "pinging" mechanism, though.
- *
- * @param conn address of config(s) server(s)
- * @param name identifier for the lock
- * @param lockTimeout how long can the log go "unpinged" before a new attempt to lock steals it (in minutes).
- * @param lockPing how long to wait between lock pings
- * @param legacy use legacy logic
- *
- */
- DistributedLock( const ConnectionString& conn , const string& name , unsigned long long lockTimeout = 0, bool asProcess = false );
- ~DistributedLock(){};
-
- /**
- * Attempts to acquire 'this' lock, checking if it could or should be stolen from the previous holder. Please
- * consider using the dist_lock_try construct to acquire this lock in an exception safe way.
- *
- * @param why human readable description of why the lock is being taken (used to log)
- * @param whether this is a lock re-entry or a new lock
- * @param other configdb's lock document that is currently holding the lock, if lock is taken, or our own lock
- * details if not
- * @return true if it managed to grab the lock
- */
- bool lock_try( const string& why , bool reenter = false, BSONObj * other = 0 );
-
- /**
- * Releases a previously taken lock.
- */
- void unlock( BSONObj* oldLockPtr = NULL );
-
- Date_t getRemoteTime();
-
- bool isRemoteTimeSkewed();
-
- const string& getProcessId();
-
- const ConnectionString& getRemoteConnection();
-
- /**
- * Check the skew between a cluster of servers
- */
- static bool checkSkew( const ConnectionString& cluster, unsigned skewChecks = NUM_LOCK_SKEW_CHECKS, unsigned long long maxClockSkew = MAX_LOCK_CLOCK_SKEW, unsigned long long maxNetSkew = MAX_LOCK_NET_SKEW );
-
- /**
- * Get the remote time from a server or cluster
- */
- static Date_t remoteTime( const ConnectionString& cluster, unsigned long long maxNetSkew = MAX_LOCK_NET_SKEW );
-
- static bool killPinger( DistributedLock& lock );
-
- /**
- * Namespace for lock pings
- */
- static const string lockPingNS;
-
- /**
- * Namespace for locks
- */
- static const string locksNS;
-
- const ConnectionString _conn;
- const string _name;
- const BSONObj _id;
- const string _processId;
-
- // Timeout for lock, usually LOCK_TIMEOUT
- const unsigned long long _lockTimeout;
- const unsigned long long _maxClockSkew;
- const unsigned long long _maxNetSkew;
- const unsigned long long _lockPing;
-
- private:
-
- void resetLastPing(){ lastPings.setLastPing( _conn, _name, PingData() ); }
- void setLastPing( const PingData& pd ){ lastPings.setLastPing( _conn, _name, pd ); }
- PingData getLastPing(){ return lastPings.getLastPing( _conn, _name ); }
-
- // May or may not exist, depending on startup
- mongo::mutex _mutex;
- string _threadId;
-
- };
-
- class dist_lock_try {
- public:
-
- dist_lock_try() : _lock(NULL), _got(false) {}
-
- dist_lock_try( const dist_lock_try& that ) : _lock(that._lock), _got(that._got), _other(that._other) {
- _other.getOwned();
-
- // Make sure the lock ownership passes to this object,
- // so we only unlock once.
- ((dist_lock_try&) that)._got = false;
- ((dist_lock_try&) that)._lock = NULL;
- ((dist_lock_try&) that)._other = BSONObj();
- }
-
- // Needed so we can handle lock exceptions in context of lock try.
- dist_lock_try& operator=( const dist_lock_try& that ){
-
- if( this == &that ) return *this;
-
- _lock = that._lock;
- _got = that._got;
- _other = that._other;
- _other.getOwned();
- _why = that._why;
-
- // Make sure the lock ownership passes to this object,
- // so we only unlock once.
- ((dist_lock_try&) that)._got = false;
- ((dist_lock_try&) that)._lock = NULL;
- ((dist_lock_try&) that)._other = BSONObj();
-
- return *this;
- }
-
- dist_lock_try( DistributedLock * lock , string why )
- : _lock(lock), _why(why) {
- _got = _lock->lock_try( why , false , &_other );
- }
-
- ~dist_lock_try() {
- if ( _got ) {
- assert( ! _other.isEmpty() );
- _lock->unlock( &_other );
- }
- }
-
- bool reestablish(){
- return retry();
- }
-
- bool retry() {
- assert( _lock );
- assert( _got );
- assert( ! _other.isEmpty() );
-
- return _got = _lock->lock_try( _why , true, &_other );
- }
-
- bool got() const { return _got; }
- BSONObj other() const { return _other; }
-
- private:
- DistributedLock * _lock;
- bool _got;
- BSONObj _other;
- string _why;
- };
-
-}
-
diff --git a/client/distlock_test.cpp b/client/distlock_test.cpp
deleted file mode 100644
index 5f37e6b3e9b..00000000000
--- a/client/distlock_test.cpp
+++ /dev/null
@@ -1,445 +0,0 @@
-// distlock_test.h
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <iostream>
-#include "../pch.h"
-#include "dbclient.h"
-#include "distlock.h"
-#include "../db/commands.h"
-#include "../util/bson_util.h"
-
-// Modify some config options for the RNG, since they cause MSVC to fail
-#include <boost/config.hpp>
-
-#if defined(BOOST_MSVC) && defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
-#undef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
-#define BOOST_RNG_HACK
-#endif
-
-// Well, sort-of cross-platform RNG
-#include <boost/random/mersenne_twister.hpp>
-
-#ifdef BOOST_RNG_HACK
-#define BOOST_NO_MEMBER_TEMPLATE_FRIENDS
-#undef BOOST_RNG_HACK
-#endif
-
-
-#include <boost/random/uniform_int.hpp>
-#include <boost/random/variate_generator.hpp>
-
-
-// TODO: Make a method in BSONObj if useful, don't modify for now
-#define string_field(obj, name, def) ( obj.hasField(name) ? obj[name].String() : def )
-#define number_field(obj, name, def) ( obj.hasField(name) ? obj[name].Number() : def )
-
-namespace mongo {
-
- class TestDistLockWithSync: public Command {
- public:
- TestDistLockWithSync() :
- Command("_testDistLockWithSyncCluster") {
- }
- virtual void help(stringstream& help) const {
- help << "should not be calling this directly" << endl;
- }
-
- virtual bool slaveOk() const {
- return false;
- }
- virtual bool adminOnly() const {
- return true;
- }
- virtual LockType locktype() const {
- return NONE;
- }
-
- static void runThread() {
- while (keepGoing) {
- if (current->lock_try( "test" )) {
- count++;
- int before = count;
- sleepmillis(3);
- int after = count;
-
- if (after != before) {
- error() << " before: " << before << " after: " << after
- << endl;
- }
-
- current->unlock();
- }
- }
- }
-
- bool run(const string&, BSONObj& cmdObj, int, string& errmsg,
- BSONObjBuilder& result, bool) {
- Timer t;
- DistributedLock lk(ConnectionString(cmdObj["host"].String(),
- ConnectionString::SYNC), "testdistlockwithsync", 0, 0);
- current = &lk;
- count = 0;
- gotit = 0;
- errors = 0;
- keepGoing = true;
-
- vector<shared_ptr<boost::thread> > l;
- for (int i = 0; i < 4; i++) {
- l.push_back(
- shared_ptr<boost::thread> (new boost::thread(runThread)));
- }
-
- int secs = 10;
- if (cmdObj["secs"].isNumber())
- secs = cmdObj["secs"].numberInt();
- sleepsecs(secs);
- keepGoing = false;
-
- for (unsigned i = 0; i < l.size(); i++)
- l[i]->join();
-
- current = 0;
-
- result.append("count", count);
- result.append("gotit", gotit);
- result.append("errors", errors);
- result.append("timeMS", t.millis());
-
- return errors == 0;
- }
-
- // variables for test
- static DistributedLock * current;
- static int gotit;
- static int errors;
- static AtomicUInt count;
-
- static bool keepGoing;
-
- } testDistLockWithSyncCmd;
-
- DistributedLock * TestDistLockWithSync::current;
- AtomicUInt TestDistLockWithSync::count;
- int TestDistLockWithSync::gotit;
- int TestDistLockWithSync::errors;
- bool TestDistLockWithSync::keepGoing;
-
-
-
- class TestDistLockWithSkew: public Command {
- public:
-
- static const int logLvl = 1;
-
- TestDistLockWithSkew() :
- Command("_testDistLockWithSkew") {
- }
- virtual void help(stringstream& help) const {
- help << "should not be calling this directly" << endl;
- }
-
- virtual bool slaveOk() const {
- return false;
- }
- virtual bool adminOnly() const {
- return true;
- }
- virtual LockType locktype() const {
- return NONE;
- }
-
- void runThread(ConnectionString& hostConn, unsigned threadId, unsigned seed,
- BSONObj& cmdObj, BSONObjBuilder& result) {
-
- stringstream ss;
- ss << "thread-" << threadId;
- setThreadName(ss.str().c_str());
-
- // Lock name
- string lockName = string_field(cmdObj, "lockName", this->name + "_lock");
-
- // Range of clock skew in diff threads
- int skewRange = (int) number_field(cmdObj, "skewRange", 1);
-
- // How long to wait with the lock
- int threadWait = (int) number_field(cmdObj, "threadWait", 30);
- if(threadWait <= 0) threadWait = 1;
-
- // Max amount of time (ms) a thread waits before checking the lock again
- int threadSleep = (int) number_field(cmdObj, "threadSleep", 30);
- if(threadSleep <= 0) threadSleep = 1;
-
- // How long until the lock is forced in ms, only compared locally
- unsigned long long takeoverMS = (unsigned long long) number_field(cmdObj, "takeoverMS", 0);
-
- // Whether or not we should hang some threads
- int hangThreads = (int) number_field(cmdObj, "hangThreads", 0);
-
-
- boost::mt19937 gen((boost::mt19937::result_type) seed);
-
- boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomSkew(gen, boost::uniform_int<>(0, skewRange));
- boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomWait(gen, boost::uniform_int<>(1, threadWait));
- boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomSleep(gen, boost::uniform_int<>(1, threadSleep));
- boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomNewLock(gen, boost::uniform_int<>(0, 3));
-
-
- int skew = 0;
- if (!lock.get()) {
-
- // Pick a skew, but the first two threads skew the whole range
- if(threadId == 0)
- skew = -skewRange / 2;
- else if(threadId == 1)
- skew = skewRange / 2;
- else skew = randomSkew() - (skewRange / 2);
-
- // Skew this thread
- jsTimeVirtualThreadSkew( skew );
-
- log() << "Initializing lock with skew of " << skew << " for thread " << threadId << endl;
-
- lock.reset(new DistributedLock(hostConn, lockName, takeoverMS, true ));
-
- log() << "Skewed time " << jsTime() << " for thread " << threadId << endl
- << " max wait (with lock: " << threadWait << ", after lock: " << threadSleep << ")" << endl
- << " takeover in " << takeoverMS << "(ms remote)" << endl;
-
- }
-
- DistributedLock* myLock = lock.get();
-
- bool errors = false;
- BSONObj lockObj;
- while (keepGoing) {
- try {
-
- if (myLock->lock_try("Testing distributed lock with skew.", false, &lockObj )) {
-
- log() << "**** Locked for thread " << threadId << " with ts " << lockObj["ts"] << endl;
-
- if( count % 2 == 1 && ! myLock->lock_try( "Testing lock re-entry.", true ) ) {
- errors = true;
- log() << "**** !Could not re-enter lock already held" << endl;
- break;
- }
-
- if( count % 3 == 1 && myLock->lock_try( "Testing lock non-re-entry.", false ) ) {
- errors = true;
- log() << "**** !Invalid lock re-entry" << endl;
- break;
- }
-
- count++;
- int before = count;
- int sleep = randomWait();
- sleepmillis(sleep);
- int after = count;
-
- if(after != before) {
- errors = true;
- log() << "**** !Bad increment while sleeping with lock for: " << sleep << "ms" << endl;
- break;
- }
-
- // Unlock only half the time...
- if(hangThreads == 0 || threadId % hangThreads != 0) {
- log() << "**** Unlocking for thread " << threadId << " with ts " << lockObj["ts"] << endl;
- myLock->unlock( &lockObj );
- }
- else {
- log() << "**** Not unlocking for thread " << threadId << endl;
- assert( DistributedLock::killPinger( *myLock ) );
- // We're simulating a crashed process...
- break;
- }
- }
-
- }
- catch( LockException& e ) {
- log() << "*** !Could not try distributed lock." << causedBy( e ) << endl;
- break;
- }
-
- // Create a new lock 1/3 of the time
- if( randomNewLock() > 1 ){
- lock.reset(new DistributedLock( hostConn, lockName, takeoverMS, true ));
- myLock = lock.get();
- }
-
- sleepmillis(randomSleep());
- }
-
- result << "errors" << errors
- << "skew" << skew
- << "takeover" << (long long) takeoverMS
- << "localTimeout" << (takeoverMS > 0);
-
- }
-
- void test(ConnectionString& hostConn, string& lockName, unsigned seed) {
- return;
- }
-
- bool run(const string&, BSONObj& cmdObj, int, string& errmsg,
- BSONObjBuilder& result, bool) {
-
- Timer t;
-
- ConnectionString hostConn(cmdObj["host"].String(),
- ConnectionString::SYNC);
-
- unsigned seed = (unsigned) number_field(cmdObj, "seed", 0);
- int numThreads = (int) number_field(cmdObj, "numThreads", 4);
- int wait = (int) number_field(cmdObj, "wait", 10000);
-
- log() << "Starting " << this->name << " with -" << endl
- << " seed: " << seed << endl
- << " numThreads: " << numThreads << endl
- << " total wait: " << wait << endl << endl;
-
- // Skew host clocks if needed
- try {
- skewClocks( hostConn, cmdObj );
- }
- catch( DBException e ) {
- errmsg = str::stream() << "Clocks could not be skewed." << causedBy( e );
- return false;
- }
-
- count = 0;
- keepGoing = true;
-
- vector<shared_ptr<boost::thread> > threads;
- vector<shared_ptr<BSONObjBuilder> > results;
- for (int i = 0; i < numThreads; i++) {
- results.push_back(shared_ptr<BSONObjBuilder> (new BSONObjBuilder()));
- threads.push_back(shared_ptr<boost::thread> (new boost::thread(
- boost::bind(&TestDistLockWithSkew::runThread, this,
- hostConn, (unsigned) i, seed + i, boost::ref(cmdObj),
- boost::ref(*(results[i].get()))))));
- }
-
- sleepsecs(wait / 1000);
- keepGoing = false;
-
- bool errors = false;
- for (unsigned i = 0; i < threads.size(); i++) {
- threads[i]->join();
- errors = errors || results[i].get()->obj()["errors"].Bool();
- }
-
- result.append("count", count);
- result.append("errors", errors);
- result.append("timeMS", t.millis());
-
- return !errors;
-
- }
-
- /**
- * Skews the clocks of a remote cluster by a particular amount, specified by
- * the "skewHosts" element in a BSONObj.
- */
- static void skewClocks( ConnectionString& cluster, BSONObj& cmdObj ) {
-
- vector<long long> skew;
- if(cmdObj.hasField("skewHosts")) {
- bsonArrToNumVector<long long>(cmdObj["skewHosts"], skew);
- }
- else {
- log( logLvl ) << "No host clocks to skew." << endl;
- return;
- }
-
- log( logLvl ) << "Skewing clocks of hosts " << cluster << endl;
-
- unsigned s = 0;
- for(vector<long long>::iterator i = skew.begin(); i != skew.end(); ++i,s++) {
-
- ConnectionString server( cluster.getServers()[s] );
- ScopedDbConnection conn( server );
-
- BSONObj result;
- try {
- bool success = conn->runCommand( string("admin"), BSON( "_skewClockCommand" << 1 << "skew" << *i ), result );
-
- uassert(13678, str::stream() << "Could not communicate with server " << server.toString() << " in cluster " << cluster.toString() << " to change skew by " << *i, success );
-
- log( logLvl + 1 ) << " Skewed host " << server << " clock by " << *i << endl;
- }
- catch(...) {
- conn.done();
- throw;
- }
-
- conn.done();
-
- }
-
- }
-
- // variables for test
- thread_specific_ptr<DistributedLock> lock;
- AtomicUInt count;
- bool keepGoing;
-
- } testDistLockWithSkewCmd;
-
-
- /**
- * Utility command to virtually skew the clock of a mongo server a particular amount.
- * This skews the clock globally, per-thread skew is also possible.
- */
- class SkewClockCommand: public Command {
- public:
- SkewClockCommand() :
- Command("_skewClockCommand") {
- }
- virtual void help(stringstream& help) const {
- help << "should not be calling this directly" << endl;
- }
-
- virtual bool slaveOk() const {
- return false;
- }
- virtual bool adminOnly() const {
- return true;
- }
- virtual LockType locktype() const {
- return NONE;
- }
-
- bool run(const string&, BSONObj& cmdObj, int, string& errmsg,
- BSONObjBuilder& result, bool) {
-
- long long skew = (long long) number_field(cmdObj, "skew", 0);
-
- log() << "Adjusting jsTime() clock skew to " << skew << endl;
-
- jsTimeVirtualSkew( skew );
-
- log() << "JSTime adjusted, now is " << jsTime() << endl;
-
- return true;
-
- }
-
- } testSkewClockCommand;
-
-}
-
diff --git a/client/examples/authTest.cpp b/client/examples/authTest.cpp
deleted file mode 100644
index 71cdd390cff..00000000000
--- a/client/examples/authTest.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-// authTest.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <iostream>
-
-#include "client/dbclient.h"
-
-using namespace mongo;
-
-int main( int argc, const char **argv ) {
-
- const char *port = "27017";
- if ( argc != 1 ) {
- if ( argc != 3 )
- throw -12;
- port = argv[ 2 ];
- }
-
- DBClientConnection conn;
- string errmsg;
- if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) {
- cout << "couldn't connect : " << errmsg << endl;
- throw -11;
- }
-
- {
- // clean up old data from any previous tests
- conn.remove( "test.system.users" , BSONObj() );
- }
-
- conn.insert( "test.system.users" , BSON( "user" << "eliot" << "pwd" << conn.createPasswordDigest( "eliot" , "bar" ) ) );
-
- errmsg.clear();
- bool ok = conn.auth( "test" , "eliot" , "bar" , errmsg );
- if ( ! ok )
- cout << errmsg << endl;
- MONGO_assert( ok );
-
- MONGO_assert( ! conn.auth( "test" , "eliot" , "bars" , errmsg ) );
-}
diff --git a/client/examples/clientTest.cpp b/client/examples/clientTest.cpp
deleted file mode 100644
index aaea6bd1bdf..00000000000
--- a/client/examples/clientTest.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-// clientTest.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * a simple test for the c++ driver
- */
-
-// this header should be first to ensure that it includes cleanly in any context
-#include "client/dbclient.h"
-
-#include <iostream>
-
-#ifndef assert
-# define assert(x) MONGO_assert(x)
-#endif
-
-using namespace std;
-using namespace mongo;
-
-int main( int argc, const char **argv ) {
-
- const char *port = "27017";
- if ( argc != 1 ) {
- if ( argc != 3 )
- throw -12;
- port = argv[ 2 ];
- }
-
- DBClientConnection conn;
- string errmsg;
- if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) {
- cout << "couldn't connect : " << errmsg << endl;
- throw -11;
- }
-
- const char * ns = "test.test1";
-
- conn.dropCollection(ns);
-
- // clean up old data from any previous tests
- conn.remove( ns, BSONObj() );
- assert( conn.findOne( ns , BSONObj() ).isEmpty() );
-
- // test insert
- conn.insert( ns ,BSON( "name" << "eliot" << "num" << 1 ) );
- assert( ! conn.findOne( ns , BSONObj() ).isEmpty() );
-
- // test remove
- conn.remove( ns, BSONObj() );
- assert( conn.findOne( ns , BSONObj() ).isEmpty() );
-
-
- // insert, findOne testing
- conn.insert( ns , BSON( "name" << "eliot" << "num" << 1 ) );
- {
- BSONObj res = conn.findOne( ns , BSONObj() );
- assert( strstr( res.getStringField( "name" ) , "eliot" ) );
- assert( ! strstr( res.getStringField( "name2" ) , "eliot" ) );
- assert( 1 == res.getIntField( "num" ) );
- }
-
-
- // cursor
- conn.insert( ns ,BSON( "name" << "sara" << "num" << 2 ) );
- {
- auto_ptr<DBClientCursor> cursor = conn.query( ns , BSONObj() );
- int count = 0;
- while ( cursor->more() ) {
- count++;
- BSONObj obj = cursor->next();
- }
- assert( count == 2 );
- }
-
- {
- auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 1 ) );
- int count = 0;
- while ( cursor->more() ) {
- count++;
- BSONObj obj = cursor->next();
- }
- assert( count == 1 );
- }
-
- {
- auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 3 ) );
- int count = 0;
- while ( cursor->more() ) {
- count++;
- BSONObj obj = cursor->next();
- }
- assert( count == 0 );
- }
-
- // update
- {
- BSONObj res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() );
- assert( ! strstr( res.getStringField( "name2" ) , "eliot" ) );
-
- BSONObj after = BSONObjBuilder().appendElements( res ).append( "name2" , "h" ).obj();
-
- conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after );
- res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() );
- assert( ! strstr( res.getStringField( "name2" ) , "eliot" ) );
- assert( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() );
-
- conn.update( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() , after );
- res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() );
- assert( strstr( res.getStringField( "name" ) , "eliot" ) );
- assert( strstr( res.getStringField( "name2" ) , "h" ) );
- assert( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() );
-
- // upsert
- conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after , 1 );
- assert( ! conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ).isEmpty() );
-
- }
-
- {
- // ensure index
- assert( conn.ensureIndex( ns , BSON( "name" << 1 ) ) );
- assert( ! conn.ensureIndex( ns , BSON( "name" << 1 ) ) );
- }
-
- {
- // hint related tests
- assert( conn.findOne(ns, "{}")["name"].str() == "sara" );
-
- assert( conn.findOne(ns, "{ name : 'eliot' }")["name"].str() == "eliot" );
- assert( conn.getLastError() == "" );
-
- // nonexistent index test
- bool asserted = false;
- try {
- conn.findOne(ns, Query("{name:\"eliot\"}").hint("{foo:1}"));
- }
- catch ( ... ) {
- asserted = true;
- }
- assert( asserted );
-
- //existing index
- assert( conn.findOne(ns, Query("{name:'eliot'}").hint("{name:1}")).hasElement("name") );
-
- // run validate
- assert( conn.validate( ns ) );
- }
-
- {
- // timestamp test
-
- const char * tsns = "test.tstest1";
- conn.dropCollection( tsns );
-
- {
- mongo::BSONObjBuilder b;
- b.appendTimestamp( "ts" );
- conn.insert( tsns , b.obj() );
- }
-
- mongo::BSONObj out = conn.findOne( tsns , mongo::BSONObj() );
- Date_t oldTime = out["ts"].timestampTime();
- unsigned int oldInc = out["ts"].timestampInc();
-
- {
- mongo::BSONObjBuilder b1;
- b1.append( out["_id"] );
-
- mongo::BSONObjBuilder b2;
- b2.append( out["_id"] );
- b2.appendTimestamp( "ts" );
-
- conn.update( tsns , b1.obj() , b2.obj() );
- }
-
- BSONObj found = conn.findOne( tsns , mongo::BSONObj() );
- cout << "old: " << out << "\nnew: " << found << endl;
- assert( ( oldTime < found["ts"].timestampTime() ) ||
- ( oldTime == found["ts"].timestampTime() && oldInc < found["ts"].timestampInc() ) );
-
- }
-
- {
- // check that killcursors doesn't affect last error
- assert( conn.getLastError().empty() );
-
- BufBuilder b;
- b.appendNum( (int)0 ); // reserved
- b.appendNum( (int)-1 ); // invalid # of cursors triggers exception
- b.appendNum( (int)-1 ); // bogus cursor id
-
- Message m;
- m.setData( dbKillCursors, b.buf(), b.len() );
-
- // say() is protected in DBClientConnection, so get superclass
- static_cast< DBConnector* >( &conn )->say( m );
-
- assert( conn.getLastError().empty() );
- }
-
- {
- list<string> l = conn.getDatabaseNames();
- for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ) {
- cout << "db name : " << *i << endl;
- }
-
- l = conn.getCollectionNames( "test" );
- for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ) {
- cout << "coll name : " << *i << endl;
- }
- }
-
- {
- //Map Reduce (this mostly just tests that it compiles with all output types)
- const string ns = "test.mr";
- conn.insert(ns, BSON("a" << 1));
- conn.insert(ns, BSON("a" << 1));
-
- const char* map = "function() { emit(this.a, 1); }";
- const char* reduce = "function(key, values) { return Array.sum(values); }";
-
- const string outcoll = ns + ".out";
-
- BSONObj out;
- out = conn.mapreduce(ns, map, reduce, BSONObj()); // default to inline
- //MONGO_PRINT(out);
- out = conn.mapreduce(ns, map, reduce, BSONObj(), outcoll);
- //MONGO_PRINT(out);
- out = conn.mapreduce(ns, map, reduce, BSONObj(), outcoll.c_str());
- //MONGO_PRINT(out);
- out = conn.mapreduce(ns, map, reduce, BSONObj(), BSON("reduce" << outcoll));
- //MONGO_PRINT(out);
- }
-
- {
- // test timeouts
-
- DBClientConnection conn( true , 0 , 2 );
- if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) {
- cout << "couldn't connect : " << errmsg << endl;
- throw -11;
- }
- conn.insert( "test.totest" , BSON( "x" << 1 ) );
- BSONObj res;
-
- bool gotError = false;
- assert( conn.eval( "test" , "return db.totest.findOne().x" , res ) );
- try {
- conn.eval( "test" , "sleep(5000); return db.totest.findOne().x" , res );
- }
- catch ( std::exception& e ) {
- gotError = true;
- log() << e.what() << endl;
- }
- assert( gotError );
- // sleep so the server isn't locked anymore
- sleepsecs( 4 );
-
- assert( conn.eval( "test" , "return db.totest.findOne().x" , res ) );
-
-
- }
-
- cout << "client test finished!" << endl;
-}
diff --git a/client/examples/first.cpp b/client/examples/first.cpp
deleted file mode 100644
index ab5efb325f5..00000000000
--- a/client/examples/first.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-// first.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * this is a good first example of how to use mongo from c++
- */
-
-#include <iostream>
-
-#include "client/dbclient.h"
-
-using namespace std;
-
-void insert( mongo::DBClientConnection & conn , const char * name , int num ) {
- mongo::BSONObjBuilder obj;
- obj.append( "name" , name );
- obj.append( "num" , num );
- conn.insert( "test.people" , obj.obj() );
-}
-
-int main( int argc, const char **argv ) {
-
- const char *port = "27017";
- if ( argc != 1 ) {
- if ( argc != 3 )
- throw -12;
- port = argv[ 2 ];
- }
-
- mongo::DBClientConnection conn;
- string errmsg;
- if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) {
- cout << "couldn't connect : " << errmsg << endl;
- throw -11;
- }
-
- {
- // clean up old data from any previous tests
- mongo::BSONObjBuilder query;
- conn.remove( "test.people" , query.obj() );
- }
-
- insert( conn , "eliot" , 15 );
- insert( conn , "sara" , 23 );
-
- {
- mongo::BSONObjBuilder query;
- auto_ptr<mongo::DBClientCursor> cursor = conn.query( "test.people" , query.obj() );
- cout << "using cursor" << endl;
- while ( cursor->more() ) {
- mongo::BSONObj obj = cursor->next();
- cout << "\t" << obj.jsonString() << endl;
- }
-
- }
-
- {
- mongo::BSONObjBuilder query;
- query.append( "name" , "eliot" );
- mongo::BSONObj res = conn.findOne( "test.people" , query.obj() );
- cout << res.isEmpty() << "\t" << res.jsonString() << endl;
- }
-
- {
- mongo::BSONObjBuilder query;
- query.append( "name" , "asd" );
- mongo::BSONObj res = conn.findOne( "test.people" , query.obj() );
- cout << res.isEmpty() << "\t" << res.jsonString() << endl;
- }
-
-
-}
diff --git a/client/examples/httpClientTest.cpp b/client/examples/httpClientTest.cpp
deleted file mode 100644
index 4055d4492d5..00000000000
--- a/client/examples/httpClientTest.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-// httpClientTest.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <iostream>
-
-#include "client/dbclient.h"
-#include "util/net/httpclient.h"
-
-using namespace mongo;
-
-void play( string url ) {
- cout << "[" << url << "]" << endl;
-
- HttpClient c;
- HttpClient::Result r;
- MONGO_assert( c.get( url , &r ) == 200 );
-
- HttpClient::Headers h = r.getHeaders();
- MONGO_assert( h["Content-Type"].find( "text/html" ) == 0 );
-
- cout << "\tHeaders" << endl;
- for ( HttpClient::Headers::iterator i = h.begin() ; i != h.end(); ++i ) {
- cout << "\t\t" << i->first << "\t" << i->second << endl;
- }
-
-}
-
-int main( int argc, const char **argv ) {
-
- int port = 27017;
- if ( argc != 1 ) {
- if ( argc != 3 )
- throw -12;
- port = atoi( argv[ 2 ] );
- }
- port += 1000;
-
- play( str::stream() << "http://localhost:" << port << "/" );
-
-#ifdef MONGO_SSL
- play( "https://www.10gen.com/" );
-#endif
-
-}
diff --git a/client/examples/insert_demo.cpp b/client/examples/insert_demo.cpp
deleted file mode 100644
index 14ac79ee1a0..00000000000
--- a/client/examples/insert_demo.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- C++ client program which inserts documents in a MongoDB database.
-
- How to build and run:
-
- Using mongo_client_lib.cpp:
- g++ -I .. -I ../.. insert_demo.cpp ../mongo_client_lib.cpp -lboost_thread-mt -lboost_filesystem
- ./a.out
-*/
-
-#include <iostream>
-#include "dbclient.h" // the mongo c++ driver
-
-using namespace std;
-using namespace mongo;
-using namespace bson;
-
-int main() {
- try {
- cout << "connecting to localhost..." << endl;
- DBClientConnection c;
- c.connect("localhost");
- cout << "connected ok" << endl;
-
- bo o = BSON( "hello" << "world" );
-
- cout << "inserting..." << endl;
-
- time_t start = time(0);
- for( unsigned i = 0; i < 1000000; i++ ) {
- c.insert("test.foo", o);
- }
-
- // wait until all operations applied
- cout << "getlasterror returns: \"" << c.getLastError() << '"' << endl;
-
- time_t done = time(0);
- time_t dt = done-start;
- cout << dt << " seconds " << 1000000/dt << " per second" << endl;
- }
- catch(DBException& e) {
- cout << "caught DBException " << e.toString() << endl;
- return 1;
- }
-
- return 0;
-}
diff --git a/client/examples/rs.cpp b/client/examples/rs.cpp
deleted file mode 100644
index 3307d87b56b..00000000000
--- a/client/examples/rs.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-// rs.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * example of using replica sets from c++
- */
-
-#include "client/dbclient.h"
-#include <iostream>
-#include <vector>
-
-using namespace mongo;
-using namespace std;
-
-void workerThread( string collName , bool print , DBClientReplicaSet * conn ) {
-
- while ( true ) {
- try {
- conn->update( collName , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) , true );
-
- BSONObj x = conn->findOne( collName , BSONObj() );
-
- if ( print ) {
- cout << x << endl;
- }
-
- BSONObj a = conn->slaveConn().findOne( collName , BSONObj() , 0 , QueryOption_SlaveOk );
- BSONObj b = conn->findOne( collName , BSONObj() , 0 , QueryOption_SlaveOk );
-
- if ( print ) {
- cout << "\t A " << a << endl;
- cout << "\t B " << b << endl;
- }
- }
- catch ( std::exception& e ) {
- cout << "ERROR: " << e.what() << endl;
- }
- sleepmillis( 10 );
- }
-}
-
-int main( int argc , const char ** argv ) {
-
- unsigned nThreads = 1;
- bool print = false;
- bool testTimeout = false;
-
- for ( int i=1; i<argc; i++ ) {
- if ( mongoutils::str::equals( "--threads" , argv[i] ) ) {
- nThreads = atoi( argv[++i] );
- }
- else if ( mongoutils::str::equals( "--print" , argv[i] ) ) {
- print = true;
- }
- // Run a special mode to demonstrate the DBClientReplicaSet so_timeout option.
- else if ( mongoutils::str::equals( "--testTimeout" , argv[i] ) ) {
- testTimeout = true;
- }
- else {
- cerr << "unknown option: " << argv[i] << endl;
- return 1;
- }
-
- }
-
- string errmsg;
- ConnectionString cs = ConnectionString::parse( "foo/127.0.0.1" , errmsg );
- if ( ! cs.isValid() ) {
- cout << "error parsing url: " << errmsg << endl;
- return 1;
- }
-
- DBClientReplicaSet * conn = dynamic_cast<DBClientReplicaSet*>(cs.connect( errmsg, testTimeout ? 10 : 0 ));
- if ( ! conn ) {
- cout << "error connecting: " << errmsg << endl;
- return 2;
- }
-
- string collName = "test.rs1";
-
- conn->dropCollection( collName );
-
- if ( testTimeout ) {
- conn->insert( collName, BSONObj() );
- try {
- conn->count( collName, BSON( "$where" << "sleep(40000)" ) );
- } catch( DBException& ) {
- return 0;
- }
- cout << "expected socket exception" << endl;
- return 1;
- }
-
- vector<boost::shared_ptr<boost::thread> > threads;
- for ( unsigned i=0; i<nThreads; i++ ) {
- string errmsg;
- threads.push_back( boost::shared_ptr<boost::thread>( new boost::thread( boost::bind( workerThread , collName , print , (DBClientReplicaSet*)cs.connect(errmsg) ) ) ) );
- }
-
- for ( unsigned i=0; i<threads.size(); i++ ) {
- threads[i]->join();
- }
-
-}
diff --git a/client/examples/second.cpp b/client/examples/second.cpp
deleted file mode 100644
index 6cc2111580f..00000000000
--- a/client/examples/second.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-// second.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <iostream>
-
-#include "client/dbclient.h"
-
-using namespace std;
-using namespace mongo;
-
-int main( int argc, const char **argv ) {
-
- const char *port = "27017";
- if ( argc != 1 ) {
- if ( argc != 3 )
- throw -12;
- port = argv[ 2 ];
- }
-
- DBClientConnection conn;
- string errmsg;
- if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) {
- cout << "couldn't connect : " << errmsg << endl;
- throw -11;
- }
-
- const char * ns = "test.second";
-
- conn.remove( ns , BSONObj() );
-
- conn.insert( ns , BSON( "name" << "eliot" << "num" << 17 ) );
- conn.insert( ns , BSON( "name" << "sara" << "num" << 24 ) );
-
- auto_ptr<DBClientCursor> cursor = conn.query( ns , BSONObj() );
- cout << "using cursor" << endl;
- while ( cursor->more() ) {
- BSONObj obj = cursor->next();
- cout << "\t" << obj.jsonString() << endl;
- }
-
- conn.ensureIndex( ns , BSON( "name" << 1 << "num" << -1 ) );
-}
diff --git a/client/examples/simple_client_demo.vcxproj b/client/examples/simple_client_demo.vcxproj
deleted file mode 100755
index 4658a42900f..00000000000
--- a/client/examples/simple_client_demo.vcxproj
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{89C30BC3-2874-4F2C-B4DA-EB04E9782236}</ProjectGuid>
- <Keyword>Win32Proj</Keyword>
- <RootNamespace>simple_client_demo</RootNamespace>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <LinkIncremental>true</LinkIncremental>
- <IncludePath>..\..;..\..\pcre-7.4;$(IncludePath)</IncludePath>
- <LibraryPath>\boost\lib\vs2010_32;$(LibraryPath)</LibraryPath>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <LinkIncremental>false</LinkIncremental>
- <IncludePath>..\..;..\..\pcre-7.4;$(IncludePath)</IncludePath>
- <LibraryPath>\boost\lib\vs2010_32;$(LibraryPath)</LibraryPath>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <ClCompile>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions> _CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>c:\boost;\boost</AdditionalIncludeDirectories>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions> _CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <AdditionalIncludeDirectories>c:\boost;\boost</AdditionalIncludeDirectories>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <ClCompile Include="..\mongo_client_lib.cpp" />
- <ClCompile Include="..\simple_client_demo.cpp" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
-</Project> \ No newline at end of file
diff --git a/client/examples/simple_client_demo.vcxproj.filters b/client/examples/simple_client_demo.vcxproj.filters
deleted file mode 100755
index d6580c32279..00000000000
--- a/client/examples/simple_client_demo.vcxproj.filters
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup>
- <Filter Include="Source Files">
- <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
- <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
- </Filter>
- <Filter Include="Header Files">
- <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
- <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
- </Filter>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="..\simple_client_demo.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="..\mongo_client_lib.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- </ItemGroup>
-</Project> \ No newline at end of file
diff --git a/client/examples/tail.cpp b/client/examples/tail.cpp
deleted file mode 100644
index 90e62d279c1..00000000000
--- a/client/examples/tail.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-// tail.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* example of using a tailable cursor */
-
-#include "../../client/dbclient.h"
-#include "../../util/goodies.h"
-
-using namespace mongo;
-
-void tail(DBClientBase& conn, const char *ns) {
- BSONElement lastId = minKey.firstElement();
- Query query = Query();
-
- auto_ptr<DBClientCursor> c =
- conn.query(ns, query, 0, 0, 0, QueryOption_CursorTailable);
-
- while( 1 ) {
- if( !c->more() ) {
- if( c->isDead() ) {
- break; // we need to requery
- }
-
- // all data (so far) exhausted, wait for more
- sleepsecs(1);
- continue;
- }
- BSONObj o = c->next();
- lastId = o["_id"];
- cout << o.toString() << endl;
- }
-}
diff --git a/client/examples/tutorial.cpp b/client/examples/tutorial.cpp
deleted file mode 100644
index 3cdf3593cd8..00000000000
--- a/client/examples/tutorial.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-//tutorial.cpp
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <iostream>
-#include "../../client/dbclient.h"
-
-// g++ tutorial.cpp -lmongoclient -lboost_thread -lboost_filesystem -o tutorial
-
-using namespace mongo;
-
-void printIfAge(DBClientConnection& c, int age) {
- auto_ptr<DBClientCursor> cursor = c.query("tutorial.persons", QUERY( "age" << age ).sort("name") );
- while( cursor->more() ) {
- BSONObj p = cursor->next();
- cout << p.getStringField("name") << endl;
- }
-}
-
-void run() {
- DBClientConnection c;
- c.connect("localhost"); //"192.168.58.1");
- cout << "connected ok" << endl;
- BSONObj p = BSON( "name" << "Joe" << "age" << 33 );
- c.insert("tutorial.persons", p);
- p = BSON( "name" << "Jane" << "age" << 40 );
- c.insert("tutorial.persons", p);
- p = BSON( "name" << "Abe" << "age" << 33 );
- c.insert("tutorial.persons", p);
- p = BSON( "name" << "Samantha" << "age" << 21 << "city" << "Los Angeles" << "state" << "CA" );
- c.insert("tutorial.persons", p);
-
- c.ensureIndex("tutorial.persons", fromjson("{age:1}"));
-
- cout << "count:" << c.count("tutorial.persons") << endl;
-
- auto_ptr<DBClientCursor> cursor = c.query("tutorial.persons", BSONObj());
- while( cursor->more() ) {
- cout << cursor->next().toString() << endl;
- }
-
- cout << "\nprintifage:\n";
- printIfAge(c, 33);
-}
-
-int main() {
- try {
- run();
- }
- catch( DBException &e ) {
- cout << "caught " << e.what() << endl;
- }
- return 0;
-}
diff --git a/client/examples/whereExample.cpp b/client/examples/whereExample.cpp
deleted file mode 100644
index 12b68d7add3..00000000000
--- a/client/examples/whereExample.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-// @file whereExample.cpp
-// @see http://www.mongodb.org/display/DOCS/Server-side+Code+Execution
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <iostream>
-
-#include "client/dbclient.h"
-
-using namespace std;
-using namespace mongo;
-
-int main( int argc, const char **argv ) {
-
- const char *port = "27017";
- if ( argc != 1 ) {
- if ( argc != 3 )
- throw -12;
- port = argv[ 2 ];
- }
-
- DBClientConnection conn;
- string errmsg;
- if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) {
- cout << "couldn't connect : " << errmsg << endl;
- throw -11;
- }
-
- const char * ns = "test.where";
-
- conn.remove( ns , BSONObj() );
-
- conn.insert( ns , BSON( "name" << "eliot" << "num" << 17 ) );
- conn.insert( ns , BSON( "name" << "sara" << "num" << 24 ) );
-
- auto_ptr<DBClientCursor> cursor = conn.query( ns , BSONObj() );
-
- while ( cursor->more() ) {
- BSONObj obj = cursor->next();
- cout << "\t" << obj.jsonString() << endl;
- }
-
- cout << "now using $where" << endl;
-
- Query q = Query("{}").where("this.name == name" , BSON( "name" << "sara" ));
-
- cursor = conn.query( ns , q );
-
- int num = 0;
- while ( cursor->more() ) {
- BSONObj obj = cursor->next();
- cout << "\t" << obj.jsonString() << endl;
- num++;
- }
- MONGO_assert( num == 1 );
-}
diff --git a/client/gridfs.cpp b/client/gridfs.cpp
deleted file mode 100644
index 233724ae8e5..00000000000
--- a/client/gridfs.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-// gridfs.cpp
-
-/* Copyright 2009 10gen
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "pch.h"
-#include <fcntl.h>
-#include <utility>
-
-#include "gridfs.h"
-#include <boost/smart_ptr.hpp>
-
-#if defined(_WIN32)
-#include <io.h>
-#endif
-
-#ifndef MIN
-#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
-#endif
-
-namespace mongo {
-
- const unsigned DEFAULT_CHUNK_SIZE = 256 * 1024;
-
- GridFSChunk::GridFSChunk( BSONObj o ) {
- _data = o;
- }
-
- GridFSChunk::GridFSChunk( BSONObj fileObject , int chunkNumber , const char * data , int len ) {
- BSONObjBuilder b;
- b.appendAs( fileObject["_id"] , "files_id" );
- b.append( "n" , chunkNumber );
- b.appendBinData( "data" , len, BinDataGeneral, data );
- _data = b.obj();
- }
-
-
- GridFS::GridFS( DBClientBase& client , const string& dbName , const string& prefix ) : _client( client ) , _dbName( dbName ) , _prefix( prefix ) {
- _filesNS = dbName + "." + prefix + ".files";
- _chunksNS = dbName + "." + prefix + ".chunks";
- _chunkSize = DEFAULT_CHUNK_SIZE;
-
- client.ensureIndex( _filesNS , BSON( "filename" << 1 ) );
- client.ensureIndex( _chunksNS , BSON( "files_id" << 1 << "n" << 1 ) );
- }
-
- GridFS::~GridFS() {
-
- }
-
- void GridFS::setChunkSize(unsigned int size) {
- massert( 13296 , "invalid chunk size is specified", (size == 0));
- _chunkSize = size;
- }
-
- BSONObj GridFS::storeFile( const char* data , size_t length , const string& remoteName , const string& contentType) {
- char const * const end = data + length;
-
- OID id;
- id.init();
- BSONObj idObj = BSON("_id" << id);
-
- int chunkNumber = 0;
- while (data < end) {
- int chunkLen = MIN(_chunkSize, (unsigned)(end-data));
- GridFSChunk c(idObj, chunkNumber, data, chunkLen);
- _client.insert( _chunksNS.c_str() , c._data );
-
- chunkNumber++;
- data += chunkLen;
- }
-
- return insertFile(remoteName, id, length, contentType);
- }
-
-
- BSONObj GridFS::storeFile( const string& fileName , const string& remoteName , const string& contentType) {
- uassert( 10012 , "file doesn't exist" , fileName == "-" || boost::filesystem::exists( fileName ) );
-
- FILE* fd;
- if (fileName == "-")
- fd = stdin;
- else
- fd = fopen( fileName.c_str() , "rb" );
- uassert( 10013 , "error opening file", fd);
-
- OID id;
- id.init();
- BSONObj idObj = BSON("_id" << id);
-
- int chunkNumber = 0;
- gridfs_offset length = 0;
- while (!feof(fd)) {
- //boost::scoped_array<char>buf (new char[_chunkSize+1]);
- char * buf = new char[_chunkSize+1];
- char* bufPos = buf;//.get();
- unsigned int chunkLen = 0; // how much in the chunk now
- while(chunkLen != _chunkSize && !feof(fd)) {
- int readLen = fread(bufPos, 1, _chunkSize - chunkLen, fd);
- chunkLen += readLen;
- bufPos += readLen;
-
- assert(chunkLen <= _chunkSize);
- }
-
- GridFSChunk c(idObj, chunkNumber, buf, chunkLen);
- _client.insert( _chunksNS.c_str() , c._data );
-
- length += chunkLen;
- chunkNumber++;
- delete[] buf;
- }
-
- if (fd != stdin)
- fclose( fd );
-
- return insertFile((remoteName.empty() ? fileName : remoteName), id, length, contentType);
- }
-
- BSONObj GridFS::insertFile(const string& name, const OID& id, gridfs_offset length, const string& contentType) {
-
- BSONObj res;
- if ( ! _client.runCommand( _dbName.c_str() , BSON( "filemd5" << id << "root" << _prefix ) , res ) )
- throw UserException( 9008 , "filemd5 failed" );
-
- BSONObjBuilder file;
- file << "_id" << id
- << "filename" << name
- << "chunkSize" << _chunkSize
- << "uploadDate" << DATENOW
- << "md5" << res["md5"]
- ;
-
- if (length < 1024*1024*1024) { // 2^30
- file << "length" << (int) length;
- }
- else {
- file << "length" << (long long) length;
- }
-
- if (!contentType.empty())
- file << "contentType" << contentType;
-
- BSONObj ret = file.obj();
- _client.insert(_filesNS.c_str(), ret);
-
- return ret;
- }
-
- void GridFS::removeFile( const string& fileName ) {
- auto_ptr<DBClientCursor> files = _client.query( _filesNS , BSON( "filename" << fileName ) );
- while (files->more()) {
- BSONObj file = files->next();
- BSONElement id = file["_id"];
- _client.remove( _filesNS.c_str() , BSON( "_id" << id ) );
- _client.remove( _chunksNS.c_str() , BSON( "files_id" << id ) );
- }
- }
-
- GridFile::GridFile( GridFS * grid , BSONObj obj ) {
- _grid = grid;
- _obj = obj;
- }
-
- GridFile GridFS::findFile( const string& fileName ) {
- return findFile( BSON( "filename" << fileName ) );
- };
-
- GridFile GridFS::findFile( BSONObj query ) {
- query = BSON("query" << query << "orderby" << BSON("uploadDate" << -1));
- return GridFile( this , _client.findOne( _filesNS.c_str() , query ) );
- }
-
- auto_ptr<DBClientCursor> GridFS::list() {
- return _client.query( _filesNS.c_str() , BSONObj() );
- }
-
- auto_ptr<DBClientCursor> GridFS::list( BSONObj o ) {
- return _client.query( _filesNS.c_str() , o );
- }
-
- BSONObj GridFile::getMetadata() {
- BSONElement meta_element = _obj["metadata"];
- if( meta_element.eoo() ) {
- return BSONObj();
- }
-
- return meta_element.embeddedObject();
- }
-
- GridFSChunk GridFile::getChunk( int n ) {
- _exists();
- BSONObjBuilder b;
- b.appendAs( _obj["_id"] , "files_id" );
- b.append( "n" , n );
-
- BSONObj o = _grid->_client.findOne( _grid->_chunksNS.c_str() , b.obj() );
- uassert( 10014 , "chunk is empty!" , ! o.isEmpty() );
- return GridFSChunk(o);
- }
-
- gridfs_offset GridFile::write( ostream & out ) {
- _exists();
-
- const int num = getNumChunks();
-
- for ( int i=0; i<num; i++ ) {
- GridFSChunk c = getChunk( i );
-
- int len;
- const char * data = c.data( len );
- out.write( data , len );
- }
-
- return getContentLength();
- }
-
- gridfs_offset GridFile::write( const string& where ) {
- if (where == "-") {
- return write( cout );
- }
- else {
- ofstream out(where.c_str() , ios::out | ios::binary );
- uassert(13325, "couldn't open file: " + where, out.is_open() );
- return write( out );
- }
- }
-
- void GridFile::_exists() {
- uassert( 10015 , "doesn't exists" , exists() );
- }
-
-}
diff --git a/client/gridfs.h b/client/gridfs.h
deleted file mode 100644
index b52cf75117a..00000000000
--- a/client/gridfs.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/** @file gridfs.h */
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "dbclient.h"
-#include "redef_macros.h"
-
-namespace mongo {
-
- typedef unsigned long long gridfs_offset;
-
- class GridFS;
- class GridFile;
-
- class GridFSChunk {
- public:
- GridFSChunk( BSONObj data );
- GridFSChunk( BSONObj fileId , int chunkNumber , const char * data , int len );
-
- int len() {
- int len;
- _data["data"].binDataClean( len );
- return len;
- }
-
- const char * data( int & len ) {
- return _data["data"].binDataClean( len );
- }
-
- private:
- BSONObj _data;
- friend class GridFS;
- };
-
-
- /**
- GridFS is for storing large file-style objects in MongoDB.
- @see http://www.mongodb.org/display/DOCS/GridFS+Specification
- */
- class GridFS {
- public:
- /**
- * @param client - db connection
- * @param dbName - root database name
- * @param prefix - if you want your data somewhere besides <dbname>.fs
- */
- GridFS( DBClientBase& client , const string& dbName , const string& prefix="fs" );
- ~GridFS();
-
- /**
- * @param
- */
- void setChunkSize(unsigned int size);
-
- /**
- * puts the file reference by fileName into the db
- * @param fileName local filename relative to process
- * @param remoteName optional filename to use for file stored in GridFS
- * (default is to use fileName parameter)
- * @param contentType optional MIME type for this object.
- * (default is to omit)
- * @return the file object
- */
- BSONObj storeFile( const string& fileName , const string& remoteName="" , const string& contentType="");
-
- /**
- * puts the file represented by data into the db
- * @param data pointer to buffer to store in GridFS
- * @param length length of buffer
- * @param remoteName optional filename to use for file stored in GridFS
- * (default is to use fileName parameter)
- * @param contentType optional MIME type for this object.
- * (default is to omit)
- * @return the file object
- */
- BSONObj storeFile( const char* data , size_t length , const string& remoteName , const string& contentType="");
-
- /**
- * removes file referenced by fileName from the db
- * @param fileName filename (in GridFS) of the file to remove
- * @return the file object
- */
- void removeFile( const string& fileName );
-
- /**
- * returns a file object matching the query
- */
- GridFile findFile( BSONObj query );
-
- /**
- * equiv to findFile( { filename : filename } )
- */
- GridFile findFile( const string& fileName );
-
- /**
- * convenience method to get all the files
- */
- auto_ptr<DBClientCursor> list();
-
- /**
- * convenience method to get all the files with a filter
- */
- auto_ptr<DBClientCursor> list( BSONObj query );
-
- private:
- DBClientBase& _client;
- string _dbName;
- string _prefix;
- string _filesNS;
- string _chunksNS;
- unsigned int _chunkSize;
-
- // insert fileobject. All chunks must be in DB.
- BSONObj insertFile(const string& name, const OID& id, gridfs_offset length, const string& contentType);
-
- friend class GridFile;
- };
-
- /**
- wrapper for a file stored in the Mongo database
- */
- class GridFile {
- public:
- /**
- * @return whether or not this file exists
- * findFile will always return a GriFile, so need to check this
- */
- bool exists() {
- return ! _obj.isEmpty();
- }
-
- string getFilename() {
- return _obj["filename"].str();
- }
-
- int getChunkSize() {
- return (int)(_obj["chunkSize"].number());
- }
-
- gridfs_offset getContentLength() {
- return (gridfs_offset)(_obj["length"].number());
- }
-
- string getContentType() {
- return _obj["contentType"].valuestr();
- }
-
- Date_t getUploadDate() {
- return _obj["uploadDate"].date();
- }
-
- string getMD5() {
- return _obj["md5"].str();
- }
-
- BSONElement getFileField( const string& name ) {
- return _obj[name];
- }
-
- BSONObj getMetadata();
-
- int getNumChunks() {
- return (int) ceil( (double)getContentLength() / (double)getChunkSize() );
- }
-
- GridFSChunk getChunk( int n );
-
- /**
- write the file to the output stream
- */
- gridfs_offset write( ostream & out );
-
- /**
- write the file to this filename
- */
- gridfs_offset write( const string& where );
-
- private:
- GridFile( GridFS * grid , BSONObj obj );
-
- void _exists();
-
- GridFS * _grid;
- BSONObj _obj;
-
- friend class GridFS;
- };
-}
-
-#include "undef_macros.h"
diff --git a/client/model.cpp b/client/model.cpp
deleted file mode 100644
index bd10a3c5528..00000000000
--- a/client/model.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-// model.cpp
-
-/* Copyright 2009 10gen
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "pch.h"
-#include "model.h"
-#include "connpool.h"
-
-namespace mongo {
-
- bool Model::load(BSONObj& query) {
- ScopedDbConnection conn( modelServer() );
-
- BSONObj b = conn->findOne(getNS(), query);
- conn.done();
-
- if ( b.isEmpty() )
- return false;
-
- unserialize(b);
- _id = b["_id"].wrap().getOwned();
- return true;
- }
-
- void Model::remove( bool safe ) {
- uassert( 10016 , "_id isn't set - needed for remove()" , _id["_id"].type() );
-
- ScopedDbConnection conn( modelServer() );
- conn->remove( getNS() , _id );
-
- string errmsg = "";
- if ( safe )
- errmsg = conn->getLastError();
-
- conn.done();
-
- if ( safe && errmsg.size() )
- throw UserException( 9002 , (string)"error on Model::remove: " + errmsg );
- }
-
- void Model::save( bool safe ) {
- ScopedDbConnection conn( modelServer() );
-
- BSONObjBuilder b;
- serialize( b );
-
- BSONElement myId;
- {
- BSONObjIterator i = b.iterator();
- while ( i.more() ) {
- BSONElement e = i.next();
- if ( strcmp( e.fieldName() , "_id" ) == 0 ) {
- myId = e;
- break;
- }
- }
- }
-
- if ( myId.type() ) {
- if ( _id.isEmpty() ) {
- _id = myId.wrap();
- }
- else if ( myId.woCompare( _id.firstElement() ) ) {
- stringstream ss;
- ss << "_id from serialize and stored differ: ";
- ss << '[' << myId << "] != ";
- ss << '[' << _id.firstElement() << ']';
- throw UserException( 13121 , ss.str() );
- }
- }
-
- if ( _id.isEmpty() ) {
- OID oid;
- oid.init();
- b.appendOID( "_id" , &oid );
-
- BSONObj o = b.obj();
- conn->insert( getNS() , o );
- _id = o["_id"].wrap().getOwned();
-
- log(4) << "inserted new model " << getNS() << " " << o << endl;
- }
- else {
- if ( myId.eoo() ) {
- myId = _id["_id"];
- b.append( myId );
- }
-
- assert( ! myId.eoo() );
-
- BSONObjBuilder qb;
- qb.append( myId );
-
- BSONObj q = qb.obj();
- BSONObj o = b.obj();
-
- log(4) << "updated model" << getNS() << " " << q << " " << o << endl;
-
- conn->update( getNS() , q , o , true );
-
- }
-
- string errmsg = "";
- if ( safe )
- errmsg = conn->getLastError();
-
- conn.done();
-
- if ( safe && errmsg.size() )
- throw UserException( 9003 , (string)"error on Model::save: " + errmsg );
- }
-
- BSONObj Model::toObject() {
- BSONObjBuilder b;
- serialize( b );
- return b.obj();
- }
-
- void Model::append( const char * name , BSONObjBuilder& b ) {
- BSONObjBuilder bb( b.subobjStart( name ) );
- serialize( bb );
- bb.done();
- }
-
-} // namespace mongo
diff --git a/client/model.h b/client/model.h
deleted file mode 100644
index 7dd31434f49..00000000000
--- a/client/model.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/** @file model.h */
-
-/* Copyright 2009 10gen
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "dbclient.h"
-#include "redef_macros.h"
-
-namespace mongo {
-
- /** Model is a base class for defining objects which are serializable to the Mongo
- database via the database driver.
-
- Definition
- Your serializable class should inherit from Model and implement the abstract methods
- below.
-
- Loading
- To load, first construct an (empty) object. Then call load(). Do not load an object
- more than once.
- */
- class Model {
- public:
- Model() { }
- virtual ~Model() { }
-
- virtual const char * getNS() = 0;
- virtual void serialize(BSONObjBuilder& to) = 0;
- virtual void unserialize(const BSONObj& from) = 0;
- virtual BSONObj toObject();
- virtual void append( const char * name , BSONObjBuilder& b );
-
- virtual string modelServer() = 0;
-
- /** Load a single object.
- @return true if successful.
- */
- virtual bool load(BSONObj& query);
- virtual void save( bool safe=false );
- virtual void remove( bool safe=false );
-
- protected:
- BSONObj _id;
- };
-
-} // namespace mongo
-
-#include "undef_macros.h"
diff --git a/client/mongo_client_lib.cpp b/client/mongo_client_lib.cpp
deleted file mode 100644
index 8100d719e81..00000000000
--- a/client/mongo_client_lib.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/* @file client_lib.cpp
-
- MongoDB C++ Driver
-
- Normally one includes dbclient.h, and links against libmongoclient.a, when connecting to MongoDB
- from C++. However, if you have a situation where the pre-built library does not work, you can use
- this file instead to build all the necessary symbols. To do so, include mongo_client_lib.cpp in your
- project.
-
- GCC
- ---
- For example, to build and run simple_client_demo.cpp with GCC and run it:
-
- g++ -I .. simple_client_demo.cpp mongo_client_lib.cpp -lboost_thread-mt -lboost_filesystem
- ./a.out
-
- Visual Studio (2010 tested)
- ---------------------------
- First, see client/examples/simple_client_demo.vcxproj.
- - Be sure to include your boost include directory in your project as an Additional Include Directory.
- - Define _CRT_SECURE_NO_WARNINGS to avoid warnings on use of strncpy and such by the MongoDB client code.
- - Include the boost libraries directory.
- - Linker.Input.Additional Dependencies - add ws2_32.lib for the Winsock library.
-*/
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if defined(_WIN32)
-// C4800 forcing value to bool 'true' or 'false' (performance warning)
-#pragma warning( disable : 4800 )
-#endif
-
-#include "../util/md5main.cpp"
-
-#define MONGO_EXPOSE_MACROS
-#include "../pch.h"
-
-#include "../util/assert_util.cpp"
-#include "../util/net/message.cpp"
-#include "../util/util.cpp"
-#include "../util/background.cpp"
-#include "../util/base64.cpp"
-#include "../util/net/sock.cpp"
-#include "../util/log.cpp"
-#include "../util/password.cpp"
-#include "../util/net/message_port.cpp"
-
-#include "../util/concurrency/thread_pool.cpp"
-#include "../util/concurrency/vars.cpp"
-#include "../util/concurrency/task.cpp"
-#include "../util/concurrency/spin_lock.cpp"
-
-#include "connpool.cpp"
-#include "syncclusterconnection.cpp"
-#include "dbclient.cpp"
-#include "clientOnly.cpp"
-#include "gridfs.cpp"
-#include "dbclientcursor.cpp"
-
-#include "../util/text.cpp"
-#include "dbclient_rs.cpp"
-#include "../bson/oid.cpp"
-
-#include "../db/lasterror.cpp"
-#include "../db/json.cpp"
-#include "../db/jsobj.cpp"
-//#include "../db/common.cpp"
-#include "../db/nonce.cpp"
-#include "../db/commands.cpp"
-
-#include "../pch.cpp"
-
-extern "C" {
-#include "../util/md5.c"
-}
-
diff --git a/client/parallel.cpp b/client/parallel.cpp
deleted file mode 100644
index df2f3fcfc5b..00000000000
--- a/client/parallel.cpp
+++ /dev/null
@@ -1,804 +0,0 @@
-// parallel.cpp
-/*
- * Copyright 2010 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#include "pch.h"
-#include "parallel.h"
-#include "connpool.h"
-#include "../db/queryutil.h"
-#include "../db/dbmessage.h"
-#include "../s/util.h"
-#include "../s/shard.h"
-
-namespace mongo {
-
- // -------- ClusteredCursor -----------
-
- ClusteredCursor::ClusteredCursor( QueryMessage& q ) {
- _ns = q.ns;
- _query = q.query.copy();
- _options = q.queryOptions;
- _fields = q.fields.copy();
- _batchSize = q.ntoreturn;
- if ( _batchSize == 1 )
- _batchSize = 2;
-
- _done = false;
- _didInit = false;
- }
-
- ClusteredCursor::ClusteredCursor( const string& ns , const BSONObj& q , int options , const BSONObj& fields ) {
- _ns = ns;
- _query = q.getOwned();
- _options = options;
- _fields = fields.getOwned();
- _batchSize = 0;
-
- _done = false;
- _didInit = false;
- }
-
- ClusteredCursor::~ClusteredCursor() {
- _done = true; // just in case
- }
-
- void ClusteredCursor::init() {
- if ( _didInit )
- return;
- _didInit = true;
- _init();
- }
-
- void ClusteredCursor::_checkCursor( DBClientCursor * cursor ) {
- assert( cursor );
-
- if ( cursor->hasResultFlag( ResultFlag_ShardConfigStale ) ) {
- throw StaleConfigException( _ns , "ClusteredCursor::_checkCursor" );
- }
-
- if ( cursor->hasResultFlag( ResultFlag_ErrSet ) ) {
- BSONObj o = cursor->next();
- throw UserException( o["code"].numberInt() , o["$err"].String() );
- }
- }
-
- auto_ptr<DBClientCursor> ClusteredCursor::query( const string& server , int num , BSONObj extra , int skipLeft , bool lazy ) {
- uassert( 10017 , "cursor already done" , ! _done );
- assert( _didInit );
-
- BSONObj q = _query;
- if ( ! extra.isEmpty() ) {
- q = concatQuery( q , extra );
- }
-
- try {
- ShardConnection conn( server , _ns );
-
- if ( conn.setVersion() ) {
- conn.done();
- throw StaleConfigException( _ns , "ClusteredCursor::query" , true );
- }
-
- LOG(5) << "ClusteredCursor::query (" << type() << ") server:" << server
- << " ns:" << _ns << " query:" << q << " num:" << num
- << " _fields:" << _fields << " options: " << _options << endl;
-
- auto_ptr<DBClientCursor> cursor =
- conn->query( _ns , q , num , 0 , ( _fields.isEmpty() ? 0 : &_fields ) , _options , _batchSize == 0 ? 0 : _batchSize + skipLeft );
-
- if ( ! cursor.get() && _options & QueryOption_PartialResults ) {
- _done = true;
- conn.done();
- return cursor;
- }
-
- massert( 13633 , str::stream() << "error querying server: " << server , cursor.get() );
-
- cursor->attach( &conn ); // this calls done on conn
- assert( ! conn.ok() );
- _checkCursor( cursor.get() );
- return cursor;
- }
- catch ( SocketException& e ) {
- if ( ! ( _options & QueryOption_PartialResults ) )
- throw e;
- _done = true;
- return auto_ptr<DBClientCursor>();
- }
- }
-
- BSONObj ClusteredCursor::explain( const string& server , BSONObj extra ) {
- BSONObj q = _query;
- if ( ! extra.isEmpty() ) {
- q = concatQuery( q , extra );
- }
-
- BSONObj o;
-
- ShardConnection conn( server , _ns );
- auto_ptr<DBClientCursor> cursor = conn->query( _ns , Query( q ).explain() , abs( _batchSize ) * -1 , 0 , _fields.isEmpty() ? 0 : &_fields );
- if ( cursor.get() && cursor->more() )
- o = cursor->next().getOwned();
- conn.done();
- return o;
- }
-
- BSONObj ClusteredCursor::concatQuery( const BSONObj& query , const BSONObj& extraFilter ) {
- if ( ! query.hasField( "query" ) )
- return _concatFilter( query , extraFilter );
-
- BSONObjBuilder b;
- BSONObjIterator i( query );
- while ( i.more() ) {
- BSONElement e = i.next();
-
- if ( strcmp( e.fieldName() , "query" ) ) {
- b.append( e );
- continue;
- }
-
- b.append( "query" , _concatFilter( e.embeddedObjectUserCheck() , extraFilter ) );
- }
- return b.obj();
- }
-
- BSONObj ClusteredCursor::_concatFilter( const BSONObj& filter , const BSONObj& extra ) {
- BSONObjBuilder b;
- b.appendElements( filter );
- b.appendElements( extra );
- return b.obj();
- // TODO: should do some simplification here if possibl ideally
- }
-
- BSONObj ClusteredCursor::explain() {
- // Note: by default we filter out allPlans and oldPlan in the shell's
- // explain() function. If you add any recursive structures, make sure to
- // edit the JS to make sure everything gets filtered.
-
- BSONObjBuilder b;
- b.append( "clusteredType" , type() );
-
- long long millis = 0;
- double numExplains = 0;
-
- map<string,long long> counters;
-
- map<string,list<BSONObj> > out;
- {
- _explain( out );
-
- BSONObjBuilder x( b.subobjStart( "shards" ) );
- for ( map<string,list<BSONObj> >::iterator i=out.begin(); i!=out.end(); ++i ) {
- string shard = i->first;
- list<BSONObj> l = i->second;
- BSONArrayBuilder y( x.subarrayStart( shard ) );
- for ( list<BSONObj>::iterator j=l.begin(); j!=l.end(); ++j ) {
- BSONObj temp = *j;
- y.append( temp );
-
- BSONObjIterator k( temp );
- while ( k.more() ) {
- BSONElement z = k.next();
- if ( z.fieldName()[0] != 'n' )
- continue;
- long long& c = counters[z.fieldName()];
- c += z.numberLong();
- }
-
- millis += temp["millis"].numberLong();
- numExplains++;
- }
- y.done();
- }
- x.done();
- }
-
- for ( map<string,long long>::iterator i=counters.begin(); i!=counters.end(); ++i )
- b.appendNumber( i->first , i->second );
-
- b.appendNumber( "millisTotal" , millis );
- b.append( "millisAvg" , (int)((double)millis / numExplains ) );
- b.append( "numQueries" , (int)numExplains );
- b.append( "numShards" , (int)out.size() );
-
- return b.obj();
- }
-
- // -------- FilteringClientCursor -----------
- FilteringClientCursor::FilteringClientCursor( const BSONObj filter )
- : _matcher( filter ) , _done( true ) {
- }
-
- FilteringClientCursor::FilteringClientCursor( auto_ptr<DBClientCursor> cursor , const BSONObj filter )
- : _matcher( filter ) , _cursor( cursor ) , _done( cursor.get() == 0 ) {
- }
-
- FilteringClientCursor::FilteringClientCursor( DBClientCursor* cursor , const BSONObj filter )
- : _matcher( filter ) , _cursor( cursor ) , _done( cursor == 0 ) {
- }
-
-
- FilteringClientCursor::~FilteringClientCursor() {
- }
-
- void FilteringClientCursor::reset( auto_ptr<DBClientCursor> cursor ) {
- _cursor = cursor;
- _next = BSONObj();
- _done = _cursor.get() == 0;
- }
-
- void FilteringClientCursor::reset( DBClientCursor* cursor ) {
- _cursor.reset( cursor );
- _next = BSONObj();
- _done = cursor == 0;
- }
-
-
- bool FilteringClientCursor::more() {
- if ( ! _next.isEmpty() )
- return true;
-
- if ( _done )
- return false;
-
- _advance();
- return ! _next.isEmpty();
- }
-
- BSONObj FilteringClientCursor::next() {
- assert( ! _next.isEmpty() );
- assert( ! _done );
-
- BSONObj ret = _next;
- _next = BSONObj();
- _advance();
- return ret;
- }
-
- BSONObj FilteringClientCursor::peek() {
- if ( _next.isEmpty() )
- _advance();
- return _next;
- }
-
- void FilteringClientCursor::_advance() {
- assert( _next.isEmpty() );
- if ( ! _cursor.get() || _done )
- return;
-
- while ( _cursor->more() ) {
- _next = _cursor->next();
- if ( _matcher.matches( _next ) ) {
- if ( ! _cursor->moreInCurrentBatch() )
- _next = _next.getOwned();
- return;
- }
- _next = BSONObj();
- }
- _done = true;
- }
-
- // -------- SerialServerClusteredCursor -----------
-
- SerialServerClusteredCursor::SerialServerClusteredCursor( const set<ServerAndQuery>& servers , QueryMessage& q , int sortOrder) : ClusteredCursor( q ) {
- for ( set<ServerAndQuery>::const_iterator i = servers.begin(); i!=servers.end(); i++ )
- _servers.push_back( *i );
-
- if ( sortOrder > 0 )
- sort( _servers.begin() , _servers.end() );
- else if ( sortOrder < 0 )
- sort( _servers.rbegin() , _servers.rend() );
-
- _serverIndex = 0;
-
- _needToSkip = q.ntoskip;
- }
-
- bool SerialServerClusteredCursor::more() {
-
- // TODO: optimize this by sending on first query and then back counting
- // tricky in case where 1st server doesn't have any after
- // need it to send n skipped
- while ( _needToSkip > 0 && _current.more() ) {
- _current.next();
- _needToSkip--;
- }
-
- if ( _current.more() )
- return true;
-
- if ( _serverIndex >= _servers.size() ) {
- return false;
- }
-
- ServerAndQuery& sq = _servers[_serverIndex++];
-
- _current.reset( query( sq._server , 0 , sq._extra ) );
- return more();
- }
-
- BSONObj SerialServerClusteredCursor::next() {
- uassert( 10018 , "no more items" , more() );
- return _current.next();
- }
-
- void SerialServerClusteredCursor::_explain( map< string,list<BSONObj> >& out ) {
- for ( unsigned i=0; i<_servers.size(); i++ ) {
- ServerAndQuery& sq = _servers[i];
- list<BSONObj> & l = out[sq._server];
- l.push_back( explain( sq._server , sq._extra ) );
- }
- }
-
- // -------- ParallelSortClusteredCursor -----------
-
- ParallelSortClusteredCursor::ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , QueryMessage& q ,
- const BSONObj& sortKey )
- : ClusteredCursor( q ) , _servers( servers ) {
- _sortKey = sortKey.getOwned();
- _needToSkip = q.ntoskip;
- _finishCons();
- }
-
- ParallelSortClusteredCursor::ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , const string& ns ,
- const Query& q ,
- int options , const BSONObj& fields )
- : ClusteredCursor( ns , q.obj , options , fields ) , _servers( servers ) {
- _sortKey = q.getSort().copy();
- _needToSkip = 0;
- _finishCons();
- }
-
- void ParallelSortClusteredCursor::_finishCons() {
- _numServers = _servers.size();
- _lastFrom = 0;
- _cursors = 0;
-
- if ( ! _sortKey.isEmpty() && ! _fields.isEmpty() ) {
- // we need to make sure the sort key is in the projection
-
- set<string> sortKeyFields;
- _sortKey.getFieldNames(sortKeyFields);
-
- BSONObjBuilder b;
- bool isNegative = false;
- {
- BSONObjIterator i( _fields );
- while ( i.more() ) {
- BSONElement e = i.next();
- b.append( e );
-
- string fieldName = e.fieldName();
-
- // exact field
- bool found = sortKeyFields.erase(fieldName);
-
- // subfields
- set<string>::const_iterator begin = sortKeyFields.lower_bound(fieldName + ".\x00");
- set<string>::const_iterator end = sortKeyFields.lower_bound(fieldName + ".\xFF");
- sortKeyFields.erase(begin, end);
-
- if ( ! e.trueValue() ) {
- uassert( 13431 , "have to have sort key in projection and removing it" , !found && begin == end );
- }
- else if (!e.isABSONObj()) {
- isNegative = true;
- }
- }
- }
-
- if (isNegative) {
- for (set<string>::const_iterator it(sortKeyFields.begin()), end(sortKeyFields.end()); it != end; ++it) {
- b.append(*it, 1);
- }
- }
-
- _fields = b.obj();
- }
- }
-
- // TODO: Merge with futures API? We do a lot of error checking here that would be useful elsewhere.
- void ParallelSortClusteredCursor::_init() {
-
- // log() << "Starting parallel search..." << endl;
-
- // make sure we're not already initialized
- assert( ! _cursors );
- _cursors = new FilteringClientCursor[_numServers];
-
- bool returnPartial = ( _options & QueryOption_PartialResults );
-
- vector<ServerAndQuery> queries( _servers.begin(), _servers.end() );
- set<int> retryQueries;
- int finishedQueries = 0;
-
- vector< shared_ptr<ShardConnection> > conns;
- vector<string> servers;
-
- // Since we may get all sorts of errors, record them all as they come and throw them later if necessary
- vector<string> staleConfigExs;
- vector<string> socketExs;
- vector<string> otherExs;
- bool allConfigStale = false;
-
- int retries = -1;
-
- // Loop through all the queries until we've finished or gotten a socket exception on all of them
- // We break early for non-socket exceptions, and socket exceptions if we aren't returning partial results
- do {
- retries++;
-
- bool firstPass = retryQueries.size() == 0;
-
- if( ! firstPass ){
- log() << "retrying " << ( returnPartial ? "(partial) " : "" ) << "parallel connection to ";
- for( set<int>::iterator it = retryQueries.begin(); it != retryQueries.end(); ++it ){
- log() << queries[*it]._server << ", ";
- }
- log() << finishedQueries << " finished queries." << endl;
- }
-
- size_t num = 0;
- for ( vector<ServerAndQuery>::iterator it = queries.begin(); it != queries.end(); ++it ) {
- size_t i = num++;
-
- const ServerAndQuery& sq = *it;
-
- // If we're not retrying this cursor on later passes, continue
- if( ! firstPass && retryQueries.find( i ) == retryQueries.end() ) continue;
-
- // log() << "Querying " << _query << " from " << _ns << " for " << sq._server << endl;
-
- BSONObj q = _query;
- if ( ! sq._extra.isEmpty() ) {
- q = concatQuery( q , sq._extra );
- }
-
- string errLoc = " @ " + sq._server;
-
- if( firstPass ){
-
- // This may be the first time connecting to this shard, if so we can get an error here
- try {
- conns.push_back( shared_ptr<ShardConnection>( new ShardConnection( sq._server , _ns ) ) );
- }
- catch( std::exception& e ){
- socketExs.push_back( e.what() + errLoc );
- if( ! returnPartial ){
- num--;
- break;
- }
- conns.push_back( shared_ptr<ShardConnection>() );
- continue;
- }
-
- servers.push_back( sq._server );
- }
-
- if ( conns[i]->setVersion() ) {
- conns[i]->done();
- staleConfigExs.push_back( (string)"stale config detected for " + StaleConfigException( _ns , "ParallelCursor::_init" , true ).what() + errLoc );
- break;
- }
-
- LOG(5) << "ParallelSortClusteredCursor::init server:" << sq._server << " ns:" << _ns
- << " query:" << q << " _fields:" << _fields << " options: " << _options << endl;
-
- if( ! _cursors[i].raw() )
- _cursors[i].reset( new DBClientCursor( conns[i]->get() , _ns , q ,
- 0 , // nToReturn
- 0 , // nToSkip
- _fields.isEmpty() ? 0 : &_fields , // fieldsToReturn
- _options ,
- // NtoReturn is weird.
- // If zero, it means use default size, so we do that for all cursors
- // If positive, it's the batch size (we don't want this cursor limiting results), tha
- // done at a higher level
- // If negative, it's the batch size, but we don't create a cursor - so we don't want
- // to create a child cursor either.
- // Either way, if non-zero, we want to pull back the batch size + the skip amount as
- // quickly as possible. Potentially, for a cursor on a single shard or if we keep be
- // chunks, we can actually add the skip value into the cursor and/or make some assump
- // return value size ( (batch size + skip amount) / num_servers ).
- _batchSize == 0 ? 0 :
- ( _batchSize > 0 ? _batchSize + _needToSkip :
- _batchSize - _needToSkip ) // batchSize
- ) );
-
- try{
- _cursors[i].raw()->initLazy( ! firstPass );
- }
- catch( SocketException& e ){
- socketExs.push_back( e.what() + errLoc );
- _cursors[i].reset( NULL );
- conns[i]->done();
- if( ! returnPartial ) break;
- }
- catch( std::exception& e){
- otherExs.push_back( e.what() + errLoc );
- _cursors[i].reset( NULL );
- conns[i]->done();
- break;
- }
-
- }
-
- // Go through all the potentially started cursors and finish initializing them or log any errors and
- // potentially retry
- // TODO: Better error classification would make this easier, errors are indicated in all sorts of ways
- // here that we need to trap.
- for ( size_t i = 0; i < num; i++ ) {
-
- // log() << "Finishing query for " << cons[i].get()->getHost() << endl;
- string errLoc = " @ " + queries[i]._server;
-
- if( ! _cursors[i].raw() || ( ! firstPass && retryQueries.find( i ) == retryQueries.end() ) ){
- if( conns[i] ) conns[i].get()->done();
- continue;
- }
-
- assert( conns[i] );
- retryQueries.erase( i );
-
- bool retry = false;
-
- try {
-
- if( ! _cursors[i].raw()->initLazyFinish( retry ) ) {
-
- warning() << "invalid result from " << conns[i]->getHost() << ( retry ? ", retrying" : "" ) << endl;
- _cursors[i].reset( NULL );
-
- if( ! retry ){
- socketExs.push_back( str::stream() << "error querying server: " << servers[i] );
- conns[i]->done();
- }
- else {
- retryQueries.insert( i );
- }
-
- continue;
- }
- }
- catch ( MsgAssertionException& e ){
- socketExs.push_back( e.what() + errLoc );
- _cursors[i].reset( NULL );
- conns[i]->done();
- continue;
- }
- catch ( SocketException& e ) {
- socketExs.push_back( e.what() + errLoc );
- _cursors[i].reset( NULL );
- conns[i]->done();
- continue;
- }
- catch( std::exception& e ){
- otherExs.push_back( e.what() + errLoc );
- _cursors[i].reset( NULL );
- conns[i]->done();
- continue;
- }
-
- try {
- _cursors[i].raw()->attach( conns[i].get() ); // this calls done on conn
- _checkCursor( _cursors[i].raw() );
-
- finishedQueries++;
- }
- catch ( StaleConfigException& e ){
-
- // Our stored configuration data is actually stale, we need to reload it
- // when we throw our exception
- allConfigStale = true;
-
- staleConfigExs.push_back( (string)"stale config detected for " + e.what() + errLoc );
- _cursors[i].reset( NULL );
- conns[i]->done();
- continue;
- }
- catch( std::exception& e ){
- otherExs.push_back( e.what() + errLoc );
- _cursors[i].reset( NULL );
- conns[i]->done();
- continue;
- }
- }
-
- // Don't exceed our max retries, should not happen
- assert( retries < 5 );
- }
- while( retryQueries.size() > 0 /* something to retry */ &&
- ( socketExs.size() == 0 || returnPartial ) /* no conn issues */ &&
- staleConfigExs.size() == 0 /* no config issues */ &&
- otherExs.size() == 0 /* no other issues */);
-
- // Assert that our conns are all closed!
- for( vector< shared_ptr<ShardConnection> >::iterator i = conns.begin(); i < conns.end(); ++i ){
- assert( ! (*i) || ! (*i)->ok() );
- }
-
- // Handle errors we got during initialization.
- // If we're returning partial results, we can ignore socketExs, but nothing else
- // Log a warning in any case, so we don't lose these messages
- bool throwException = ( socketExs.size() > 0 && ! returnPartial ) || staleConfigExs.size() > 0 || otherExs.size() > 0;
-
- if( socketExs.size() > 0 || staleConfigExs.size() > 0 || otherExs.size() > 0 ) {
-
- vector<string> errMsgs;
-
- errMsgs.insert( errMsgs.end(), staleConfigExs.begin(), staleConfigExs.end() );
- errMsgs.insert( errMsgs.end(), otherExs.begin(), otherExs.end() );
- errMsgs.insert( errMsgs.end(), socketExs.begin(), socketExs.end() );
-
- stringstream errMsg;
- errMsg << "could not initialize cursor across all shards because : ";
- for( vector<string>::iterator i = errMsgs.begin(); i != errMsgs.end(); i++ ){
- if( i != errMsgs.begin() ) errMsg << " :: and :: ";
- errMsg << *i;
- }
-
- if( throwException && staleConfigExs.size() > 0 )
- throw StaleConfigException( _ns , errMsg.str() , ! allConfigStale );
- else if( throwException )
- throw DBException( errMsg.str(), 14827 );
- else
- warning() << errMsg.str() << endl;
- }
-
- if( retries > 0 )
- log() << "successfully finished parallel query after " << retries << " retries" << endl;
-
- }
-
- ParallelSortClusteredCursor::~ParallelSortClusteredCursor() {
- delete [] _cursors;
- _cursors = 0;
- }
-
- bool ParallelSortClusteredCursor::more() {
-
- if ( _needToSkip > 0 ) {
- int n = _needToSkip;
- _needToSkip = 0;
-
- while ( n > 0 && more() ) {
- BSONObj x = next();
- n--;
- }
-
- _needToSkip = n;
- }
-
- for ( int i=0; i<_numServers; i++ ) {
- if ( _cursors[i].more() )
- return true;
- }
- return false;
- }
-
- BSONObj ParallelSortClusteredCursor::next() {
- BSONObj best = BSONObj();
- int bestFrom = -1;
-
- for( int j = 0; j < _numServers; j++ ){
-
- // Iterate _numServers times, starting one past the last server we used.
- // This means we actually start at server #1, not #0, but shouldn't matter
-
- int i = ( j + _lastFrom + 1 ) % _numServers;
-
- if ( ! _cursors[i].more() )
- continue;
-
- BSONObj me = _cursors[i].peek();
-
- if ( best.isEmpty() ) {
- best = me;
- bestFrom = i;
- if( _sortKey.isEmpty() ) break;
- continue;
- }
-
- int comp = best.woSortOrder( me , _sortKey , true );
- if ( comp < 0 )
- continue;
-
- best = me;
- bestFrom = i;
- }
-
- _lastFrom = bestFrom;
-
- uassert( 10019 , "no more elements" , ! best.isEmpty() );
- _cursors[bestFrom].next();
-
- return best;
- }
-
- void ParallelSortClusteredCursor::_explain( map< string,list<BSONObj> >& out ) {
- for ( set<ServerAndQuery>::iterator i=_servers.begin(); i!=_servers.end(); ++i ) {
- const ServerAndQuery& sq = *i;
- list<BSONObj> & l = out[sq._server];
- l.push_back( explain( sq._server , sq._extra ) );
- }
-
- }
-
- // -----------------
- // ---- Future -----
- // -----------------
-
- Future::CommandResult::CommandResult( const string& server , const string& db , const BSONObj& cmd , int options , DBClientBase * conn )
- :_server(server) ,_db(db) , _options(options), _cmd(cmd) ,_conn(conn) ,_done(false)
- {
- try {
- if ( ! _conn ){
- _connHolder.reset( new ScopedDbConnection( _server ) );
- _conn = _connHolder->get();
- }
-
- if ( _conn->lazySupported() ) {
- _cursor.reset( new DBClientCursor(_conn, _db + ".$cmd", _cmd, -1/*limit*/, 0, NULL, _options, 0));
- _cursor->initLazy();
- }
- else {
- _done = true; // we set _done first because even if there is an error we're done
- _ok = _conn->runCommand( db , cmd , _res , options );
- }
- }
- catch ( std::exception& e ) {
- error() << "Future::spawnComand (part 1) exception: " << e.what() << endl;
- _ok = false;
- _done = true;
- }
- }
-
- bool Future::CommandResult::join() {
- if (_done)
- return _ok;
-
- try {
- // TODO: Allow retries?
- bool retry = false;
- bool finished = _cursor->initLazyFinish( retry );
-
- // Shouldn't need to communicate with server any more
- if ( _connHolder )
- _connHolder->done();
-
- uassert(14812, str::stream() << "Error running command on server: " << _server, finished);
- massert(14813, "Command returned nothing", _cursor->more());
-
- _res = _cursor->nextSafe();
- _ok = _res["ok"].trueValue();
-
- }
- catch ( std::exception& e ) {
- error() << "Future::spawnComand (part 2) exception: " << e.what() << endl;
- _ok = false;
- }
-
- _done = true;
- return _ok;
- }
-
- shared_ptr<Future::CommandResult> Future::spawnCommand( const string& server , const string& db , const BSONObj& cmd , int options , DBClientBase * conn ) {
- shared_ptr<Future::CommandResult> res (new Future::CommandResult( server , db , cmd , options , conn ));
- return res;
- }
-
-}
diff --git a/client/parallel.h b/client/parallel.h
deleted file mode 100644
index cbf64826596..00000000000
--- a/client/parallel.h
+++ /dev/null
@@ -1,315 +0,0 @@
-// parallel.h
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- tools for working in parallel/sharded/clustered environment
- */
-
-#include "../pch.h"
-#include "dbclient.h"
-#include "redef_macros.h"
-#include "../db/dbmessage.h"
-#include "../db/matcher.h"
-#include "../util/concurrency/mvar.h"
-
-namespace mongo {
-
- /**
- * holder for a server address and a query to run
- */
- class ServerAndQuery {
- public:
- ServerAndQuery( const string& server , BSONObj extra = BSONObj() , BSONObj orderObject = BSONObj() ) :
- _server( server ) , _extra( extra.getOwned() ) , _orderObject( orderObject.getOwned() ) {
- }
-
- bool operator<( const ServerAndQuery& other ) const {
- if ( ! _orderObject.isEmpty() )
- return _orderObject.woCompare( other._orderObject ) < 0;
-
- if ( _server < other._server )
- return true;
- if ( other._server > _server )
- return false;
- return _extra.woCompare( other._extra ) < 0;
- }
-
- string toString() const {
- StringBuilder ss;
- ss << "server:" << _server << " _extra:" << _extra.toString() << " _orderObject:" << _orderObject.toString();
- return ss.str();
- }
-
- operator string() const {
- return toString();
- }
-
- string _server;
- BSONObj _extra;
- BSONObj _orderObject;
- };
-
- /**
- * this is a cursor that works over a set of servers
- * can be used in serial/paralellel as controlled by sub classes
- */
- class ClusteredCursor {
- public:
- ClusteredCursor( QueryMessage& q );
- ClusteredCursor( const string& ns , const BSONObj& q , int options=0 , const BSONObj& fields=BSONObj() );
- virtual ~ClusteredCursor();
-
- /** call before using */
- void init();
-
- virtual bool more() = 0;
- virtual BSONObj next() = 0;
-
- static BSONObj concatQuery( const BSONObj& query , const BSONObj& extraFilter );
-
- virtual string type() const = 0;
-
- virtual BSONObj explain();
-
- protected:
-
- virtual void _init() = 0;
-
- auto_ptr<DBClientCursor> query( const string& server , int num = 0 , BSONObj extraFilter = BSONObj() , int skipLeft = 0 , bool lazy=false );
- BSONObj explain( const string& server , BSONObj extraFilter = BSONObj() );
-
- /**
- * checks the cursor for any errors
- * will throw an exceptionif an error is encountered
- */
- void _checkCursor( DBClientCursor * cursor );
-
- static BSONObj _concatFilter( const BSONObj& filter , const BSONObj& extraFilter );
-
- virtual void _explain( map< string,list<BSONObj> >& out ) = 0;
-
- string _ns;
- BSONObj _query;
- int _options;
- BSONObj _fields;
- int _batchSize;
-
- bool _didInit;
-
- bool _done;
- };
-
-
- class FilteringClientCursor {
- public:
- FilteringClientCursor( const BSONObj filter = BSONObj() );
- FilteringClientCursor( DBClientCursor* cursor , const BSONObj filter = BSONObj() );
- FilteringClientCursor( auto_ptr<DBClientCursor> cursor , const BSONObj filter = BSONObj() );
- ~FilteringClientCursor();
-
- void reset( auto_ptr<DBClientCursor> cursor );
- void reset( DBClientCursor* cursor );
-
- bool more();
- BSONObj next();
-
- BSONObj peek();
-
- DBClientCursor* raw() { return _cursor.get(); }
-
- private:
- void _advance();
-
- Matcher _matcher;
- auto_ptr<DBClientCursor> _cursor;
-
- BSONObj _next;
- bool _done;
- };
-
-
- class Servers {
- public:
- Servers() {
- }
-
- void add( const ServerAndQuery& s ) {
- add( s._server , s._extra );
- }
-
- void add( const string& server , const BSONObj& filter ) {
- vector<BSONObj>& mine = _filters[server];
- mine.push_back( filter.getOwned() );
- }
-
- // TOOO: pick a less horrible name
- class View {
- View( const Servers* s ) {
- for ( map<string, vector<BSONObj> >::const_iterator i=s->_filters.begin(); i!=s->_filters.end(); ++i ) {
- _servers.push_back( i->first );
- _filters.push_back( i->second );
- }
- }
- public:
- int size() const {
- return _servers.size();
- }
-
- string getServer( int n ) const {
- return _servers[n];
- }
-
- vector<BSONObj> getFilter( int n ) const {
- return _filters[ n ];
- }
-
- private:
- vector<string> _servers;
- vector< vector<BSONObj> > _filters;
-
- friend class Servers;
- };
-
- View view() const {
- return View( this );
- }
-
-
- private:
- map<string, vector<BSONObj> > _filters;
-
- friend class View;
- };
-
-
- /**
- * runs a query in serial across any number of servers
- * returns all results from 1 server, then the next, etc...
- */
- class SerialServerClusteredCursor : public ClusteredCursor {
- public:
- SerialServerClusteredCursor( const set<ServerAndQuery>& servers , QueryMessage& q , int sortOrder=0);
- virtual bool more();
- virtual BSONObj next();
- virtual string type() const { return "SerialServer"; }
-
- protected:
- virtual void _explain( map< string,list<BSONObj> >& out );
-
- void _init() {}
-
- vector<ServerAndQuery> _servers;
- unsigned _serverIndex;
-
- FilteringClientCursor _current;
-
- int _needToSkip;
- };
-
-
- /**
- * runs a query in parellel across N servers
- * sots
- */
- class ParallelSortClusteredCursor : public ClusteredCursor {
- public:
- ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , QueryMessage& q , const BSONObj& sortKey );
- ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , const string& ns ,
- const Query& q , int options=0, const BSONObj& fields=BSONObj() );
- virtual ~ParallelSortClusteredCursor();
- virtual bool more();
- virtual BSONObj next();
- virtual string type() const { return "ParallelSort"; }
- protected:
- void _finishCons();
- void _init();
-
- virtual void _explain( map< string,list<BSONObj> >& out );
-
- int _numServers;
- int _lastFrom;
- set<ServerAndQuery> _servers;
- BSONObj _sortKey;
-
- FilteringClientCursor * _cursors;
- int _needToSkip;
- };
-
- /**
- * tools for doing asynchronous operations
- * right now uses underlying sync network ops and uses another thread
- * should be changed to use non-blocking io
- */
- class Future {
- public:
- class CommandResult {
- public:
-
- string getServer() const { return _server; }
-
- bool isDone() const { return _done; }
-
- bool ok() const {
- assert( _done );
- return _ok;
- }
-
- BSONObj result() const {
- assert( _done );
- return _res;
- }
-
- /**
- blocks until command is done
- returns ok()
- */
- bool join();
-
- private:
-
- CommandResult( const string& server , const string& db , const BSONObj& cmd , int options , DBClientBase * conn );
-
- string _server;
- string _db;
- int _options;
- BSONObj _cmd;
- DBClientBase * _conn;
- scoped_ptr<ScopedDbConnection> _connHolder; // used if not provided a connection
-
- scoped_ptr<DBClientCursor> _cursor;
-
- BSONObj _res;
- bool _ok;
- bool _done;
-
- friend class Future;
- };
-
-
- /**
- * @param server server name
- * @param db db name
- * @param cmd cmd to exec
- * @param conn optional connection to use. will use standard pooled if non-specified
- */
- static shared_ptr<CommandResult> spawnCommand( const string& server , const string& db , const BSONObj& cmd , int options , DBClientBase * conn = 0 );
- };
-
-
-}
-
-#include "undef_macros.h"
diff --git a/client/redef_macros.h b/client/redef_macros.h
deleted file mode 100644
index 5a39561afd5..00000000000
--- a/client/redef_macros.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/** @file redef_macros.h macros for mongo internals
-
- @see undef_macros.h undefines these after use to minimize name pollution.
-*/
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// If you define a new global un-prefixed macro, please add it here and in undef_macros
-
-#define MONGO_MACROS_PUSHED 1
-
-// util/allocator.h
-#pragma push_macro("malloc")
-#undef malloc
-#define malloc MONGO_malloc
-#pragma push_macro("realloc")
-#undef realloc
-#define realloc MONGO_realloc
-
-// util/assert_util.h
-#pragma push_macro("assert")
-#undef assert
-#define assert MONGO_assert
-#pragma push_macro("verify")
-#undef verify
-#define verify MONGO_verify
-#pragma push_macro("dassert")
-#undef dassert
-#define dassert MONGO_dassert
-#pragma push_macro("wassert")
-#undef wassert
-#define wassert MONGO_wassert
-#pragma push_macro("massert")
-#undef massert
-#define massert MONGO_massert
-#pragma push_macro("uassert")
-#undef uassert
-#define uassert MONGO_uassert
-#define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION
-#pragma push_macro("DESTRUCTOR_GUARD")
-#undef DESTRUCTOR_GUARD
-#define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD
-
-// util/goodies.h
-#pragma push_macro("PRINT")
-#undef PRINT
-#define PRINT MONGO_PRINT
-#pragma push_macro("PRINTFL")
-#undef PRINTFL
-#define PRINTFL MONGO_PRINTFL
-#pragma push_macro("asctime")
-#undef asctime
-#define asctime MONGO_asctime
-#pragma push_macro("gmtime")
-#undef gmtime
-#define gmtime MONGO_gmtime
-#pragma push_macro("localtime")
-#undef localtime
-#define localtime MONGO_localtime
-#pragma push_macro("ctime")
-#undef ctime
-#define ctime MONGO_ctime
-
-// util/debug_util.h
-#pragma push_macro("DEV")
-#undef DEV
-#define DEV MONGO_DEV
-#pragma push_macro("DEBUGGING")
-#undef DEBUGGING
-#define DEBUGGING MONGO_DEBUGGING
-#pragma push_macro("SOMETIMES")
-#undef SOMETIMES
-#define SOMETIMES MONGO_SOMETIMES
-#pragma push_macro("OCCASIONALLY")
-#undef OCCASIONALLY
-#define OCCASIONALLY MONGO_OCCASIONALLY
-#pragma push_macro("RARELY")
-#undef RARELY
-#define RARELY MONGO_RARELY
-#pragma push_macro("ONCE")
-#undef ONCE
-#define ONCE MONGO_ONCE
-
-// util/log.h
-#pragma push_macro("LOG")
-#undef LOG
-#define LOG MONGO_LOG
-
-
diff --git a/client/simple_client_demo.cpp b/client/simple_client_demo.cpp
deleted file mode 100644
index f4278dd4e54..00000000000
--- a/client/simple_client_demo.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/* simple_client_demo.cpp
-
- See also : http://www.mongodb.org/pages/viewpage.action?pageId=133415
-
- How to build and run:
-
- (1) Using the mongoclient:
- g++ simple_client_demo.cpp -lmongoclient -lboost_thread-mt -lboost_filesystem -lboost_program_options
- ./a.out
-
- (2) using client_lib.cpp:
- g++ -I .. simple_client_demo.cpp mongo_client_lib.cpp -lboost_thread-mt -lboost_filesystem
- ./a.out
-*/
-
-#include <iostream>
-#include "dbclient.h" // the mongo c++ driver
-
-using namespace std;
-using namespace mongo;
-using namespace bson;
-
-int main() {
- try {
- cout << "connecting to localhost..." << endl;
- DBClientConnection c;
- c.connect("localhost");
- cout << "connected ok" << endl;
- unsigned long long count = c.count("test.foo");
- cout << "count of exiting documents in collection test.foo : " << count << endl;
-
- bo o = BSON( "hello" << "world" );
- c.insert("test.foo", o);
-
- string e = c.getLastError();
- if( !e.empty() ) {
- cout << "insert #1 failed: " << e << endl;
- }
-
- // make an index with a unique key constraint
- c.ensureIndex("test.foo", BSON("hello"<<1), /*unique*/true);
-
- c.insert("test.foo", o); // will cause a dup key error on "hello" field
- cout << "we expect a dup key error here:" << endl;
- cout << " " << c.getLastErrorDetailed().toString() << endl;
- }
- catch(DBException& e) {
- cout << "caught DBException " << e.toString() << endl;
- return 1;
- }
-
- return 0;
-}
-
diff --git a/client/syncclusterconnection.cpp b/client/syncclusterconnection.cpp
deleted file mode 100644
index 34633d15fa8..00000000000
--- a/client/syncclusterconnection.cpp
+++ /dev/null
@@ -1,407 +0,0 @@
-// syncclusterconnection.cpp
-/*
- * Copyright 2010 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#include "pch.h"
-#include "syncclusterconnection.h"
-#include "../db/dbmessage.h"
-
-// error codes 8000-8009
-
-namespace mongo {
-
- SyncClusterConnection::SyncClusterConnection( const list<HostAndPort> & L, double socketTimeout) : _mutex("SyncClusterConnection"), _socketTimeout( socketTimeout ) {
- {
- stringstream s;
- int n=0;
- for( list<HostAndPort>::const_iterator i = L.begin(); i != L.end(); i++ ) {
- if( ++n > 1 ) s << ',';
- s << i->toString();
- }
- _address = s.str();
- }
- for( list<HostAndPort>::const_iterator i = L.begin(); i != L.end(); i++ )
- _connect( i->toString() );
- }
-
- SyncClusterConnection::SyncClusterConnection( string commaSeperated, double socketTimeout) : _mutex("SyncClusterConnection"), _socketTimeout( socketTimeout ) {
- _address = commaSeperated;
- string::size_type idx;
- while ( ( idx = commaSeperated.find( ',' ) ) != string::npos ) {
- string h = commaSeperated.substr( 0 , idx );
- commaSeperated = commaSeperated.substr( idx + 1 );
- _connect( h );
- }
- _connect( commaSeperated );
- uassert( 8004 , "SyncClusterConnection needs 3 servers" , _conns.size() == 3 );
- }
-
- SyncClusterConnection::SyncClusterConnection( string a , string b , string c, double socketTimeout) : _mutex("SyncClusterConnection"), _socketTimeout( socketTimeout ) {
- _address = a + "," + b + "," + c;
- // connect to all even if not working
- _connect( a );
- _connect( b );
- _connect( c );
- }
-
- SyncClusterConnection::SyncClusterConnection( SyncClusterConnection& prev, double socketTimeout) : _mutex("SyncClusterConnection"), _socketTimeout( socketTimeout ) {
- assert(0);
- }
-
- SyncClusterConnection::~SyncClusterConnection() {
- for ( size_t i=0; i<_conns.size(); i++ )
- delete _conns[i];
- _conns.clear();
- }
-
- bool SyncClusterConnection::prepare( string& errmsg ) {
- _lastErrors.clear();
- return fsync( errmsg );
- }
-
- bool SyncClusterConnection::fsync( string& errmsg ) {
- bool ok = true;
- errmsg = "";
- for ( size_t i=0; i<_conns.size(); i++ ) {
- BSONObj res;
- try {
- if ( _conns[i]->simpleCommand( "admin" , &res , "fsync" ) )
- continue;
- }
- catch ( DBException& e ) {
- errmsg += e.toString();
- }
- catch ( std::exception& e ) {
- errmsg += e.what();
- }
- catch ( ... ) {
- }
- ok = false;
- errmsg += " " + _conns[i]->toString() + ":" + res.toString();
- }
- return ok;
- }
-
- void SyncClusterConnection::_checkLast() {
- _lastErrors.clear();
- vector<string> errors;
-
- for ( size_t i=0; i<_conns.size(); i++ ) {
- BSONObj res;
- string err;
- try {
- if ( ! _conns[i]->runCommand( "admin" , BSON( "getlasterror" << 1 << "fsync" << 1 ) , res ) )
- err = "cmd failed: ";
- }
- catch ( std::exception& e ) {
- err += e.what();
- }
- catch ( ... ) {
- err += "unknown failure";
- }
- _lastErrors.push_back( res.getOwned() );
- errors.push_back( err );
- }
-
- assert( _lastErrors.size() == errors.size() && _lastErrors.size() == _conns.size() );
-
- stringstream err;
- bool ok = true;
-
- for ( size_t i = 0; i<_conns.size(); i++ ) {
- BSONObj res = _lastErrors[i];
- if ( res["ok"].trueValue() && (res["fsyncFiles"].numberInt() > 0 || res.hasElement("waited")))
- continue;
- ok = false;
- err << _conns[i]->toString() << ": " << res << " " << errors[i];
- }
-
- if ( ok )
- return;
- throw UserException( 8001 , (string)"SyncClusterConnection write op failed: " + err.str() );
- }
-
- BSONObj SyncClusterConnection::getLastErrorDetailed() {
- if ( _lastErrors.size() )
- return _lastErrors[0];
- return DBClientBase::getLastErrorDetailed();
- }
-
- void SyncClusterConnection::_connect( string host ) {
- log() << "SyncClusterConnection connecting to [" << host << "]" << endl;
- DBClientConnection * c = new DBClientConnection( true );
- c->setSoTimeout( _socketTimeout );
- string errmsg;
- if ( ! c->connect( host , errmsg ) )
- log() << "SyncClusterConnection connect fail to: " << host << " errmsg: " << errmsg << endl;
- _connAddresses.push_back( host );
- _conns.push_back( c );
- }
-
- bool SyncClusterConnection::callRead( Message& toSend , Message& response ) {
- // TODO: need to save state of which one to go back to somehow...
- return _conns[0]->callRead( toSend , response );
- }
-
- BSONObj SyncClusterConnection::findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn, int queryOptions) {
-
- if ( ns.find( ".$cmd" ) != string::npos ) {
- string cmdName = query.obj.firstElementFieldName();
-
- int lockType = _lockType( cmdName );
-
- if ( lockType > 0 ) { // write $cmd
- string errmsg;
- if ( ! prepare( errmsg ) )
- throw UserException( 13104 , (string)"SyncClusterConnection::findOne prepare failed: " + errmsg );
-
- vector<BSONObj> all;
- for ( size_t i=0; i<_conns.size(); i++ ) {
- all.push_back( _conns[i]->findOne( ns , query , 0 , queryOptions ).getOwned() );
- }
-
- _checkLast();
-
- for ( size_t i=0; i<all.size(); i++ ) {
- BSONObj temp = all[i];
- if ( isOk( temp ) )
- continue;
- stringstream ss;
- ss << "write $cmd failed on a node: " << temp.jsonString();
- ss << " " << _conns[i]->toString();
- ss << " ns: " << ns;
- ss << " cmd: " << query.toString();
- throw UserException( 13105 , ss.str() );
- }
-
- return all[0];
- }
- }
-
- return DBClientBase::findOne( ns , query , fieldsToReturn , queryOptions );
- }
-
- bool SyncClusterConnection::auth(const string &dbname, const string &username, const string &password_text, string& errmsg, bool digestPassword) {
- for (vector<DBClientConnection*>::iterator it = _conns.begin(); it < _conns.end(); it++) {
- massert( 15848, "sync cluster of sync clusters?", (*it)->type() != ConnectionString::SYNC);
-
- if (!(*it)->auth(dbname, username, password_text, errmsg, digestPassword)) {
- return false;
- }
- }
- return true;
- }
-
- auto_ptr<DBClientCursor> SyncClusterConnection::query(const string &ns, Query query, int nToReturn, int nToSkip,
- const BSONObj *fieldsToReturn, int queryOptions, int batchSize ) {
- _lastErrors.clear();
- if ( ns.find( ".$cmd" ) != string::npos ) {
- string cmdName = query.obj.firstElementFieldName();
- int lockType = _lockType( cmdName );
- uassert( 13054 , (string)"write $cmd not supported in SyncClusterConnection::query for:" + cmdName , lockType <= 0 );
- }
-
- return _queryOnActive( ns , query , nToReturn , nToSkip , fieldsToReturn , queryOptions , batchSize );
- }
-
- bool SyncClusterConnection::_commandOnActive(const string &dbname, const BSONObj& cmd, BSONObj &info, int options ) {
- auto_ptr<DBClientCursor> cursor = _queryOnActive( dbname + ".$cmd" , cmd , 1 , 0 , 0 , options , 0 );
- if ( cursor->more() )
- info = cursor->next().copy();
- else
- info = BSONObj();
- return isOk( info );
- }
-
- auto_ptr<DBClientCursor> SyncClusterConnection::_queryOnActive(const string &ns, Query query, int nToReturn, int nToSkip,
- const BSONObj *fieldsToReturn, int queryOptions, int batchSize ) {
-
- for ( size_t i=0; i<_conns.size(); i++ ) {
- try {
- auto_ptr<DBClientCursor> cursor =
- _conns[i]->query( ns , query , nToReturn , nToSkip , fieldsToReturn , queryOptions , batchSize );
- if ( cursor.get() )
- return cursor;
- log() << "query failed to: " << _conns[i]->toString() << " no data" << endl;
- }
- catch ( ... ) {
- log() << "query failed to: " << _conns[i]->toString() << " exception" << endl;
- }
- }
- throw UserException( 8002 , "all servers down!" );
- }
-
- auto_ptr<DBClientCursor> SyncClusterConnection::getMore( const string &ns, long long cursorId, int nToReturn, int options ) {
- uassert( 10022 , "SyncClusterConnection::getMore not supported yet" , 0);
- auto_ptr<DBClientCursor> c;
- return c;
- }
-
- void SyncClusterConnection::insert( const string &ns, BSONObj obj , int flags) {
-
- uassert( 13119 , (string)"SyncClusterConnection::insert obj has to have an _id: " + obj.jsonString() ,
- ns.find( ".system.indexes" ) != string::npos || obj["_id"].type() );
-
- string errmsg;
- if ( ! prepare( errmsg ) )
- throw UserException( 8003 , (string)"SyncClusterConnection::insert prepare failed: " + errmsg );
-
- for ( size_t i=0; i<_conns.size(); i++ ) {
- _conns[i]->insert( ns , obj , flags);
- }
-
- _checkLast();
- }
-
- void SyncClusterConnection::insert( const string &ns, const vector< BSONObj >& v , int flags) {
- uassert( 10023 , "SyncClusterConnection bulk insert not implemented" , 0);
- }
-
- void SyncClusterConnection::remove( const string &ns , Query query, bool justOne ) {
- string errmsg;
- if ( ! prepare( errmsg ) )
- throw UserException( 8020 , (string)"SyncClusterConnection::remove prepare failed: " + errmsg );
-
- for ( size_t i=0; i<_conns.size(); i++ ) {
- _conns[i]->remove( ns , query , justOne );
- }
-
- _checkLast();
- }
-
- void SyncClusterConnection::update( const string &ns , Query query , BSONObj obj , bool upsert , bool multi ) {
-
- if ( upsert ) {
- uassert( 13120 , "SyncClusterConnection::update upsert query needs _id" , query.obj["_id"].type() );
- }
-
- if ( _writeConcern ) {
- string errmsg;
- if ( ! prepare( errmsg ) )
- throw UserException( 8005 , (string)"SyncClusterConnection::udpate prepare failed: " + errmsg );
- }
-
- for ( size_t i = 0; i < _conns.size(); i++ ) {
- try {
- _conns[i]->update( ns , query , obj , upsert , multi );
- }
- catch ( std::exception& e ) {
- if ( _writeConcern )
- throw e;
- }
- }
-
- if ( _writeConcern ) {
- _checkLast();
- assert( _lastErrors.size() > 1 );
-
- int a = _lastErrors[0]["n"].numberInt();
- for ( unsigned i=1; i<_lastErrors.size(); i++ ) {
- int b = _lastErrors[i]["n"].numberInt();
- if ( a == b )
- continue;
-
- throw UpdateNotTheSame( 8017 ,
- str::stream()
- << "update not consistent "
- << " ns: " << ns
- << " query: " << query.toString()
- << " update: " << obj
- << " gle1: " << _lastErrors[0]
- << " gle2: " << _lastErrors[i] ,
- _connAddresses , _lastErrors );
- }
- }
- }
-
- string SyncClusterConnection::_toString() const {
- stringstream ss;
- ss << "SyncClusterConnection [" << _address << "]";
- return ss.str();
- }
-
- bool SyncClusterConnection::call( Message &toSend, Message &response, bool assertOk , string * actualServer ) {
- uassert( 8006 , "SyncClusterConnection::call can only be used directly for dbQuery" ,
- toSend.operation() == dbQuery );
-
- DbMessage d( toSend );
- uassert( 8007 , "SyncClusterConnection::call can't handle $cmd" , strstr( d.getns(), "$cmd" ) == 0 );
-
- for ( size_t i=0; i<_conns.size(); i++ ) {
- try {
- bool ok = _conns[i]->call( toSend , response , assertOk );
- if ( ok ) {
- if ( actualServer )
- *actualServer = _connAddresses[i];
- return ok;
- }
- log() << "call failed to: " << _conns[i]->toString() << " no data" << endl;
- }
- catch ( ... ) {
- log() << "call failed to: " << _conns[i]->toString() << " exception" << endl;
- }
- }
- throw UserException( 8008 , "all servers down!" );
- }
-
- void SyncClusterConnection::say( Message &toSend, bool isRetry ) {
- string errmsg;
- if ( ! prepare( errmsg ) )
- throw UserException( 13397 , (string)"SyncClusterConnection::say prepare failed: " + errmsg );
-
- for ( size_t i=0; i<_conns.size(); i++ ) {
- _conns[i]->say( toSend );
- }
-
- _checkLast();
- }
-
- void SyncClusterConnection::sayPiggyBack( Message &toSend ) {
- assert(0);
- }
-
- int SyncClusterConnection::_lockType( const string& name ) {
- {
- scoped_lock lk(_mutex);
- map<string,int>::iterator i = _lockTypes.find( name );
- if ( i != _lockTypes.end() )
- return i->second;
- }
-
- BSONObj info;
- uassert( 13053 , str::stream() << "help failed: " << info , _commandOnActive( "admin" , BSON( name << "1" << "help" << 1 ) , info ) );
-
- int lockType = info["lockType"].numberInt();
-
- scoped_lock lk(_mutex);
- _lockTypes[name] = lockType;
- return lockType;
- }
-
- void SyncClusterConnection::killCursor( long long cursorID ) {
- // should never need to do this
- assert(0);
- }
-
- void SyncClusterConnection::setAllSoTimeouts( double socketTimeout ){
- _socketTimeout = socketTimeout;
- for ( size_t i=0; i<_conns.size(); i++ )
-
- if( _conns[i] ) _conns[i]->setSoTimeout( socketTimeout );
- }
-
-}
diff --git a/client/syncclusterconnection.h b/client/syncclusterconnection.h
deleted file mode 100644
index 68dd338a408..00000000000
--- a/client/syncclusterconnection.h
+++ /dev/null
@@ -1,147 +0,0 @@
-// @file syncclusterconnection.h
-
-/*
- * Copyright 2010 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "../pch.h"
-#include "dbclient.h"
-#include "redef_macros.h"
-
-namespace mongo {
-
- /**
- * This is a connection to a cluster of servers that operate as one
- * for super high durability.
- *
- * Write operations are two-phase. First, all nodes are asked to fsync. If successful
- * everywhere, the write is sent everywhere and then followed by an fsync. There is no
- * rollback if a problem occurs during the second phase. Naturally, with all these fsyncs,
- * these operations will be quite slow -- use sparingly.
- *
- * Read operations are sent to a single random node.
- *
- * The class checks if a command is read or write style, and sends to a single
- * node if a read lock command and to all in two phases with a write style command.
- */
- class SyncClusterConnection : public DBClientBase {
- public:
- /**
- * @param commaSeparated should be 3 hosts comma separated
- */
- SyncClusterConnection( const list<HostAndPort> &, double socketTimeout = 0);
- SyncClusterConnection( string commaSeparated, double socketTimeout = 0);
- SyncClusterConnection( string a , string b , string c, double socketTimeout = 0 );
- ~SyncClusterConnection();
-
- /**
- * @return true if all servers are up and ready for writes
- */
- bool prepare( string& errmsg );
-
- /**
- * runs fsync on all servers
- */
- bool fsync( string& errmsg );
-
- // --- from DBClientInterface
-
- virtual BSONObj findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn, int queryOptions);
-
- virtual auto_ptr<DBClientCursor> query(const string &ns, Query query, int nToReturn, int nToSkip,
- const BSONObj *fieldsToReturn, int queryOptions, int batchSize );
-
- virtual auto_ptr<DBClientCursor> getMore( const string &ns, long long cursorId, int nToReturn, int options );
-
- virtual void insert( const string &ns, BSONObj obj, int flags=0);
-
- virtual void insert( const string &ns, const vector< BSONObj >& v, int flags=0);
-
- virtual void remove( const string &ns , Query query, bool justOne );
-
- virtual void update( const string &ns , Query query , BSONObj obj , bool upsert , bool multi );
-
- virtual bool call( Message &toSend, Message &response, bool assertOk , string * actualServer );
- virtual void say( Message &toSend, bool isRetry = false );
- virtual void sayPiggyBack( Message &toSend );
-
- virtual void killCursor( long long cursorID );
-
- virtual string getServerAddress() const { return _address; }
- virtual bool isFailed() const { return false; }
- virtual string toString() { return _toString(); }
-
- virtual BSONObj getLastErrorDetailed();
-
- virtual bool callRead( Message& toSend , Message& response );
-
- virtual ConnectionString::ConnectionType type() const { return ConnectionString::SYNC; }
-
- void setAllSoTimeouts( double socketTimeout );
- double getSoTimeout() const { return _socketTimeout; }
-
- virtual bool auth(const string &dbname, const string &username, const string &password_text, string& errmsg, bool digestPassword);
-
- virtual bool lazySupported() const { return false; }
- private:
- SyncClusterConnection( SyncClusterConnection& prev, double socketTimeout = 0 );
- string _toString() const;
- bool _commandOnActive(const string &dbname, const BSONObj& cmd, BSONObj &info, int options=0);
- auto_ptr<DBClientCursor> _queryOnActive(const string &ns, Query query, int nToReturn, int nToSkip,
- const BSONObj *fieldsToReturn, int queryOptions, int batchSize );
- int _lockType( const string& name );
- void _checkLast();
- void _connect( string host );
-
- string _address;
- vector<string> _connAddresses;
- vector<DBClientConnection*> _conns;
- map<string,int> _lockTypes;
- mongo::mutex _mutex;
-
- vector<BSONObj> _lastErrors;
-
- double _socketTimeout;
- };
-
- class UpdateNotTheSame : public UserException {
- public:
- UpdateNotTheSame( int code , const string& msg , const vector<string>& addrs , const vector<BSONObj>& lastErrors )
- : UserException( code , msg ) , _addrs( addrs ) , _lastErrors( lastErrors ) {
- assert( _addrs.size() == _lastErrors.size() );
- }
-
- virtual ~UpdateNotTheSame() throw() {
- }
-
- unsigned size() const {
- return _addrs.size();
- }
-
- pair<string,BSONObj> operator[](unsigned i) const {
- return make_pair( _addrs[i] , _lastErrors[i] );
- }
-
- private:
-
- vector<string> _addrs;
- vector<BSONObj> _lastErrors;
- };
-
-};
-
-#include "undef_macros.h"
diff --git a/client/undef_macros.h b/client/undef_macros.h
deleted file mode 100644
index c880f063771..00000000000
--- a/client/undef_macros.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/** @file undef_macros.h remove mongo implementation macros after using */
-
-/* Copyright 2009 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// If you define a new global un-prefixed macro, please add it here and in redef_macros
-
-// #pragma once // this file is intended to be processed multiple times
-
-#if !defined (MONGO_EXPOSE_MACROS)
-
-#ifdef MONGO_MACROS_PUSHED
-
-// util/allocator.h
-#undef malloc
-#pragma pop_macro("malloc")
-#undef realloc
-#pragma pop_macro("realloc")
-
-// util/assert_util.h
-#undef assert
-#pragma pop_macro("assert")
-#undef dassert
-#pragma pop_macro("dassert")
-#undef wassert
-#pragma pop_macro("wassert")
-#undef massert
-#pragma pop_macro("massert")
-#undef uassert
-#pragma pop_macro("uassert")
-#undef BOOST_CHECK_EXCEPTION
-#undef verify
-#pragma pop_macro("verify")
-#undef DESTRUCTOR_GUARD
-#pragma pop_macro("DESTRUCTOR_GUARD")
-
-// util/goodies.h
-#undef PRINT
-#pragma pop_macro("PRINT")
-#undef PRINTFL
-#pragma pop_macro("PRINTFL")
-#undef asctime
-#pragma pop_macro("asctime")
-#undef gmtime
-#pragma pop_macro("gmtime")
-#undef localtime
-#pragma pop_macro("localtime")
-#undef ctime
-#pragma pop_macro("ctime")
-
-// util/debug_util.h
-#undef DEV
-#pragma pop_macro("DEV")
-#undef DEBUGGING
-#pragma pop_macro("DEBUGGING")
-#undef SOMETIMES
-#pragma pop_macro("SOMETIMES")
-#undef OCCASIONALLY
-#pragma pop_macro("OCCASIONALLY")
-#undef RARELY
-#pragma pop_macro("RARELY")
-#undef ONCE
-#pragma pop_macro("ONCE")
-
-// util/log.h
-#undef LOG
-#pragma pop_macro("LOG")
-
-#undef MONGO_MACROS_PUSHED
-#endif
-#endif