summaryrefslogtreecommitdiff
path: root/src/mongo/executor/connection_pool_test.cpp
diff options
context:
space:
mode:
authorAndrew Morrow <acm@mongodb.com>2017-04-08 14:02:46 -0400
committerAndrew Morrow <acm@mongodb.com>2017-04-13 18:13:31 -0400
commit3edc5558fa1728761549c775350a2e17fb68f8ab (patch)
tree0054521588c112db14355a889c71c8e665dcbf45 /src/mongo/executor/connection_pool_test.cpp
parentcc954e9e1d88b30d1ab89ee3bbbd9db0bb15263d (diff)
downloadmongo-3edc5558fa1728761549c775350a2e17fb68f8ab.tar.gz
SERVER-28664 Use pool connections in MRU order
Diffstat (limited to 'src/mongo/executor/connection_pool_test.cpp')
-rw-r--r--src/mongo/executor/connection_pool_test.cpp174
1 files changed, 174 insertions, 0 deletions
diff --git a/src/mongo/executor/connection_pool_test.cpp b/src/mongo/executor/connection_pool_test.cpp
index 6a857602ca6..a3b5cca24a8 100644
--- a/src/mongo/executor/connection_pool_test.cpp
+++ b/src/mongo/executor/connection_pool_test.cpp
@@ -27,12 +27,17 @@
#include "mongo/platform/basic.h"
+#include <algorithm>
+#include <random>
+#include <stack>
+
#include "mongo/executor/connection_pool_test_fixture.h"
#include "mongo/executor/connection_pool.h"
#include "mongo/stdx/future.h"
#include "mongo/stdx/memory.h"
#include "mongo/unittest/unittest.h"
+#include "mongo/util/scopeguard.h"
namespace mongo {
namespace executor {
@@ -95,6 +100,175 @@ TEST_F(ConnectionPoolTest, SameConn) {
}
/**
+ * Verify that connections are obtained in MRU order.
+ */
+TEST_F(ConnectionPoolTest, ConnectionsAreAcquiredInMRUOrder) {
+ ConnectionPool pool(stdx::make_unique<PoolImpl>(), "test pool");
+
+ // Obtain a set of connections
+ constexpr size_t kSize = 100;
+ std::vector<ConnectionPool::ConnectionHandle> connections;
+
+ // Ensure that no matter how we leave the test, we mark any
+ // checked out connections as OK before implicity returning them
+ // to the pool by destroying the 'connections' vector. Otherwise,
+ // this test would cause an invariant failure instead of a normal
+ // test failure if it fails, which would be confusing.
+ const auto guard = MakeGuard([&] {
+ while (!connections.empty()) {
+ try {
+ ConnectionPool::ConnectionHandle conn = std::move(connections.back());
+ connections.pop_back();
+ conn->indicateSuccess();
+ } catch (...) {
+ }
+ }
+ });
+
+ for (size_t i = 0; i != kSize; ++i) {
+ ConnectionImpl::pushSetup(Status::OK());
+ pool.get(HostAndPort(),
+ Milliseconds(5000),
+ [&](StatusWith<ConnectionPool::ConnectionHandle> swConn) {
+ ASSERT(swConn.isOK());
+ connections.push_back(std::move(swConn.getValue()));
+ });
+ }
+
+ // Shuffle them into a random order
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ std::shuffle(connections.begin(), connections.end(), rng);
+
+ // Return them to the pool in that random order, recording IDs in a stack
+ std::stack<size_t> ids;
+ while (!connections.empty()) {
+ ConnectionPool::ConnectionHandle conn = std::move(connections.back());
+ connections.pop_back();
+ ids.push(static_cast<ConnectionImpl*>(conn.get())->id());
+ conn->indicateSuccess();
+ }
+
+ // Re-obtain the connections. They should come back in the same order
+ // as the IDs in the stack, since the pool returns them in MRU order.
+ for (size_t i = 0; i != kSize; ++i) {
+ ConnectionImpl::pushSetup(Status::OK());
+ pool.get(HostAndPort(),
+ Milliseconds(5000),
+ [&](StatusWith<ConnectionPool::ConnectionHandle> swConn) {
+ ASSERT(swConn.isOK());
+ const auto id = CONN2ID(swConn);
+ connections.push_back(std::move(swConn.getValue()));
+ ASSERT(id == ids.top());
+ ids.pop();
+ });
+ }
+}
+
+/**
+ * Verify that recently used connections are not purged.
+ */
+TEST_F(ConnectionPoolTest, ConnectionsNotUsedRecentlyArePurged) {
+ ConnectionPool::Options options;
+ options.minConnections = 0;
+ options.refreshRequirement = Milliseconds(1000);
+ options.refreshTimeout = Milliseconds(5000);
+ options.hostTimeout = Minutes(1);
+ ConnectionPool pool(stdx::make_unique<PoolImpl>(), "test pool", options);
+
+ ASSERT_EQ(pool.getNumConnectionsPerHost(HostAndPort()), 0U);
+
+ // Obtain a set of connections
+ constexpr size_t kSize = 100;
+ std::vector<ConnectionPool::ConnectionHandle> connections;
+
+ // Ensure that no matter how we leave the test, we mark any
+ // checked out connections as OK before implicity returning them
+ // to the pool by destroying the 'connections' vector. Otherwise,
+ // this test would cause an invariant failure instead of a normal
+ // test failure if it fails, which would be confusing.
+ const auto guard = MakeGuard([&] {
+ while (!connections.empty()) {
+ try {
+ ConnectionPool::ConnectionHandle conn = std::move(connections.back());
+ connections.pop_back();
+ conn->indicateSuccess();
+ } catch (...) {
+ }
+ }
+ });
+
+ auto now = Date_t::now();
+ PoolImpl::setNow(now);
+
+ // Check out kSize connections from the pool, and record their IDs in a set.
+ std::set<size_t> original_ids;
+ for (size_t i = 0; i != kSize; ++i) {
+ ConnectionImpl::pushSetup(Status::OK());
+ pool.get(HostAndPort(),
+ Milliseconds(5000),
+ [&](StatusWith<ConnectionPool::ConnectionHandle> swConn) {
+ ASSERT(swConn.isOK());
+ original_ids.insert(CONN2ID(swConn));
+ connections.push_back(std::move(swConn.getValue()));
+ });
+ }
+
+ ASSERT_EQ(original_ids.size(), kSize);
+ ASSERT_EQ(pool.getNumConnectionsPerHost(HostAndPort()), kSize);
+
+ // Shuffle them into a random order
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ std::shuffle(connections.begin(), connections.end(), rng);
+
+ // Return them to the pool in that random order.
+ while (!connections.empty()) {
+ ConnectionPool::ConnectionHandle conn = std::move(connections.back());
+ connections.pop_back();
+ conn->indicateSuccess();
+ }
+
+ // Advance the time, but not enough to age out connections. We should still have them all.
+ PoolImpl::setNow(now + Milliseconds(500));
+ ASSERT_EQ(pool.getNumConnectionsPerHost(HostAndPort()), kSize);
+
+ // Re-obtain a quarter of the connections, and record their IDs in a set.
+ std::set<size_t> reacquired_ids;
+ for (size_t i = 0; i < kSize / 4; ++i) {
+ ConnectionImpl::pushSetup(Status::OK());
+ pool.get(HostAndPort(),
+ Milliseconds(5000),
+ [&](StatusWith<ConnectionPool::ConnectionHandle> swConn) {
+ ASSERT(swConn.isOK());
+ reacquired_ids.insert(CONN2ID(swConn));
+ connections.push_back(std::move(swConn.getValue()));
+ });
+ }
+
+ ASSERT_EQ(reacquired_ids.size(), kSize / 4);
+ ASSERT(std::includes(
+ original_ids.begin(), original_ids.end(), reacquired_ids.begin(), reacquired_ids.end()));
+ ASSERT_EQ(pool.getNumConnectionsPerHost(HostAndPort()), kSize);
+
+ // Put them right back in.
+ while (!connections.empty()) {
+ ConnectionPool::ConnectionHandle conn = std::move(connections.back());
+ connections.pop_back();
+ conn->indicateSuccess();
+ }
+
+ // We should still have all of them in the pool
+ ASSERT_EQ(pool.getNumConnectionsPerHost(HostAndPort()), kSize);
+
+ // Advance across the host timeout for the 75 connections we
+ // didn't use. Afterwards, the pool should contain only those
+ // kSize/4 connections we used above.
+ PoolImpl::setNow(now + Milliseconds(1000));
+ ASSERT_EQ(pool.getNumConnectionsPerHost(HostAndPort()), kSize / 4);
+}
+
+/**
* Verify that a failed connection isn't returned to the pool
*/
TEST_F(ConnectionPoolTest, FailedConnDifferentConn) {