Skip to content

CDRIVER-4663 Eagerly create minPoolSize connections #1520

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libmongoc/doc/mongoc_client_pool_t.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ Example
mongoc_client_pool_set_server_api
mongoc_client_pool_set_ssl_opts
mongoc_client_pool_try_pop
mongoc_client_pool_warmup

22 changes: 22 additions & 0 deletions src/libmongoc/doc/mongoc_client_pool_warmup.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
:man_page: mongoc_client_pool_warmup

mongoc_client_pool_warmup()
===========================

Synopsis
--------

.. code-block:: c

bool
mongoc_client_pool_warmup (mongoc_client_pool_t *pool, size_t num_to_warmup);

Eagerly connect ``num_to_warmup`` clients in the pool to the MongoDB servers in the pool's topology.

Parameters
----------

* ``pool``: A :symbol:`mongoc_client_pool_t`.
* ``num_to_warmup``: The number of clients to eagerly connect to the MongoDB servers in the pool's topology.

.. include:: includes/mongoc_client_pool_thread_safe.txt
71 changes: 71 additions & 0 deletions src/libmongoc/src/mongoc/mongoc-client-pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,77 @@ mongoc_client_pool_try_pop (mongoc_client_pool_t *pool)
RETURN (client);
}

static bool
_mongoc_connect_client_to_servers (mongoc_client_t *client)
{
bool ok = true;
bson_error_t error = {0};
bson_t *ping_command = BCON_NEW ("ping", BCON_INT32 (1));
size_t num_servers = 0;
mongoc_server_description_t **descriptions =
mongoc_client_get_server_descriptions (client, &num_servers);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest sending a ping to one server before calling mongoc_client_get_server_descriptions.

mongoc_client_get_server_descriptions does not connect to a server. If server monitoring has not yet returned, I expect it will return the initial set of server descriptions from the URI.

Example: if the URI is mongodb://localhost:27017,localhost:27018, but there are three nodes in the replica set, mongoc_client_get_server_descriptions may only return the two servers in the URI (if monitoring has not yet occurred). Once a hello response is processed from the two known servers, the third server will be added to the topology description.

This is applied in the suggested C++ implementation.


for (size_t i = 0; i < num_servers; i++) {
ok = mongoc_client_command_simple_with_server_id (client,
"admin",
ping_command,
NULL,
descriptions[i]->id,
NULL,
&error);
if (!ok) {
MONGOC_WARNING ("failed to ping server during client pool warmup: %s",
error.message);
goto done;
}
}

done:
bson_destroy (ping_command);
mongoc_server_descriptions_destroy_all (descriptions, num_servers);
return ok;
}

bool
mongoc_client_pool_warmup (mongoc_client_pool_t *pool, size_t num_to_warmup)
{
mongoc_client_t **clients = bson_malloc0 (num_to_warmup * sizeof (*clients));
bool ok = true;

BSON_ASSERT (pool);

for (size_t i = 0; i < num_to_warmup; i++) {
bson_mutex_lock (&pool->mutex);
size_t size = pool->size;
size_t max_pool_size = pool->max_pool_size;
bson_mutex_unlock (&pool->mutex);

if ((size >= max_pool_size) || (size >= num_to_warmup)) {
goto done;
}

clients[i] = mongoc_client_pool_pop (pool);
if (!clients[i]) {
ok = false;
goto done;
}

ok = _mongoc_connect_client_to_servers (clients[i]);
if (!ok) {
goto done;
}
}

done:
for (size_t i = 0; i < num_to_warmup; i++) {
if (!clients[i]) {
break;
}
mongoc_client_pool_push (pool, clients[i]);
}
bson_free (clients);
return ok;
}

void
mongoc_client_pool_push (mongoc_client_pool_t *pool, mongoc_client_t *client)
Expand Down
2 changes: 2 additions & 0 deletions src/libmongoc/src/mongoc/mongoc-client-pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ mongoc_client_pool_pop (mongoc_client_pool_t *pool)
BSON_GNUC_WARN_UNUSED_RESULT;
MONGOC_EXPORT (void)
mongoc_client_pool_push (mongoc_client_pool_t *pool, mongoc_client_t *client);
MONGOC_EXPORT (bool)
mongoc_client_pool_warmup (mongoc_client_pool_t *pool, size_t num_to_warmup);
MONGOC_EXPORT (mongoc_client_t *)
mongoc_client_pool_try_pop (mongoc_client_pool_t *pool)
BSON_GNUC_WARN_UNUSED_RESULT;
Expand Down
83 changes: 83 additions & 0 deletions src/libmongoc/tests/test-mongoc-client-pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,87 @@ test_client_pool_max_pool_size_exceeded (void)
bson_free (args);
}

static void
_test_client_pool_warmup_with_maxpoolsize (size_t max_pool_size,
size_t num_to_warmup)
{
mongoc_uri_t *uri = NULL;
mongoc_client_pool_t *pool = NULL;
char *uri_str = bson_strdup_printf ("mongodb://127.0.0.1/?maxpoolsize=%zu",
max_pool_size);

uri = mongoc_uri_new (uri_str);

pool = test_framework_client_pool_new_from_uri (uri, NULL);
ASSERT_WITH_MSG (mongoc_client_pool_warmup (pool, num_to_warmup),
"failed to warmup to client pool");

/* We want the size of the pool to equal the number to warmup unless that
* would exceed the max pool size, in which case we want the size of the pool
* to equal the max pool size */
size_t size = mongoc_client_pool_get_size (pool);
BSON_ASSERT (size <= max_pool_size);
if (size != num_to_warmup) {
BSON_ASSERT (size == max_pool_size);
BSON_ASSERT (max_pool_size < num_to_warmup);
}

mongoc_uri_destroy (uri);
bson_free (uri_str);
mongoc_client_pool_destroy (pool);
}

static BSON_THREAD_FUN (pool_pop_push_loop, data)
{
mongoc_client_pool_t *pool = data;

for (size_t i = 0; i < 10; i++) {
mongoc_client_t *client = mongoc_client_pool_pop (pool);
BSON_ASSERT (client);
_mongoc_usleep (i * 100);
mongoc_client_pool_push (pool, client);
}

BSON_THREAD_RETURN;
}

static void
test_client_pool_warmup (void)
{
mongoc_uri_t *uri = NULL;
mongoc_client_pool_t *pool = NULL;
const size_t num_threads = 10;
const size_t num_to_warmup = 20;
bson_thread_t *threads = bson_malloc0 (sizeof (*threads) * num_threads);

/* Ensure correct handling of max_pool_size when warming up n connections */
_test_client_pool_warmup_with_maxpoolsize (1, 1);
_test_client_pool_warmup_with_maxpoolsize (10, 2);
_test_client_pool_warmup_with_maxpoolsize (10, 20);
_test_client_pool_warmup_with_maxpoolsize (100, 100);

/* Ensure there are no race conditions if you are already using the pool with
* clients in different threads */
uri = mongoc_uri_new ("mongodb://127.0.0.1/");
pool = test_framework_client_pool_new_from_uri (uri, NULL);

for (size_t i = 0; i < num_threads; i++) {
ASSERT_CMPINT (
0, ==, mcommon_thread_create (&threads[i], pool_pop_push_loop, pool));
}

ASSERT_WITH_MSG (mongoc_client_pool_warmup (pool, num_to_warmup),
"failed to warmup to client pool");

for (size_t i = 0; i < num_threads; i++) {
ASSERT_CMPINT (0, ==, mcommon_thread_join (threads[i]));
}

mongoc_client_pool_destroy (pool);
mongoc_uri_destroy (uri);
bson_free (threads);
}

void
test_client_pool_install (TestSuite *suite)
{
Expand All @@ -492,6 +573,8 @@ test_client_pool_install (TestSuite *suite)
TestSuite_Add (
suite, "/ClientPool/handshake", test_mongoc_client_pool_handshake);

TestSuite_Add (suite, "/ClientPool/warmup", test_client_pool_warmup);

TestSuite_AddFull (suite,
"/ClientPool/create_client_pool_unused_session",
test_client_pool_create_unused_session,
Expand Down