1 /* connpool.cpp
2  */
3 
4 
5 /**
6  *    Copyright (C) 2018-present MongoDB, Inc.
7  *
8  *    This program is free software: you can redistribute it and/or modify
9  *    it under the terms of the Server Side Public License, version 1,
10  *    as published by MongoDB, Inc.
11  *
12  *    This program is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    Server Side Public License for more details.
16  *
17  *    You should have received a copy of the Server Side Public License
18  *    along with this program. If not, see
19  *    <http://www.mongodb.com/licensing/server-side-public-license>.
20  *
21  *    As a special exception, the copyright holders give permission to link the
22  *    code of portions of this program with the OpenSSL library under certain
23  *    conditions as described in each individual source file and distribute
24  *    linked combinations including the program with the OpenSSL library. You
25  *    must comply with the Server Side Public License in all respects for
26  *    all of the code used other than as permitted herein. If you modify file(s)
27  *    with this exception, you may extend this exception to your version of the
28  *    file(s), but you are not obligated to do so. If you do not wish to do so,
29  *    delete this exception statement from your version. If you delete this
30  *    exception statement from all source files in the program, then also delete
31  *    it in the license file.
32  */
33 
34 // _ todo: reconnect?
35 
36 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork
37 
38 #include "mongo/platform/basic.h"
39 
40 #include "mongo/client/connpool.h"
41 
42 #include <limits>
43 #include <string>
44 
45 #include "mongo/client/connection_string.h"
46 #include "mongo/client/global_conn_pool.h"
47 #include "mongo/client/replica_set_monitor.h"
48 #include "mongo/executor/connection_pool_stats.h"
49 #include "mongo/stdx/chrono.h"
50 #include "mongo/util/exit.h"
51 #include "mongo/util/log.h"
52 #include "mongo/util/net/socket_exception.h"
53 
54 #if !defined(__has_feature)
55 #define __has_feature(x) 0
56 #endif
57 
58 #if __has_feature(address_sanitizer)
59 #include <sanitizer/lsan_interface.h>
60 #endif
61 
62 namespace mongo {
63 
64 namespace {
65 const int kDefaultIdleTimeout = std::numeric_limits<int>::max();
66 const int kDefaultMaxInUse = std::numeric_limits<int>::max();
67 }  // namespace
68 
69 using std::endl;
70 using std::list;
71 using std::map;
72 using std::set;
73 using std::string;
74 using std::vector;
75 
76 // ------ PoolForHost ------
77 
PoolForHost()78 PoolForHost::PoolForHost()
79     : _created(0),
80       _minValidCreationTimeMicroSec(0),
81       _type(ConnectionString::INVALID),
82       _maxPoolSize(kPoolSizeUnlimited),
83       _maxInUse(kDefaultMaxInUse),
84       _checkedOut(0),
85       _badConns(0),
86       _parentDestroyed(false),
87       _inShutdown(false) {}
88 
~PoolForHost()89 PoolForHost::~PoolForHost() {
90     clear();
91 }
92 
clear()93 void PoolForHost::clear() {
94     if (!_parentDestroyed) {
95         logNoCache() << "Dropping all pooled connections to " << _hostName << "(with timeout of "
96                      << _socketTimeoutSecs << " seconds)";
97     }
98 
99     _pool = decltype(_pool){};
100 }
101 
done(DBConnectionPool * pool,DBClientBase * c_raw)102 void PoolForHost::done(DBConnectionPool* pool, DBClientBase* c_raw) {
103     std::unique_ptr<DBClientBase> c{c_raw};
104     const bool isFailed = c->isFailed();
105 
106     --_checkedOut;
107 
108     // Remember that this host had a broken connection for later
109     if (isFailed) {
110         reportBadConnectionAt(c->getSockCreationMicroSec());
111     }
112 
113     // Another (later) connection was reported as broken to this host
114     bool isBroken = c->getSockCreationMicroSec() < _minValidCreationTimeMicroSec;
115     if (isFailed || isBroken) {
116         _badConns++;
117         logNoCache() << "Ending connection to host " << _hostName << "(with timeout of "
118                      << _socketTimeoutSecs << " seconds)"
119                      << " due to bad connection status; " << openConnections()
120                      << " connections to that host remain open";
121         pool->onDestroy(c.get());
122     } else if (_maxPoolSize >= 0 && static_cast<int>(_pool.size()) >= _maxPoolSize) {
123         // We have a pool size that we need to enforce
124         logNoCache() << "Ending idle connection to host " << _hostName << "(with timeout of "
125                      << _socketTimeoutSecs << " seconds)"
126                      << " because the pool meets constraints; " << openConnections()
127                      << " connections to that host remain open";
128         pool->onDestroy(c.get());
129     } else {
130         // The connection is probably fine, save for later
131         _pool.push(std::move(c));
132     }
133 }
134 
reportBadConnectionAt(uint64_t microSec)135 void PoolForHost::reportBadConnectionAt(uint64_t microSec) {
136     if (microSec != DBClientBase::INVALID_SOCK_CREATION_TIME &&
137         microSec > _minValidCreationTimeMicroSec) {
138         _minValidCreationTimeMicroSec = microSec;
139         logNoCache() << "Detected bad connection created at " << _minValidCreationTimeMicroSec
140                      << " microSec, clearing pool for " << _hostName << " of " << openConnections()
141                      << " connections" << endl;
142         clear();
143     }
144 }
145 
isBadSocketCreationTime(uint64_t microSec)146 bool PoolForHost::isBadSocketCreationTime(uint64_t microSec) {
147     return microSec != DBClientBase::INVALID_SOCK_CREATION_TIME &&
148         microSec <= _minValidCreationTimeMicroSec;
149 }
150 
get(DBConnectionPool * pool,double socketTimeout)151 DBClientBase* PoolForHost::get(DBConnectionPool* pool, double socketTimeout) {
152     while (!_pool.empty()) {
153         auto sc = std::move(_pool.top());
154         _pool.pop();
155 
156         if (!sc.ok()) {
157             _badConns++;
158             pool->onDestroy(sc.conn.get());
159             continue;
160         }
161 
162         verify(sc.conn->getSoTimeout() == socketTimeout);
163 
164         ++_checkedOut;
165         return sc.conn.release();
166     }
167 
168     return nullptr;
169 }
170 
flush()171 void PoolForHost::flush() {
172     clear();
173 }
174 
getStaleConnections(Date_t idleThreshold,vector<DBClientBase * > & stale)175 void PoolForHost::getStaleConnections(Date_t idleThreshold, vector<DBClientBase*>& stale) {
176     vector<StoredConnection> all;
177     while (!_pool.empty()) {
178         StoredConnection c = std::move(_pool.top());
179         _pool.pop();
180 
181         if (c.ok() && !c.addedBefore(idleThreshold)) {
182             all.push_back(std::move(c));
183         } else {
184             _badConns++;
185             stale.emplace_back(c.conn.release());
186         }
187     }
188 
189     for (auto& conn : all) {
190         _pool.push(std::move(conn));
191     }
192 }
193 
194 
StoredConnection(std::unique_ptr<DBClientBase> c)195 PoolForHost::StoredConnection::StoredConnection(std::unique_ptr<DBClientBase> c)
196     : conn(std::move(c)), added(Date_t::now()) {}
197 
ok()198 bool PoolForHost::StoredConnection::ok() {
199     // Poke the connection to see if we're still ok
200     return conn->isStillConnected();
201 }
202 
addedBefore(Date_t time)203 bool PoolForHost::StoredConnection::addedBefore(Date_t time) {
204     return added < time;
205 }
206 
createdOne(DBClientBase * base)207 void PoolForHost::createdOne(DBClientBase* base) {
208     if (_created == 0)
209         _type = base->type();
210     ++_created;
211     // _checkedOut is used to indicate the number of in-use connections so
212     // though we didn't actually check this connection out, we bump it here.
213     ++_checkedOut;
214 }
215 
initializeHostName(const std::string & hostName)216 void PoolForHost::initializeHostName(const std::string& hostName) {
217     if (_hostName.empty()) {
218         _hostName = hostName;
219     }
220 }
221 
waitForFreeConnection(int timeout,stdx::unique_lock<stdx::mutex> & lk)222 void PoolForHost::waitForFreeConnection(int timeout, stdx::unique_lock<stdx::mutex>& lk) {
223     auto condition = [&] { return (numInUse() < _maxInUse || _inShutdown.load()); };
224 
225     if (timeout > 0) {
226         stdx::chrono::seconds timeoutSeconds{timeout};
227 
228         // If we timed out waiting without getting a new connection, throw.
229         uassert(ErrorCodes::ExceededTimeLimit,
230                 str::stream() << "too many connections to " << _hostName << ":" << timeout,
231                 !_cv.wait_for(lk, timeoutSeconds, condition));
232     } else {
233         _cv.wait(lk, condition);
234     }
235 }
236 
notifyWaiters()237 void PoolForHost::notifyWaiters() {
238     _cv.notify_one();
239 }
240 
shutdown()241 void PoolForHost::shutdown() {
242     _inShutdown.store(true);
243     _cv.notify_all();
244 }
245 
246 // ------ DBConnectionPool::Detail ------
247 
248 class DBConnectionPool::Detail {
249 public:
250     template <typename Connect>
get(DBConnectionPool * _this,const std::string & host,double timeout,Connect connect)251     static DBClientBase* get(DBConnectionPool* _this,
252                              const std::string& host,
253                              double timeout,
254                              Connect connect) {
255         while (!(_this->_inShutdown.load())) {
256             // Get a connection from the pool, if there is one.
257             std::unique_ptr<DBClientBase> c(_this->_get(host, timeout));
258             if (c) {
259                 // This call may throw.
260                 _this->onHandedOut(c.get());
261                 return c.release();
262             }
263 
264             // If there are no pooled connections for this host, create a new connection. If
265             // there are too many connections in this pool to make a new one, block until a
266             // connection is released.
267             {
268                 stdx::unique_lock<stdx::mutex> lk(_this->_mutex);
269                 PoolForHost& p = _this->_pools[PoolKey(host, timeout)];
270 
271                 if (p.openConnections() >= _this->_maxInUse) {
272                     log() << "Too many in-use connections; waiting until there are fewer than "
273                           << _this->_maxInUse;
274                     p.waitForFreeConnection(timeout, lk);
275                 } else {
276                     // Drop the lock here, so we can connect without holding it.
277                     // _finishCreate will take the lock again.
278                     lk.unlock();
279 
280                     // Create a new connection and return. All Connect functions
281                     // should throw if they cannot create a connection.
282                     auto c = connect();
283                     invariant(c);
284                     return _this->_finishCreate(host, timeout, c);
285                 }
286             }
287         }
288 
289         // If we get here, we are in shutdown, and it does not matter what we return.
290         invariant(_this->_inShutdown.load());
291         uassert(ErrorCodes::ShutdownInProgress, "connection pool is in shutdown", false);
292         MONGO_UNREACHABLE;
293     }
294 };
295 
296 // ------ DBConnectionPool ------
297 
298 const int PoolForHost::kPoolSizeUnlimited(-1);
299 
DBConnectionPool()300 DBConnectionPool::DBConnectionPool()
301     : _name("dbconnectionpool"),
302       _maxPoolSize(PoolForHost::kPoolSizeUnlimited),
303       _maxInUse(kDefaultMaxInUse),
304       _idleTimeout(kDefaultIdleTimeout),
305       _inShutdown(false),
306       _hooks(new list<DBConnectionHook*>())
307 
308 {}
309 
shutdown()310 void DBConnectionPool::shutdown() {
311     if (!_inShutdown.swap(true)) {
312         stdx::lock_guard<stdx::mutex> L(_mutex);
313         for (auto i = _pools.begin(); i != _pools.end(); i++) {
314             PoolForHost& p = i->second;
315             p.shutdown();
316         }
317     }
318 }
319 
_get(const string & ident,double socketTimeout)320 DBClientBase* DBConnectionPool::_get(const string& ident, double socketTimeout) {
321     uassert(ErrorCodes::ShutdownInProgress,
322             "Can't use connection pool during shutdown",
323             !globalInShutdownDeprecated());
324     stdx::lock_guard<stdx::mutex> L(_mutex);
325     PoolForHost& p = _pools[PoolKey(ident, socketTimeout)];
326     p.setMaxPoolSize(_maxPoolSize);
327     p.setSocketTimeout(socketTimeout);
328     p.initializeHostName(ident);
329     return p.get(this, socketTimeout);
330 }
331 
openConnections(const string & ident,double socketTimeout)332 int DBConnectionPool::openConnections(const string& ident, double socketTimeout) {
333     stdx::lock_guard<stdx::mutex> L(_mutex);
334     PoolForHost& p = _pools[PoolKey(ident, socketTimeout)];
335     return p.openConnections();
336 }
337 
_finishCreate(const string & ident,double socketTimeout,DBClientBase * conn)338 DBClientBase* DBConnectionPool::_finishCreate(const string& ident,
339                                               double socketTimeout,
340                                               DBClientBase* conn) {
341     {
342         stdx::lock_guard<stdx::mutex> L(_mutex);
343         PoolForHost& p = _pools[PoolKey(ident, socketTimeout)];
344         p.setMaxPoolSize(_maxPoolSize);
345         p.initializeHostName(ident);
346         p.createdOne(conn);
347     }
348 
349     try {
350         onCreate(conn);
351         onHandedOut(conn);
352     } catch (std::exception&) {
353         delete conn;
354         throw;
355     }
356 
357     log() << "Successfully connected to " << ident << " (" << openConnections(ident, socketTimeout)
358           << " connections now open to " << ident << " with a " << socketTimeout
359           << " second timeout)";
360 
361     return conn;
362 }
363 
get(const ConnectionString & url,double socketTimeout)364 DBClientBase* DBConnectionPool::get(const ConnectionString& url, double socketTimeout) {
365     auto connect = [&]() {
366         string errmsg;
367         auto c = url.connect(StringData(), errmsg, socketTimeout).release();
368         uassert(13328, _name + ": connect failed " + url.toString() + " : " + errmsg, c);
369         return c;
370     };
371 
372     return Detail::get(this, url.toString(), socketTimeout, connect);
373 }
374 
get(const string & host,double socketTimeout)375 DBClientBase* DBConnectionPool::get(const string& host, double socketTimeout) {
376     auto connect = [&] {
377         const ConnectionString cs(uassertStatusOK(ConnectionString::parse(host)));
378 
379         string errmsg;
380         auto c = cs.connect(StringData(), errmsg, socketTimeout).release();
381         if (!c) {
382             throw SocketException(SocketException::CONNECT_ERROR,
383                                   host,
384                                   11002,
385                                   str::stream() << _name << " error: " << errmsg);
386         }
387 
388         return c;
389     };
390 
391     return Detail::get(this, host, socketTimeout, connect);
392 }
393 
get(const MongoURI & uri,double socketTimeout)394 DBClientBase* DBConnectionPool::get(const MongoURI& uri, double socketTimeout) {
395     auto connect = [&] {
396         string errmsg;
397         std::unique_ptr<DBClientBase> c(uri.connect(StringData(), errmsg, socketTimeout));
398         uassert(40356, _name + ": connect failed " + uri.toString() + " : " + errmsg, c);
399         return c.release();
400     };
401 
402     return Detail::get(this, uri.toString(), socketTimeout, connect);
403 }
404 
getNumAvailableConns(const string & host,double socketTimeout) const405 int DBConnectionPool::getNumAvailableConns(const string& host, double socketTimeout) const {
406     stdx::lock_guard<stdx::mutex> L(_mutex);
407     auto it = _pools.find(PoolKey(host, socketTimeout));
408     return (it == _pools.end()) ? 0 : it->second.numAvailable();
409 }
410 
getNumBadConns(const string & host,double socketTimeout) const411 int DBConnectionPool::getNumBadConns(const string& host, double socketTimeout) const {
412     stdx::lock_guard<stdx::mutex> L(_mutex);
413     auto it = _pools.find(PoolKey(host, socketTimeout));
414     return (it == _pools.end()) ? 0 : it->second.getNumBadConns();
415 }
416 
onRelease(DBClientBase * conn)417 void DBConnectionPool::onRelease(DBClientBase* conn) {
418     if (_hooks->empty()) {
419         return;
420     }
421 
422     for (list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++) {
423         (*i)->onRelease(conn);
424     }
425 }
426 
release(const string & host,DBClientBase * c)427 void DBConnectionPool::release(const string& host, DBClientBase* c) {
428     onRelease(c);
429 
430     stdx::unique_lock<stdx::mutex> lk(_mutex);
431     PoolForHost& p = _pools[PoolKey(host, c->getSoTimeout())];
432     p.done(this, c);
433 
434     lk.unlock();
435     p.notifyWaiters();
436 }
437 
decrementEgress(const string & host,DBClientBase * c)438 void DBConnectionPool::decrementEgress(const string& host, DBClientBase* c) {
439     stdx::lock_guard<stdx::mutex> L(_mutex);
440     PoolForHost& p = _pools[PoolKey(host, c->getSoTimeout())];
441     --p._checkedOut;
442 }
443 
~DBConnectionPool()444 DBConnectionPool::~DBConnectionPool() {
445     // Do not log in destruction, because global connection pools get
446     // destroyed after the logging framework.
447     stdx::lock_guard<stdx::mutex> L(_mutex);
448     for (PoolMap::iterator i = _pools.begin(); i != _pools.end(); i++) {
449         PoolForHost& p = i->second;
450         p._parentDestroyed = true;
451     }
452 
453 #if __has_feature(address_sanitizer)
454     __lsan_ignore_object(_hooks);
455 #endif
456 }
457 
flush()458 void DBConnectionPool::flush() {
459     stdx::lock_guard<stdx::mutex> L(_mutex);
460     for (PoolMap::iterator i = _pools.begin(); i != _pools.end(); i++) {
461         PoolForHost& p = i->second;
462         p.flush();
463     }
464 }
465 
clear()466 void DBConnectionPool::clear() {
467     stdx::lock_guard<stdx::mutex> L(_mutex);
468     LOG(2) << "Removing connections on all pools owned by " << _name << endl;
469     for (PoolMap::iterator iter = _pools.begin(); iter != _pools.end(); ++iter) {
470         iter->second.clear();
471     }
472 }
473 
removeHost(const string & host)474 void DBConnectionPool::removeHost(const string& host) {
475     stdx::lock_guard<stdx::mutex> L(_mutex);
476     LOG(2) << "Removing connections from all pools for host: " << host << endl;
477     for (PoolMap::iterator i = _pools.begin(); i != _pools.end(); ++i) {
478         const string& poolHost = i->first.ident;
479         if (!serverNameCompare()(host, poolHost) && !serverNameCompare()(poolHost, host)) {
480             // hosts are the same
481             i->second.clear();
482         }
483     }
484 }
485 
addHook(DBConnectionHook * hook)486 void DBConnectionPool::addHook(DBConnectionHook* hook) {
487     _hooks->push_back(hook);
488 }
489 
onCreate(DBClientBase * conn)490 void DBConnectionPool::onCreate(DBClientBase* conn) {
491     if (_hooks->size() == 0)
492         return;
493 
494     for (list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++) {
495         (*i)->onCreate(conn);
496     }
497 }
498 
onHandedOut(DBClientBase * conn)499 void DBConnectionPool::onHandedOut(DBClientBase* conn) {
500     if (_hooks->size() == 0)
501         return;
502 
503     for (list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++) {
504         (*i)->onHandedOut(conn);
505     }
506 }
507 
onDestroy(DBClientBase * conn)508 void DBConnectionPool::onDestroy(DBClientBase* conn) {
509     if (_hooks->size() == 0)
510         return;
511 
512     for (list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++) {
513         (*i)->onDestroy(conn);
514     }
515 }
516 
appendConnectionStats(executor::ConnectionPoolStats * stats) const517 void DBConnectionPool::appendConnectionStats(executor::ConnectionPoolStats* stats) const {
518     {
519         stdx::lock_guard<stdx::mutex> lk(_mutex);
520         for (PoolMap::const_iterator i = _pools.begin(); i != _pools.end(); ++i) {
521             if (i->second.numCreated() == 0)
522                 continue;
523 
524             // Mongos may use either a replica set uri or a list of addresses as
525             // the identifier here, so we always take the first server parsed out
526             // as our label for connPoolStats. Note that these stats will collide
527             // with any existing stats for the chosen host.
528             auto uri = ConnectionString::parse(i->first.ident);
529             invariant(uri.isOK());
530             HostAndPort host = uri.getValue().getServers().front();
531 
532             executor::ConnectionStatsPer hostStats{static_cast<size_t>(i->second.numInUse()),
533                                                    static_cast<size_t>(i->second.numAvailable()),
534                                                    static_cast<size_t>(i->second.numCreated()),
535                                                    0};
536             stats->updateStatsForHost("global", host, hostStats);
537         }
538     }
539 }
540 
operator ()(const string & a,const string & b) const541 bool DBConnectionPool::serverNameCompare::operator()(const string& a, const string& b) const {
542     const char* ap = a.c_str();
543     const char* bp = b.c_str();
544 
545     while (true) {
546         if (*ap == '\0' || *ap == '/') {
547             if (*bp == '\0' || *bp == '/')
548                 return false;  // equal strings
549             else
550                 return true;  // a is shorter
551         }
552 
553         if (*bp == '\0' || *bp == '/')
554             return false;  // b is shorter
555 
556         if (*ap < *bp)
557             return true;
558         else if (*ap > *bp)
559             return false;
560 
561         ++ap;
562         ++bp;
563     }
564     verify(false);
565 }
566 
operator ()(const PoolKey & a,const PoolKey & b) const567 bool DBConnectionPool::poolKeyCompare::operator()(const PoolKey& a, const PoolKey& b) const {
568     if (DBConnectionPool::serverNameCompare()(a.ident, b.ident))
569         return true;
570 
571     if (DBConnectionPool::serverNameCompare()(b.ident, a.ident))
572         return false;
573 
574     return a.timeout < b.timeout;
575 }
576 
isConnectionGood(const string & hostName,DBClientBase * conn)577 bool DBConnectionPool::isConnectionGood(const string& hostName, DBClientBase* conn) {
578     if (conn == NULL) {
579         return false;
580     }
581 
582     if (conn->isFailed()) {
583         return false;
584     }
585 
586     {
587         stdx::lock_guard<stdx::mutex> sl(_mutex);
588         PoolForHost& pool = _pools[PoolKey(hostName, conn->getSoTimeout())];
589         if (pool.isBadSocketCreationTime(conn->getSockCreationMicroSec())) {
590             return false;
591         }
592     }
593 
594     return true;
595 }
596 
taskDoWork()597 void DBConnectionPool::taskDoWork() {
598     vector<DBClientBase*> toDelete;
599     auto idleThreshold = Date_t::now() - _idleTimeout;
600     {
601         // we need to get the connections inside the lock
602         // but we can actually delete them outside
603         stdx::lock_guard<stdx::mutex> lk(_mutex);
604         for (PoolMap::iterator i = _pools.begin(); i != _pools.end(); ++i) {
605             i->second.getStaleConnections(idleThreshold, toDelete);
606         }
607     }
608 
609     for (size_t i = 0; i < toDelete.size(); i++) {
610         try {
611             onDestroy(toDelete[i]);
612             delete toDelete[i];
613         } catch (...) {
614             // we don't care if there was a socket error
615         }
616     }
617 }
618 
619 // ------ ScopedDbConnection ------
620 
ScopedDbConnection(const std::string & host,double socketTimeout)621 ScopedDbConnection::ScopedDbConnection(const std::string& host, double socketTimeout)
622     : _host(host),
623       _conn(globalConnPool.get(host, socketTimeout)),
624       _socketTimeoutSecs(socketTimeout) {
625     _setSocketTimeout();
626 }
627 
ScopedDbConnection(const ConnectionString & host,double socketTimeout)628 ScopedDbConnection::ScopedDbConnection(const ConnectionString& host, double socketTimeout)
629     : _host(host.toString()),
630       _conn(globalConnPool.get(host, socketTimeout)),
631       _socketTimeoutSecs(socketTimeout) {
632     _setSocketTimeout();
633 }
634 
ScopedDbConnection(const MongoURI & uri,double socketTimeout)635 ScopedDbConnection::ScopedDbConnection(const MongoURI& uri, double socketTimeout)
636     : _host(uri.toString()),
637       _conn(globalConnPool.get(uri, socketTimeout)),
638       _socketTimeoutSecs(socketTimeout) {
639     _setSocketTimeout();
640 }
641 
done()642 void ScopedDbConnection::done() {
643     if (!_conn) {
644         return;
645     }
646 
647     globalConnPool.release(_host, _conn);
648     _conn = NULL;
649 }
650 
kill()651 void ScopedDbConnection::kill() {
652     globalConnPool.decrementEgress(_host, _conn);
653     delete _conn;
654     _conn = NULL;
655 }
656 
_setSocketTimeout()657 void ScopedDbConnection::_setSocketTimeout() {
658     if (!_conn)
659         return;
660 
661     if (_conn->type() == ConnectionString::MASTER)
662         static_cast<DBClientConnection*>(_conn)->setSoTimeout(_socketTimeoutSecs);
663 }
664 
~ScopedDbConnection()665 ScopedDbConnection::~ScopedDbConnection() {
666     if (_conn) {
667         if (_conn->isFailed()) {
668             if (_conn->getSockCreationMicroSec() == DBClientBase::INVALID_SOCK_CREATION_TIME) {
669                 kill();
670             } else {
671                 // The pool takes care of deleting the failed connection - this
672                 // will also trigger disposal of older connections in the pool
673                 done();
674             }
675         } else {
676             /* see done() comments above for why we log this line */
677             logNoCache() << "scoped connection to " << _conn->getServerAddress()
678                          << " not being returned to the pool" << endl;
679             kill();
680         }
681     }
682 }
683 
clearPool()684 void ScopedDbConnection::clearPool() {
685     globalConnPool.clear();
686 }
687 
688 AtomicInt32 AScopedConnection::_numConnections;
689 
690 }  // namespace mongo
691