Skip to content

Commit b18c21e

Browse files
authored
CDRIVER-4054 support sessions in loadbalanced mode (#821)
1 parent a16343d commit b18c21e

10 files changed

+211
-16
lines changed

src/libmongoc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,7 @@ set (test-libmongoc-sources
960960
${PROJECT_SOURCE_DIR}/tests/test-mongoc-interrupt.c
961961
${PROJECT_SOURCE_DIR}/tests/test-mongoc-linux-distro-scanner.c
962962
${PROJECT_SOURCE_DIR}/tests/test-mongoc-list.c
963+
${PROJECT_SOURCE_DIR}/tests/test-mongoc-loadbalanced.c
963964
${PROJECT_SOURCE_DIR}/tests/test-mongoc-log.c
964965
${PROJECT_SOURCE_DIR}/tests/test-mongoc-long-namespace.c
965966
${PROJECT_SOURCE_DIR}/tests/test-mongoc-matcher.c

src/libmongoc/src/mongoc/mongoc-cmd.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,7 @@ _mongoc_cmd_parts_assemble_mongod (mongoc_cmd_parts_t *parts,
626626
break;
627627
case MONGOC_TOPOLOGY_SHARDED:
628628
case MONGOC_TOPOLOGY_UNKNOWN:
629+
case MONGOC_TOPOLOGY_LOAD_BALANCED:
629630
case MONGOC_TOPOLOGY_DESCRIPTION_TYPES:
630631
default:
631632
/* must not call this function w/ sharded or unknown topology type */

src/libmongoc/src/mongoc/mongoc-read-prefs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ assemble_query (const mongoc_read_prefs_t *read_prefs,
378378
break;
379379

380380
case MONGOC_TOPOLOGY_SHARDED:
381+
case MONGOC_TOPOLOGY_LOAD_BALANCED:
381382
_apply_read_preferences_mongos (read_prefs, query_bson, result);
382383
break;
383384

src/libmongoc/src/mongoc/mongoc-server-description-private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ typedef enum {
5454
MONGOC_SERVER_RS_ARBITER,
5555
MONGOC_SERVER_RS_OTHER,
5656
MONGOC_SERVER_RS_GHOST,
57+
MONGOC_SERVER_LOAD_BALANCER,
5758
MONGOC_SERVER_DESCRIPTION_TYPES,
5859
} mongoc_server_description_type_t;
5960

src/libmongoc/src/mongoc/mongoc-server-description.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,8 @@ mongoc_server_description_type (const mongoc_server_description_t *description)
361361
return "RSOther";
362362
case MONGOC_SERVER_RS_GHOST:
363363
return "RSGhost";
364+
case MONGOC_SERVER_LOAD_BALANCER:
365+
return "LoadBalancer";
364366
case MONGOC_SERVER_DESCRIPTION_TYPES:
365367
default:
366368
MONGOC_ERROR ("Invalid mongoc_server_description_t type");

src/libmongoc/src/mongoc/mongoc-topology-description-private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ typedef enum {
3232
MONGOC_TOPOLOGY_RS_NO_PRIMARY,
3333
MONGOC_TOPOLOGY_RS_WITH_PRIMARY,
3434
MONGOC_TOPOLOGY_SINGLE,
35+
MONGOC_TOPOLOGY_LOAD_BALANCED,
3536
MONGOC_TOPOLOGY_DESCRIPTION_TYPES
3637
} mongoc_topology_description_type_t;
3738

src/libmongoc/src/mongoc/mongoc-topology-description.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ _is_data_node (mongoc_server_description_t *sd)
3636
case MONGOC_SERVER_STANDALONE:
3737
case MONGOC_SERVER_RS_SECONDARY:
3838
case MONGOC_SERVER_RS_PRIMARY:
39+
case MONGOC_SERVER_LOAD_BALANCER:
3940
return true;
4041
case MONGOC_SERVER_RS_OTHER:
4142
case MONGOC_SERVER_RS_ARBITER:
@@ -2139,6 +2140,8 @@ mongoc_topology_description_type (const mongoc_topology_description_t *td)
21392140
return "ReplicaSetWithPrimary";
21402141
case MONGOC_TOPOLOGY_SINGLE:
21412142
return "Single";
2143+
case MONGOC_TOPOLOGY_LOAD_BALANCED:
2144+
return "LoadBalanced";
21422145
case MONGOC_TOPOLOGY_DESCRIPTION_TYPES:
21432146
default:
21442147
fprintf (stderr, "ERROR: Unknown topology type %d\n", td->type);

src/libmongoc/src/mongoc/mongoc-topology.c

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -375,11 +375,6 @@ mongoc_topology_new (const mongoc_uri_t *uri, bool single_threaded)
375375
GOTO (srv_fail);
376376
}
377377

378-
if (!mongoc_uri_finalize_loadbalanced (topology->uri,
379-
&topology->scanner->error)) {
380-
GOTO (srv_fail);
381-
}
382-
383378
topology->srv_polling_last_scan_ms = bson_get_monotonic_time () / 1000;
384379
/* TODO (CDRIVER-4047) use BSON_MIN */
385380
topology->srv_polling_rescan_interval_ms = BSON_MAX (
@@ -394,6 +389,11 @@ mongoc_topology_new (const mongoc_uri_t *uri, bool single_threaded)
394389
topology_valid = true;
395390
}
396391

392+
if (!mongoc_uri_finalize_loadbalanced (topology->uri,
393+
&topology->scanner->error)) {
394+
topology_valid = false;
395+
}
396+
397397
/*
398398
* Set topology type from URI:
399399
* + if directConnection=true
@@ -413,7 +413,12 @@ mongoc_topology_new (const mongoc_uri_t *uri, bool single_threaded)
413413
has_directconnection &&
414414
mongoc_uri_get_option_as_bool (uri, MONGOC_URI_DIRECTCONNECTION, false);
415415
hl = mongoc_uri_get_hosts (topology->uri);
416-
if (service && !has_directconnection) {
416+
/* If loadBalanced is enabled, directConnection is disabled. This was
417+
* validated in mongoc_uri_finalize_loadbalanced. */
418+
if (mongoc_uri_get_option_as_bool (
419+
topology->uri, MONGOC_URI_LOADBALANCED, false)) {
420+
init_type = MONGOC_TOPOLOGY_LOAD_BALANCED;
421+
} else if (service && !has_directconnection) {
417422
init_type = MONGOC_TOPOLOGY_UNKNOWN;
418423
} else if (has_directconnection) {
419424
if (directconnection) {
@@ -653,11 +658,6 @@ mongoc_topology_should_rescan_srv (mongoc_topology_t *topology)
653658
return false;
654659
}
655660

656-
/* TODO: rely on topology->description.type instead of URI option. */
657-
if (mongoc_uri_get_option_as_bool (
658-
topology->uri, MONGOC_URI_LOADBALANCED, false)) {
659-
return false;
660-
}
661661

662662
if ((topology->description.type != MONGOC_TOPOLOGY_SHARDED) &&
663663
(topology->description.type != MONGOC_TOPOLOGY_UNKNOWN)) {
@@ -1436,15 +1436,18 @@ _mongoc_topology_pop_server_session (mongoc_topology_t *topology,
14361436
int64_t timeout;
14371437
mongoc_server_session_t *ss = NULL;
14381438
mongoc_topology_description_t *td;
1439+
bool loadbalanced;
14391440

14401441
ENTRY;
14411442

14421443
bson_mutex_lock (&topology->mutex);
14431444

14441445
td = &topology->description;
14451446
timeout = td->session_timeout_minutes;
1447+
loadbalanced = td->type == MONGOC_TOPOLOGY_LOAD_BALANCED;
14461448

1447-
if (timeout == MONGOC_NO_SESSIONS) {
1449+
/* When the topology type is LoadBalanced, sessions are always supported. */
1450+
if (!loadbalanced && timeout == MONGOC_NO_SESSIONS) {
14481451
/* if needed, connect and check for session timeout again */
14491452
if (!mongoc_topology_description_has_data_node (td)) {
14501453
bson_mutex_unlock (&topology->mutex);
@@ -1470,6 +1473,11 @@ _mongoc_topology_pop_server_session (mongoc_topology_t *topology,
14701473
while (topology->session_pool) {
14711474
ss = topology->session_pool;
14721475
CDL_DELETE (topology->session_pool, ss);
1476+
/* Sessions do not expire when the topology type is load balanced. */
1477+
if (loadbalanced) {
1478+
break;
1479+
}
1480+
14731481
if (_mongoc_server_session_timed_out (ss, timeout)) {
14741482
_mongoc_server_session_destroy (ss);
14751483
ss = NULL;
@@ -1503,17 +1511,20 @@ _mongoc_topology_push_server_session (mongoc_topology_t *topology,
15031511
{
15041512
int64_t timeout;
15051513
mongoc_server_session_t *ss;
1514+
bool loadbalanced;
15061515

15071516
ENTRY;
15081517

15091518
bson_mutex_lock (&topology->mutex);
15101519

15111520
timeout = topology->description.session_timeout_minutes;
1521+
loadbalanced = topology->description.type == MONGOC_TOPOLOGY_LOAD_BALANCED;
15121522

15131523
/* start at back of queue and reap timed-out sessions */
15141524
while (topology->session_pool && topology->session_pool->prev) {
15151525
ss = topology->session_pool->prev;
1516-
if (_mongoc_server_session_timed_out (ss, timeout)) {
1526+
/* Sessions do not expire when the topology type is load balanced. */
1527+
if (!loadbalanced && _mongoc_server_session_timed_out (ss, timeout)) {
15171528
BSON_ASSERT (ss->next); /* silences clang scan-build */
15181529
CDL_DELETE (topology->session_pool, ss);
15191530
_mongoc_server_session_destroy (ss);
@@ -1524,8 +1535,10 @@ _mongoc_topology_push_server_session (mongoc_topology_t *topology,
15241535
}
15251536

15261537
/* If session is expiring or "dirty" (a network error occurred on it), do not
1527-
* return it to the pool. */
1528-
if (_mongoc_server_session_timed_out (server_session, timeout) ||
1538+
* return it to the pool. Sessions do not expire when the topology type is
1539+
* load balanced. */
1540+
if ((!loadbalanced &&
1541+
_mongoc_server_session_timed_out (server_session, timeout)) ||
15291542
server_session->dirty) {
15301543
_mongoc_server_session_destroy (server_session);
15311544
} else {

src/libmongoc/tests/test-libmongoc.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ extern void
272272
test_bson_util_install (TestSuite *suite);
273273
extern void
274274
test_result_install (TestSuite *suite);
275+
extern void
276+
test_loadbalanced_install (TestSuite *suite);
275277

276278
typedef struct {
277279
mongoc_log_level_t level;
@@ -1851,7 +1853,8 @@ mongoc_client_pool_t *
18511853
test_framework_new_default_client_pool ()
18521854
{
18531855
mongoc_uri_t *test_uri = test_framework_get_uri ();
1854-
mongoc_client_pool_t *pool = test_framework_client_pool_new_from_uri (test_uri, NULL);
1856+
mongoc_client_pool_t *pool =
1857+
test_framework_client_pool_new_from_uri (test_uri, NULL);
18551858

18561859
BSON_ASSERT (pool);
18571860
test_framework_set_pool_ssl_opts (pool);
@@ -2905,6 +2908,7 @@ main (int argc, char *argv[])
29052908
test_bson_match_install (&suite);
29062909
test_bson_util_install (&suite);
29072910
test_result_install (&suite);
2911+
test_loadbalanced_install (&suite);
29082912

29092913
ret = TestSuite_Run (&suite);
29102914

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright 2021-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "mongoc/mongoc.h"
18+
#include "mongoc/mongoc-client-session-private.h"
19+
#include "test-conveniences.h"
20+
#include "test-libmongoc.h"
21+
#include "TestSuite.h"
22+
23+
static char *
24+
loadbalanced_uri (void)
25+
{
26+
/* TODO (CDRIVER-4062): This will need to add TLS and auth to the URI when
27+
* run in evergreen. */
28+
return test_framework_getenv ("SINGLE_MONGOS_LB_URI");
29+
}
30+
31+
static void
32+
test_loadbalanced_sessions_supported (void *unused)
33+
{
34+
mongoc_client_t *client;
35+
mongoc_client_session_t *session;
36+
char *uristr = loadbalanced_uri ();
37+
bson_error_t error;
38+
39+
client = mongoc_client_new (uristr);
40+
session = mongoc_client_start_session (client, NULL /* opts */, &error);
41+
ASSERT_OR_PRINT (session, error);
42+
43+
mongoc_client_session_destroy (session);
44+
bson_free (uristr);
45+
mongoc_client_destroy (client);
46+
}
47+
48+
static void
49+
test_loadbalanced_sessions_do_not_expire (void *unused)
50+
{
51+
mongoc_client_t *client;
52+
mongoc_client_session_t *session1;
53+
mongoc_client_session_t *session2;
54+
char *uristr = loadbalanced_uri ();
55+
bson_error_t error;
56+
bson_t *session1_lsid;
57+
bson_t *session2_lsid;
58+
59+
client = mongoc_client_new (uristr);
60+
/* Mock a timeout so session expiration applies. */
61+
client->topology->description.session_timeout_minutes = 1;
62+
63+
/* Start two sessions, to ensure that pooled sessions remain in the pool when
64+
* the pool is accessed. */
65+
session1 = mongoc_client_start_session (client, NULL /* opts */, &error);
66+
ASSERT_OR_PRINT (session1, error);
67+
session2 = mongoc_client_start_session (client, NULL /* opts */, &error);
68+
ASSERT_OR_PRINT (session2, error);
69+
70+
session1_lsid = bson_copy (mongoc_client_session_get_lsid (session1));
71+
session2_lsid = bson_copy (mongoc_client_session_get_lsid (session2));
72+
73+
/* Expire both sessions. */
74+
session1->server_session->last_used_usec = 1;
75+
session2->server_session->last_used_usec = 1;
76+
mongoc_client_session_destroy (session1);
77+
mongoc_client_session_destroy (session2);
78+
79+
/* Get a new session, it should reuse the most recently pushed session2. */
80+
session2 = mongoc_client_start_session (client, NULL /* opts */, &error);
81+
ASSERT_OR_PRINT (session2, error);
82+
if (!bson_equal (mongoc_client_session_get_lsid (session2), session2_lsid)) {
83+
test_error ("Session not reused: %s != %s",
84+
tmp_json (mongoc_client_session_get_lsid (session2)),
85+
tmp_json (session2_lsid));
86+
}
87+
88+
session1 = mongoc_client_start_session (client, NULL /* opts */, &error);
89+
ASSERT_OR_PRINT (session1, error);
90+
if (!bson_equal (mongoc_client_session_get_lsid (session1), session1_lsid)) {
91+
test_error ("Session not reused: %s != %s",
92+
tmp_json (mongoc_client_session_get_lsid (session1)),
93+
tmp_json (session1_lsid));
94+
}
95+
96+
bson_destroy (session1_lsid);
97+
bson_destroy (session2_lsid);
98+
bson_free (uristr);
99+
mongoc_client_session_destroy (session1);
100+
mongoc_client_session_destroy (session2);
101+
mongoc_client_destroy (client);
102+
}
103+
104+
/* Test that invalid loadBalanced URI configurations are validated during client
105+
* construction. */
106+
static void
107+
test_loadbalanced_client_uri_validation (void *unused)
108+
{
109+
mongoc_client_t *client;
110+
mongoc_uri_t *uri;
111+
bson_error_t error;
112+
bool ret;
113+
114+
uri = mongoc_uri_new ("mongodb://localhost:27017");
115+
mongoc_uri_set_option_as_bool (uri, MONGOC_URI_LOADBALANCED, true);
116+
mongoc_uri_set_option_as_bool (uri, MONGOC_URI_DIRECTCONNECTION, true);
117+
client = mongoc_client_new_from_uri (uri);
118+
119+
ret = mongoc_client_command_simple (client,
120+
"admin",
121+
tmp_bson ("{'ping': 1}"),
122+
NULL /* read prefs */,
123+
NULL /* reply */,
124+
&error);
125+
ASSERT_ERROR_CONTAINS (error,
126+
MONGOC_ERROR_SERVER_SELECTION,
127+
MONGOC_ERROR_SERVER_SELECTION_FAILURE,
128+
"URI with \"loadBalanced\" enabled must not contain "
129+
"option \"directConnection\" enabled");
130+
BSON_ASSERT (!ret);
131+
132+
mongoc_uri_destroy (uri);
133+
mongoc_client_destroy (client);
134+
}
135+
136+
static int
137+
skip_if_not_loadbalanced (void)
138+
{
139+
char *val = loadbalanced_uri ();
140+
if (!val) {
141+
return 0;
142+
}
143+
bson_free (val);
144+
return 1;
145+
}
146+
147+
void
148+
test_loadbalanced_install (TestSuite *suite)
149+
{
150+
TestSuite_AddFull (suite,
151+
"/loadbalanced/sessions/supported",
152+
test_loadbalanced_sessions_supported,
153+
NULL /* ctx */,
154+
NULL /* dtor */,
155+
skip_if_not_loadbalanced);
156+
TestSuite_AddFull (suite,
157+
"/loadbalanced/sessions/do_not_expire",
158+
test_loadbalanced_sessions_do_not_expire,
159+
NULL /* ctx */,
160+
NULL /* dtor */,
161+
skip_if_not_loadbalanced);
162+
TestSuite_AddFull (suite,
163+
"/loadbalanced/client_uri_validation",
164+
test_loadbalanced_client_uri_validation,
165+
NULL /* ctx */,
166+
NULL /* dtor */,
167+
NULL);
168+
}

0 commit comments

Comments
 (0)