Skip to content

Commit 1639ca9

Browse files
committed
CDRIVER-3516 implement waitQueueTimeoutMS for client pools
1 parent ce8ae1f commit 1639ca9

File tree

6 files changed

+83
-3
lines changed

6 files changed

+83
-3
lines changed

src/libmongoc/doc/connection-pooling.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,23 @@ When the driver is in pooled mode, your program's operations are unblocked as so
5151

5252
The pool opens one connection per server for monitoring, and each client opens its own connection to each server it uses for application operations. The background thread re-scans the server topology roughly every 10 seconds. This interval is configurable with ``heartbeatFrequencyMS`` in the connection string. (See :symbol:`mongoc_uri_t`.)
5353

54+
The connection string can also specify ``waitQueueTimeoutMS`` to limit the time that :symbol:`mongoc_client_pool_pop` will wait for a client from the pool. (See :symbol:`mongoc_uri_t`.) If ``waitQueueTimeoutMS`` is specified, then it is necessary to confirm that a client was actually returned:
55+
56+
.. code-block:: c
57+
58+
mongoc_uri_t *uri = mongoc_uri_new (
59+
"mongodb://hostA,hostB/?replicaSet=my_rs&waitQueueTimeoutMS=1000");
60+
61+
mongoc_client_pool_t *pool = mongoc_client_pool_new (uri);
62+
63+
mongoc_client_t *client = mongoc_client_pool_pop (pool);
64+
65+
if (client) {
66+
/* use the client for operations ... */
67+
68+
mongoc_client_pool_push (pool, client);
69+
} else {
70+
/* take appropriate action for a timeout */
71+
}
72+
5473
See :ref:`connection_pool_options` to configure pool size and behavior, and see :symbol:`mongoc_client_pool_t` for an extended example of a multi-threaded program that uses the driver in pooled mode.

src/libmongoc/doc/mongoc_client_pool_pop.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Synopsis
1111
mongoc_client_t *
1212
mongoc_client_pool_pop (mongoc_client_pool_t *pool);
1313
14-
Retrieve a :symbol:`mongoc_client_t` from the client pool, or create one. The total number of clients that can be created from this pool is limited by the URI option "maxPoolSize", default 100. If this number of clients has been created and all are in use, ``mongoc_client_pool_pop`` blocks until another thread returns a client with :symbol:`mongoc_client_pool_push()`.
14+
Retrieve a :symbol:`mongoc_client_t` from the client pool, or create one. The total number of clients that can be created from this pool is limited by the URI option "maxPoolSize", default 100. If this number of clients has been created and all are in use, ``mongoc_client_pool_pop`` blocks until another thread returns a client with :symbol:`mongoc_client_pool_push()`. If the "waitQueueTimeoutMS" URI option was specified with a positive value, then ``mongoc_client_pool_pop`` will return ``NULL`` when the timeout expires.
1515

1616
The returned :symbol:`mongoc_client_t` must be returned to the pool with :symbol:`mongoc_client_pool_push()`.
1717

src/libmongoc/doc/mongoc_uri_t.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ MONGOC_URI_MAXPOOLSIZE maxpoolsize The
193193
MONGOC_URI_MINPOOLSIZE minpoolsize Deprecated. This option's behavior does not match its name, and its actual behavior will likely hurt performance.
194194
MONGOC_URI_MAXIDLETIMEMS maxidletimems Not implemented.
195195
MONGOC_URI_WAITQUEUEMULTIPLE waitqueuemultiple Not implemented.
196-
MONGOC_URI_WAITQUEUETIMEOUTMS waitqueuetimeoutms Not implemented.
196+
MONGOC_URI_WAITQUEUETIMEOUTMS waitqueuetimeoutms The maximum time to wait for a client to become available from the pool.
197197
========================================== ================================= =========================================================================================================================================================================================================================
198198

199199
.. _mongoc_uri_t_write_concern_options:

src/libmongoc/src/mongoc/mongoc-client-pool.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,21 @@ mongoc_client_t *
248248
mongoc_client_pool_pop (mongoc_client_pool_t *pool)
249249
{
250250
mongoc_client_t *client;
251+
int32_t wait_queue_timeout_ms;
252+
int64_t expire_at_ms = -1;
253+
int64_t now_ms;
254+
int r;
251255

252256
ENTRY;
253257

254258
BSON_ASSERT (pool);
255259

260+
wait_queue_timeout_ms = mongoc_uri_get_option_as_int32 (
261+
pool->uri, MONGOC_URI_WAITQUEUETIMEOUTMS, -1);
262+
if (wait_queue_timeout_ms > 0) {
263+
expire_at_ms =
264+
(bson_get_monotonic_time () / 1000) + wait_queue_timeout_ms;
265+
}
256266
bson_mutex_lock (&pool->mutex);
257267

258268
again:
@@ -262,12 +272,26 @@ mongoc_client_pool_pop (mongoc_client_pool_t *pool)
262272
_initialize_new_client (pool, client);
263273
pool->size++;
264274
} else {
265-
mongoc_cond_wait (&pool->cond, &pool->mutex);
275+
if (wait_queue_timeout_ms > 0) {
276+
now_ms = bson_get_monotonic_time () / 1000;
277+
if (now_ms < expire_at_ms) {
278+
r = mongoc_cond_timedwait (
279+
&pool->cond, &pool->mutex, expire_at_ms - now_ms);
280+
if (mongo_cond_ret_is_timedout (r)) {
281+
GOTO (done);
282+
}
283+
} else {
284+
GOTO (done);
285+
}
286+
} else {
287+
mongoc_cond_wait (&pool->cond, &pool->mutex);
288+
}
266289
GOTO (again);
267290
}
268291
}
269292

270293
_start_scanner_if_needed (pool);
294+
done:
271295
bson_mutex_unlock (&pool->mutex);
272296

273297
RETURN (client);

src/libmongoc/src/mongoc/mongoc-thread-private.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ mongoc_cond_timedwait (pthread_cond_t *cond,
4949

5050
return pthread_cond_timedwait (cond, mutex, &to);
5151
}
52+
static BSON_INLINE bool
53+
mongo_cond_ret_is_timedout (int ret) {
54+
return ret == ETIMEDOUT;
55+
}
5256
#define mongoc_cond_destroy pthread_cond_destroy
5357
#else
5458
#define mongoc_cond_t CONDITION_VARIABLE
@@ -73,6 +77,10 @@ mongoc_cond_timedwait (mongoc_cond_t *cond,
7377
}
7478
}
7579
}
80+
static BSON_INLINE bool
81+
mongo_cond_ret_is_timedout (int ret) {
82+
return ret == WSAETIMEDOUT;
83+
}
7684
#define mongoc_cond_signal WakeConditionVariable
7785
#define mongoc_cond_broadcast WakeAllConditionVariable
7886
static BSON_INLINE int

src/libmongoc/tests/test-mongoc-client-pool.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,32 @@ test_mongoc_client_pool_try_pop (void)
4141
mongoc_client_pool_destroy (pool);
4242
}
4343

44+
static void
45+
test_mongoc_client_pool_pop_timeout (void)
46+
{
47+
mongoc_client_pool_t *pool;
48+
mongoc_client_t *client;
49+
mongoc_uri_t *uri;
50+
int64_t start;
51+
int64_t duration_usec;
52+
53+
uri = mongoc_uri_new (
54+
"mongodb://127.0.0.1/?maxpoolsize=1&waitqueuetimeoutms=2000");
55+
pool = mongoc_client_pool_new (uri);
56+
client = mongoc_client_pool_pop (pool);
57+
BSON_ASSERT (client);
58+
start = bson_get_monotonic_time ();
59+
BSON_ASSERT (!mongoc_client_pool_pop (pool));
60+
duration_usec = bson_get_monotonic_time () - start;
61+
/* There is a possibility that the wait is a few milliseconds short. The
62+
* assertion is structured like this since the timeout is a rough lower bound
63+
* and some test environments (e.g., valgrind) might slow things down. */
64+
BSON_ASSERT (duration_usec / 1000 >= 1990);
65+
mongoc_client_pool_push (pool, client);
66+
mongoc_uri_destroy (uri);
67+
mongoc_client_pool_destroy (pool);
68+
}
69+
4470
static void
4571
test_mongoc_client_pool_min_size_zero (void)
4672
{
@@ -341,6 +367,9 @@ test_client_pool_install (TestSuite *suite)
341367
TestSuite_Add (suite, "/ClientPool/basic", test_mongoc_client_pool_basic);
342368
TestSuite_Add (
343369
suite, "/ClientPool/try_pop", test_mongoc_client_pool_try_pop);
370+
TestSuite_Add (suite,
371+
"/ClientPool/pop_timeout",
372+
test_mongoc_client_pool_pop_timeout);
344373
TestSuite_Add (suite,
345374
"/ClientPool/min_size_zero",
346375
test_mongoc_client_pool_min_size_zero);

0 commit comments

Comments
 (0)