1
/* Copyright 2012 10gen Inc.
3
* Licensed under the Apache License, Version 2.0 (the "License");
4
* you may not use this file except in compliance with the License.
5
* You may obtain a copy of the License at
7
* http://www.apache.org/licenses/LICENSE-2.0
9
* Unless required by applicable law or agreed to in writing, software
10
* distributed under the License is distributed on an "AS IS" BASIS,
11
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
* See the License for the specific language governing permissions and
13
* limitations under the License.
16
#include "mongo/base/init.h"
17
#include "mongo/client/connpool.h"
18
#include "mongo/dbtests/mock/mock_conn_registry.h"
19
#include "mongo/dbtests/mock/mock_dbclient_connection.h"
20
#include "mongo/platform/cstdint.h"
21
#include "mongo/s/shard.h"
22
#include "mongo/unittest/unittest.h"
25
#include <boost/scoped_ptr.hpp>
26
#include <boost/thread/thread.hpp>
29
* Tests for ShardConnection, particularly in connection pool management.
30
* The tests focuses more on ShardConnection's logic as opposed to testing
31
* the internal connections together, like in client/scoped_db_conn_test.cpp.
34
using boost::scoped_ptr;
35
using mongo::DBClientBase;
36
using mongo::MockRemoteDBServer;
37
using mongo::ShardConnection;
42
// Note: these are all crutch and hopefully will eventually go away
49
DBClientBase *createDirectClient() { return NULL; }
51
void dbexit(ExitCode rc, const char *why){
55
bool haveLocalShardingInfo(const string& ns) {
60
namespace mongo_test {
61
const string TARGET_HOST = "$dummy:27017";
64
* Warning: cannot run in parallel
66
class ShardConnFixture: public mongo::unittest::Test {
69
_maxPoolSizePerHost = mongo::PoolForHost::getMaxPerHost();
71
mongo::ConnectionString::setConnectionHook(
72
mongo::MockConnRegistry::get()->getConnStrHook());
73
_dummyServer = new MockRemoteDBServer(TARGET_HOST);
74
mongo::MockConnRegistry::get()->addServer(_dummyServer);
78
ShardConnection::clearPool();
80
mongo::MockConnRegistry::get()->removeServer(_dummyServer->getServerAddress());
83
mongo::PoolForHost::setMaxPerHost(_maxPoolSizePerHost);
87
_dummyServer->shutdown();
90
void restartServer() {
91
_dummyServer->reboot();
95
static void assertGreaterThan(uint64_t a, uint64_t b) {
96
ASSERT_GREATER_THAN(a, b);
99
static void assertNotEqual(uint64_t a, uint64_t b) {
100
ASSERT_NOT_EQUALS(a, b);
104
* Tries to grab a series of connections from the pool, perform checks on
105
* them, then put them back into the pool. After that, it checks these
106
* connections can be retrieved again from the pool.
108
* @param checkFunc method for comparing new connections and arg2.
109
* @param arg2 the value to pass as the 2nd parameter of checkFunc.
110
* @param newConnsToCreate the number of new connections to make.
112
void checkNewConns(void (*checkFunc)(uint64_t, uint64_t), uint64_t arg2,
113
size_t newConnsToCreate) {
114
vector<ShardConnection*> newConnList;
115
for (size_t x = 0; x < newConnsToCreate; x++) {
116
ShardConnection* newConn = new ShardConnection(TARGET_HOST, "test.user");
117
checkFunc(newConn->get()->getSockCreationMicroSec(), arg2);
118
newConnList.push_back(newConn);
121
const uint64_t oldCreationTime = mongo::curTimeMicros64();
123
for (vector<ShardConnection*>::iterator iter = newConnList.begin();
124
iter != newConnList.end(); ++iter) {
131
// Check that connections created after the purge was put back to the pool.
132
for (size_t x = 0; x < newConnsToCreate; x++) {
133
ShardConnection* newConn = new ShardConnection(TARGET_HOST, "test.user");
134
ASSERT_LESS_THAN(newConn->get()->getSockCreationMicroSec(), oldCreationTime);
135
newConnList.push_back(newConn);
138
for (vector<ShardConnection*>::iterator iter = newConnList.begin();
139
iter != newConnList.end(); ++iter) {
146
MockRemoteDBServer* _dummyServer;
147
uint32_t _maxPoolSizePerHost;
150
TEST_F(ShardConnFixture, BasicShardConnection) {
151
ShardConnection conn1(TARGET_HOST, "test.user");
152
ShardConnection conn2(TARGET_HOST, "test.user");
154
DBClientBase* conn1Ptr = conn1.get();
157
ShardConnection conn3(TARGET_HOST, "test.user");
158
ASSERT_EQUALS(conn1Ptr, conn3.get());
164
TEST_F(ShardConnFixture, InvalidateBadConnInPool) {
165
ShardConnection conn1(TARGET_HOST, "test.user");
166
ShardConnection conn2(TARGET_HOST, "test.user");
167
ShardConnection conn3(TARGET_HOST, "test.user");
172
const uint64_t badCreationTime = mongo::curTimeMicros64();
176
conn2.get()->query("test.user", mongo::Query());
178
catch (const mongo::SocketException&) {
184
checkNewConns(assertGreaterThan, badCreationTime, 10);
187
TEST_F(ShardConnFixture, DontReturnKnownBadConnToPool) {
188
ShardConnection conn1(TARGET_HOST, "test.user");
189
ShardConnection conn2(TARGET_HOST, "test.user");
190
ShardConnection conn3(TARGET_HOST, "test.user");
196
conn3.get()->query("test.user", mongo::Query());
198
catch (const mongo::SocketException&) {
203
const uint64_t badCreationTime = conn3.get()->getSockCreationMicroSec();
205
// attempting to put a 'bad' connection back to the pool
208
checkNewConns(assertGreaterThan, badCreationTime, 10);
211
TEST_F(ShardConnFixture, BadConnClearsPoolWhenKilled) {
212
ShardConnection conn1(TARGET_HOST, "test.user");
213
ShardConnection conn2(TARGET_HOST, "test.user");
214
ShardConnection conn3(TARGET_HOST, "test.user");
220
conn3.get()->query("test.user", mongo::Query());
222
catch (const mongo::SocketException&) {
227
const uint64_t badCreationTime = conn3.get()->getSockCreationMicroSec();
229
// attempting to put a 'bad' connection back to the pool
232
checkNewConns(assertGreaterThan, badCreationTime, 10);
235
TEST_F(ShardConnFixture, KilledGoodConnShouldNotClearPool) {
236
ShardConnection conn1(TARGET_HOST, "test.user");
237
ShardConnection conn2(TARGET_HOST, "test.user");
238
ShardConnection conn3(TARGET_HOST, "test.user");
240
const uint64_t upperBoundCreationTime =
241
conn3.get()->getSockCreationMicroSec();
244
const uint64_t badCreationTime = conn1.get()->getSockCreationMicroSec();
249
ShardConnection conn4(TARGET_HOST, "test.user");
250
ShardConnection conn5(TARGET_HOST, "test.user");
252
ASSERT_GREATER_THAN(conn4.get()->getSockCreationMicroSec(), badCreationTime);
253
ASSERT_LESS_THAN_OR_EQUALS(conn4.get()->getSockCreationMicroSec(),
254
upperBoundCreationTime);
256
ASSERT_GREATER_THAN(conn5.get()->getSockCreationMicroSec(), badCreationTime);
257
ASSERT_LESS_THAN_OR_EQUALS(conn5.get()->getSockCreationMicroSec(),
258
upperBoundCreationTime);
260
checkNewConns(assertGreaterThan, upperBoundCreationTime, 10);
263
TEST_F(ShardConnFixture, InvalidateBadConnEvenWhenPoolIsFull) {
264
mongo::PoolForHost::setMaxPerHost(2);
266
ShardConnection conn1(TARGET_HOST, "test.user");
267
ShardConnection conn2(TARGET_HOST, "test.user");
268
ShardConnection conn3(TARGET_HOST, "test.user");
273
const uint64_t badCreationTime = mongo::curTimeMicros64();
277
conn2.get()->query("test.user", mongo::Query());
279
catch (const mongo::SocketException&) {
285
checkNewConns(assertGreaterThan, badCreationTime, 2);
288
TEST_F(ShardConnFixture, DontReturnConnGoneBadToPool) {
289
ShardConnection conn1(TARGET_HOST, "test.user");
290
const uint64_t conn1CreationTime = conn1.get()->getSockCreationMicroSec();
292
uint64_t conn2CreationTime = 0;
295
ShardConnection conn2(TARGET_HOST, "test.user");
296
conn2CreationTime = conn2.get()->getSockCreationMicroSec();
299
// conn2 gets out of scope without calling done()
302
// conn2 should not have been put back into the pool but it should
303
// also not invalidate older connections since it didn't encounter
304
// a socket exception.
306
ShardConnection conn1Again(TARGET_HOST, "test.user");
307
ASSERT_EQUALS(conn1CreationTime, conn1Again.get()->getSockCreationMicroSec());
309
checkNewConns(assertNotEqual, conn2CreationTime, 10);