Skip to content

Commit 472242b

Browse files
committed
bypass server selection; ensure connections are established in single-threaded
1 parent 06f9fa4 commit 472242b

File tree

3 files changed

+219
-27
lines changed

3 files changed

+219
-27
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ _mongoc_topology_background_monitoring_start (mongoc_topology_t *topology)
148148
_mongoc_topology_description_monitor_opening (&topology->description);
149149
if (topology->description.type == MONGOC_TOPOLOGY_LOAD_BALANCED) {
150150
/* Do not proceed to start monitoring threads. */
151+
TRACE ("%s", "disabling monitoring for load balanced topology");
151152
return;
152153
}
153154

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

Lines changed: 93 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ _mongoc_topology_scanner_setup_err_cb (uint32_t id,
131131

132132
topology = (mongoc_topology_t *) data;
133133

134+
if (topology->description.type == MONGOC_TOPOLOGY_LOAD_BALANCED) {
135+
/* In load balanced mode, scanning is only for connection establishment. It must not modify the topology description. */
136+
MONGOC_DEBUG ("Ignoring scanner error in load balanced mode");
137+
return;
138+
}
139+
134140
mongoc_topology_description_handle_hello (&topology->description,
135141
id,
136142
NULL /* hello reply */,
@@ -167,7 +173,14 @@ _mongoc_topology_scanner_cb (uint32_t id,
167173

168174
topology = (mongoc_topology_t *) data;
169175

176+
if (topology->description.type == MONGOC_TOPOLOGY_LOAD_BALANCED) {
177+
/* In load balanced mode, scanning is only for connection establishment. It must not modify the topology description. */
178+
MONGOC_DEBUG ("Ignoring hello response in callback in load balanced mode");
179+
return;
180+
}
181+
170182
bson_mutex_lock (&topology->mutex);
183+
171184
sd = mongoc_topology_description_server_by_id (
172185
&topology->description, id, NULL);
173186

@@ -818,7 +831,9 @@ _mongoc_topology_do_blocking_scan (mongoc_topology_t *topology,
818831
{
819832
_mongoc_handshake_freeze ();
820833

834+
// LBTODO: don't even bother locking the mutex, note that this is for single-threaded clients only.
821835
bson_mutex_lock (&topology->mutex);
836+
// LBTODO if this is a load balanced cluster, then do not obey cooldown.
822837
mongoc_topology_scan_once (topology, true /* obey cooldown */);
823838
bson_mutex_unlock (&topology->mutex);
824839
mongoc_topology_scanner_get_error (topology->scanner, error);
@@ -945,6 +960,64 @@ mongoc_topology_select (mongoc_topology_t *topology,
945960
}
946961
}
947962

963+
/* Bypasses normal server selection behavior for a load balanced topology. Returns the id of the one load balancer server. Returns 0 on failure.
964+
* Successful post-condition: On a single threaded client, a connection will have been established.
965+
*/
966+
static uint32_t
967+
_mongoc_topology_select_server_id_loadbalanced (mongoc_topology_t *topology,
968+
bson_error_t *error)
969+
{
970+
mongoc_server_description_t *selected_server;
971+
int32_t selected_server_id;
972+
mongoc_topology_scanner_node_t *node;
973+
bson_error_t scanner_error = {0};
974+
975+
bson_mutex_lock (&topology->mutex);
976+
977+
BSON_ASSERT (topology->description.type == MONGOC_TOPOLOGY_LOAD_BALANCED);
978+
979+
/* Emit the opening SDAM events if they have not emitted already. */
980+
_mongoc_topology_description_monitor_opening (&topology->description);
981+
selected_server =
982+
mongoc_topology_description_select (&topology->description,
983+
MONGOC_SS_WRITE,
984+
NULL /* read prefs */,
985+
0 /* local threshold */);
986+
987+
if (!selected_server) {
988+
_mongoc_server_selection_error (
989+
"No suitable server found in load balanced deployment", NULL, error);
990+
bson_mutex_unlock (&topology->mutex);
991+
return 0;
992+
}
993+
994+
selected_server_id = selected_server->id;
995+
bson_mutex_unlock (&topology->mutex);
996+
997+
if (!topology->single_threaded) {
998+
return selected_server_id;
999+
}
1000+
1001+
/* If this is a single threaded topology, we must ensure that a connection is available to this server. Wrapping drivers make the assumption that successful server selection implies a connection is available. */
1002+
node = mongoc_topology_scanner_get_node (topology->scanner, selected_server_id);
1003+
if (!node) {
1004+
_mongoc_server_selection_error ("Topology scanner in invalid state; cannot find load balancer", NULL, error);
1005+
return 0;
1006+
}
1007+
1008+
if (!node->stream) {
1009+
MONGOC_DEBUG ("server selection performing scan since no connection has been established");
1010+
_mongoc_topology_do_blocking_scan (topology, &scanner_error);
1011+
}
1012+
1013+
if (!node->stream) {
1014+
_mongoc_server_selection_error ("Topology scanner in invalid state; cannot find load balancer", &scanner_error, error);
1015+
return 0;
1016+
}
1017+
1018+
return selected_server_id;
1019+
}
1020+
9481021
/*
9491022
*-------------------------------------------------------------------------
9501023
*
@@ -999,6 +1072,13 @@ mongoc_topology_select_server_id (mongoc_topology_t *topology,
9991072
bson_mutex_unlock (&topology->mutex);
10001073
return 0;
10011074
}
1075+
1076+
if (topology->description.type == MONGOC_TOPOLOGY_LOAD_BALANCED) {
1077+
bson_mutex_unlock (&topology->mutex);
1078+
MONGOC_DEBUG ("bypassing server selection for load balanced topology");
1079+
return _mongoc_topology_select_server_id_loadbalanced (topology, error);
1080+
}
1081+
10021082
bson_mutex_unlock (&topology->mutex);
10031083

10041084
heartbeat_msec = topology->description.heartbeat_msec;
@@ -1011,21 +1091,6 @@ mongoc_topology_select_server_id (mongoc_topology_t *topology,
10111091
if (topology->single_threaded) {
10121092
_mongoc_topology_description_monitor_opening (&topology->description);
10131093

1014-
if (topology->description.type == MONGOC_TOPOLOGY_LOAD_BALANCED) {
1015-
/* Bypass server selection loop. Always select the only server. */
1016-
// LBTODO: this will not work for PHP. Single threaded server selection must imply connection establishment.
1017-
selected_server = mongoc_topology_description_select (
1018-
&topology->description, optype, read_prefs, local_threshold_ms);
1019-
1020-
if (!selected_server) {
1021-
_mongoc_server_selection_error (
1022-
"No suitable server found in load balanced deployment",
1023-
NULL,
1024-
error);
1025-
return 0;
1026-
}
1027-
}
1028-
10291094
tried_once = false;
10301095
next_update = topology->last_scan + heartbeat_msec * 1000;
10311096
if (next_update < loop_start) {
@@ -1123,18 +1188,6 @@ mongoc_topology_select_server_id (mongoc_topology_t *topology,
11231188
selected_server = mongoc_topology_description_select (
11241189
&topology->description, optype, read_prefs, local_threshold_ms);
11251190

1126-
if (topology->description.type == MONGOC_TOPOLOGY_LOAD_BALANCED) {
1127-
/* Bypass server selection loop. Always select the only server. */
1128-
if (!selected_server) {
1129-
bson_mutex_unlock (&topology->mutex);
1130-
_mongoc_server_selection_error (
1131-
"No suitable server found in load balanced deployment",
1132-
NULL,
1133-
error);
1134-
return 0;
1135-
}
1136-
}
1137-
11381191
if (!selected_server) {
11391192
TRACE (
11401193
"server selection requesting an immediate scan, want %s",
@@ -1304,6 +1357,8 @@ mongoc_topology_invalidate_server (mongoc_topology_t *topology,
13041357
{
13051358
BSON_ASSERT (error);
13061359

1360+
// LBTODO: do not update in load balanced mode.
1361+
13071362
bson_mutex_lock (&topology->mutex);
13081363
mongoc_topology_description_invalidate_server (
13091364
&topology->description, id, error);
@@ -1332,6 +1387,14 @@ _mongoc_topology_update_from_handshake (mongoc_topology_t *topology,
13321387

13331388
bson_mutex_lock (&topology->mutex);
13341389

1390+
// LBTODO: do not update the topology if this is load balanced mode.
1391+
if (topology->description.type == MONGOC_TOPOLOGY_LOAD_BALANCED) {
1392+
/* In load balanced mode, scanning is only for connection establishment. It must not modify the topology description. */
1393+
MONGOC_DEBUG ("Ignoring handshake response in load balanced mode");
1394+
bson_mutex_unlock (&topology->mutex);
1395+
return true;
1396+
}
1397+
13351398
/* return false if server was removed from topology */
13361399
has_server = _mongoc_topology_update_no_lock (sd->id,
13371400
&sd->last_hello_response,
@@ -1743,6 +1806,7 @@ _mongoc_topology_handle_app_error (mongoc_topology_t *topology,
17431806
}
17441807

17451808
if (type == MONGOC_SDAM_APP_ERROR_NETWORK) {
1809+
// LBTODO: bypass this in load balanced mode.
17461810
/* Mark server as unknown. */
17471811
mongoc_topology_description_invalidate_server (
17481812
&topology->description, server_id, why);
@@ -1758,6 +1822,7 @@ _mongoc_topology_handle_app_error (mongoc_topology_t *topology,
17581822
return false;
17591823
}
17601824
/* Mark server as unknown. */
1825+
// LBTODO: bypass this in load balanced mode.
17611826
mongoc_topology_description_invalidate_server (
17621827
&topology->description, server_id, why);
17631828
_mongoc_topology_clear_connection_pool (topology, server_id);
@@ -1812,6 +1877,7 @@ _mongoc_topology_handle_app_error (mongoc_topology_t *topology,
18121877
* error and the error's topologyVersion is strictly greater than the
18131878
* current ServerDescription's topologyVersion it MUST replace the
18141879
* server's description with a ServerDescription of type Unknown. */
1880+
// LBTODO: bypass this in load balanced mode.
18151881
mongoc_topology_description_invalidate_server (
18161882
&topology->description, server_id, &cmd_error);
18171883

src/libmongoc/tests/test-mongoc-loadbalanced.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,109 @@ test_loadbalanced_client_uri_validation (void *unused)
133133
mongoc_client_destroy (client);
134134
}
135135

136+
/* Test basic connectivity to a load balanced cluster. */
137+
static void
138+
test_loadbalanced_connect_single (void *unused)
139+
{
140+
mongoc_client_t *client;
141+
char *uristr = loadbalanced_uri ();
142+
bson_error_t error;
143+
bool ok;
144+
mongoc_server_description_t *monitor_sd;
145+
146+
client = mongoc_client_new (uristr);
147+
ok = mongoc_client_command_simple (client,
148+
"admin",
149+
tmp_bson ("{'ping': 1}"),
150+
NULL /* read prefs */,
151+
NULL /* reply */,
152+
&error);
153+
ASSERT_OR_PRINT (ok, error);
154+
155+
/* Ensure the server description is unchanged and remains as type LoadBalancer. */
156+
monitor_sd = mongoc_client_select_server (
157+
client, true /* for writes */, NULL /* read prefs */, &error);
158+
ASSERT_OR_PRINT (monitor_sd, error);
159+
ASSERT_CMPSTR ("LoadBalancer", mongoc_server_description_type (monitor_sd));
160+
161+
mongoc_server_description_destroy (monitor_sd);
162+
bson_free (uristr);
163+
mongoc_client_destroy (client);
164+
}
165+
166+
static void
167+
test_loadbalanced_connect_pooled (void *unused)
168+
{
169+
mongoc_client_pool_t *pool;
170+
mongoc_client_t *client;
171+
char *uristr;
172+
mongoc_uri_t *uri;
173+
bson_error_t error;
174+
bool ok;
175+
mongoc_server_description_t *monitor_sd;
176+
177+
uristr = loadbalanced_uri ();
178+
uri = mongoc_uri_new (uristr);
179+
pool = mongoc_client_pool_new (uri);
180+
client = mongoc_client_pool_pop (pool);
181+
182+
ok = mongoc_client_command_simple (client,
183+
"admin",
184+
tmp_bson ("{'ping': 1}"),
185+
NULL /* read prefs */,
186+
NULL /* reply */,
187+
&error);
188+
ASSERT_OR_PRINT (ok, error);
189+
190+
/* Ensure the server description is unchanged and remains as type LoadBalancer. */
191+
monitor_sd = mongoc_client_select_server (
192+
client, true /* for writes */, NULL /* read prefs */, &error);
193+
ASSERT_OR_PRINT (monitor_sd, error);
194+
ASSERT_CMPSTR ("LoadBalancer", mongoc_server_description_type (monitor_sd));
195+
196+
mongoc_server_description_destroy (monitor_sd);
197+
bson_free (uristr);
198+
mongoc_uri_destroy (uri);
199+
mongoc_client_pool_push (pool, client);
200+
mongoc_client_pool_destroy (pool);
201+
}
202+
203+
/* Ensure that server selection on single threaded clients establishes a
204+
* connection against load balanced clusters. */
205+
static void
206+
test_loadbalanced_server_selection_establishes_connection_single (void *unused)
207+
{
208+
mongoc_client_t *client;
209+
char *uristr = loadbalanced_uri ();
210+
bson_error_t error;
211+
mongoc_server_description_t *monitor_sd;
212+
mongoc_server_description_t *handshake_sd;
213+
214+
client = mongoc_client_new (uristr);
215+
monitor_sd = mongoc_client_select_server (
216+
client, true /* for writes */, NULL /* read prefs */, &error);
217+
ASSERT_OR_PRINT (monitor_sd, error);
218+
ASSERT_CMPSTR ("LoadBalancer", mongoc_server_description_type (monitor_sd));
219+
220+
/* Ensure that a connection has been established by getting the handshake's
221+
* server description. */
222+
handshake_sd = mongoc_client_get_handshake_description (
223+
client, monitor_sd->id, NULL /* opts */, &error);
224+
ASSERT_OR_PRINT (handshake_sd, error);
225+
ASSERT_CMPSTR ("Mongos", mongoc_server_description_type (handshake_sd));
226+
227+
bson_free (uristr);
228+
mongoc_server_description_destroy (monitor_sd);
229+
mongoc_server_description_destroy (handshake_sd);
230+
mongoc_client_destroy (client);
231+
}
232+
233+
/* Test that the 5 second cooldown does not apply when establishing a new connection to the load balancer after a network error. */
234+
static void
235+
test_loadbalanced_network_error_bypasses_cooldown_single (void *unused) {
236+
237+
}
238+
136239
static int
137240
skip_if_not_loadbalanced (void)
138241
{
@@ -165,4 +268,26 @@ test_loadbalanced_install (TestSuite *suite)
165268
NULL /* ctx */,
166269
NULL /* dtor */,
167270
NULL);
271+
272+
TestSuite_AddFull (suite,
273+
"/loadbalanced/connect/single",
274+
test_loadbalanced_connect_single,
275+
NULL /* ctx */,
276+
NULL /* dtor */,
277+
skip_if_not_loadbalanced);
278+
279+
TestSuite_AddFull (suite,
280+
"/loadbalanced/connect/pooled",
281+
test_loadbalanced_connect_pooled,
282+
NULL /* ctx */,
283+
NULL /* dtor */,
284+
skip_if_not_loadbalanced);
285+
286+
TestSuite_AddFull (
287+
suite,
288+
"/loadbalanced/server_selection_establishes_connection/single",
289+
test_loadbalanced_server_selection_establishes_connection_single,
290+
NULL /* ctx */,
291+
NULL /* dtor */,
292+
skip_if_not_loadbalanced);
168293
}

0 commit comments

Comments
 (0)