Skip to content

Commit c2e7b74

Browse files
committed
Disable SDAM on LoadBalanced, add events
1 parent 48b9100 commit c2e7b74

9 files changed

+148
-23
lines changed

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

Lines changed: 3 additions & 1 deletion
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_LOADBALANCER,
5758
MONGOC_SERVER_DESCRIPTION_TYPES,
5859
} mongoc_server_description_type_t;
5960

@@ -107,7 +108,8 @@ struct _mongoc_server_description_t {
107108
1. a monitor receives a network error
108109
2. an app thread receives any network error before completing a handshake
109110
3. an app thread receives a non-timeout network error after the handshake
110-
4. an app thread receives a "not primary" or "node is recovering" error from a
111+
4. an app thread receives a "not primary" or "node is recovering" error from
112+
a
111113
pre-4.2 server.
112114
*/
113115
uint32_t generation;

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

Lines changed: 5 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_LOADBALANCER:
365+
return "LoadBalancer";
364366
case MONGOC_SERVER_DESCRIPTION_TYPES:
365367
default:
366368
MONGOC_ERROR ("Invalid mongoc_server_description_t type");
@@ -803,6 +805,9 @@ mongoc_server_description_new_copy (
803805
&description->error);
804806
} else {
805807
mongoc_server_description_reset (copy);
808+
/* preserve the original server description type, which is manually set
809+
* for a LoadBalancer server */
810+
copy->type = description->type;
806811
}
807812

808813
/* Preserve the error */

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ _mongoc_topology_background_monitoring_start (mongoc_topology_t *topology)
146146

147147
_mongoc_handshake_freeze ();
148148
_mongoc_topology_description_monitor_opening (&topology->description);
149+
if (topology->description.type == MONGOC_TOPOLOGY_LOADBALANCED) {
150+
/* Do not proceed to start monitoring threads. */
151+
return;
152+
}
149153

150154
/* Reconcile to create the first server monitors. */
151155
_mongoc_topology_background_monitoring_reconcile (topology);

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

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
/* ServerOpeningEvent */
2828
void
2929
_mongoc_topology_description_monitor_server_opening (
30-
const mongoc_topology_description_t *td,
31-
mongoc_server_description_t *sd)
30+
const mongoc_topology_description_t *td, mongoc_server_description_t *sd)
3231
{
3332
if (td->apm_callbacks.server_opening && !sd->opened) {
3433
mongoc_apm_server_opening_t event;
@@ -115,10 +114,33 @@ _mongoc_topology_description_monitor_opening (mongoc_topology_description_t *td)
115114
for (i = 0; i < td->servers->items_len; i++) {
116115
sd = (mongoc_server_description_t *) mongoc_set_get_item (td->servers,
117116
(int) i);
118-
119117
_mongoc_topology_description_monitor_server_opening (td, sd);
120118
}
121119

120+
/* If this is a load balanced topology:
121+
* - update the one server description to be LoadBalancer
122+
* - emit a server changed event Unknown => LoadBalancer
123+
* - emit a topology changed event
124+
*/
125+
if (td->type == MONGOC_TOPOLOGY_LOADBALANCED) {
126+
mongoc_server_description_t *prev_sd;
127+
128+
/* LoadBalanced deployments must have exactly one host listed. */
129+
BSON_ASSERT (td->servers->items_len == 1);
130+
sd = (mongoc_server_description_t *) mongoc_set_get_item (td->servers,
131+
(int) 0);
132+
prev_sd = mongoc_server_description_new_copy (sd);
133+
if (td->apm_callbacks.topology_changed) {
134+
_mongoc_topology_description_copy_to (td, prev_td);
135+
}
136+
sd->type = MONGOC_SERVER_LOADBALANCER;
137+
_mongoc_topology_description_monitor_server_changed (td, prev_sd, sd);
138+
mongoc_server_description_destroy (prev_sd);
139+
if (td->apm_callbacks.topology_changed) {
140+
_mongoc_topology_description_monitor_changed (prev_td, td);
141+
}
142+
}
143+
122144
if (prev_td) {
123145
mongoc_topology_description_destroy (prev_td);
124146
bson_free (prev_td);
@@ -152,6 +174,15 @@ _mongoc_topology_description_monitor_closed (
152174
if (td->apm_callbacks.topology_closed) {
153175
mongoc_apm_topology_closed_t event;
154176

177+
if (td->type == MONGOC_TOPOLOGY_LOADBALANCED) {
178+
mongoc_server_description_t *sd;
179+
180+
/* LoadBalanced deployments must have exactly one host listed. */
181+
BSON_ASSERT (td->servers->items_len == 1);
182+
sd = (mongoc_server_description_t *) mongoc_set_get_item (td->servers,
183+
(int) 0);
184+
_mongoc_topology_description_monitor_server_closed (td, sd);
185+
}
155186
bson_oid_copy (&td->topology_id, &event.topology_id);
156187
event.context = td->apm_context;
157188
td->apm_callbacks.topology_closed (&event);

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_LOADBALANCED,
3536
MONGOC_TOPOLOGY_DESCRIPTION_TYPES
3637
} mongoc_topology_description_type_t;
3738

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,6 +2139,8 @@ mongoc_topology_description_type (const mongoc_topology_description_t *td)
21392139
return "ReplicaSetWithPrimary";
21402140
case MONGOC_TOPOLOGY_SINGLE:
21412141
return "Single";
2142+
case MONGOC_TOPOLOGY_LOADBALANCED:
2143+
return "LoadBalanced";
21422144
case MONGOC_TOPOLOGY_DESCRIPTION_TYPES:
21432145
default:
21442146
fprintf (stderr, "ERROR: Unknown topology type %d\n", td->type);

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,10 @@ 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 (mongoc_uri_get_option_as_bool (
417+
topology->uri, MONGOC_URI_LOADBALANCED, false)) {
418+
init_type = MONGOC_TOPOLOGY_LOADBALANCED;
419+
} else if (service && !has_directconnection) {
417420
init_type = MONGOC_TOPOLOGY_UNKNOWN;
418421
} else if (has_directconnection) {
419422
if (directconnection) {
@@ -653,11 +656,6 @@ mongoc_topology_should_rescan_srv (mongoc_topology_t *topology)
653656
return false;
654657
}
655658

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-
}
661659

662660
if ((topology->description.type != MONGOC_TOPOLOGY_SHARDED) &&
663661
(topology->description.type != MONGOC_TOPOLOGY_UNKNOWN)) {
@@ -818,6 +816,10 @@ _mongoc_topology_do_blocking_scan (mongoc_topology_t *topology,
818816
{
819817
_mongoc_handshake_freeze ();
820818

819+
if (topology->description.type == MONGOC_TOPOLOGY_LOADBALANCED) {
820+
MONGOC_DEBUG ("bypassing server selection logic for single threaded");
821+
return;
822+
}
821823
bson_mutex_lock (&topology->mutex);
822824
mongoc_topology_scan_once (topology, true /* obey cooldown */);
823825
bson_mutex_unlock (&topology->mutex);

src/libmongoc/tests/json-test.c

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ topology_type_from_test (const char *type)
5252
return MONGOC_TOPOLOGY_SINGLE;
5353
} else if (strcmp (type, "Sharded") == 0) {
5454
return MONGOC_TOPOLOGY_SHARDED;
55+
} else if (strcmp (type, "LoadBalanced") == 0) {
56+
return MONGOC_TOPOLOGY_LOADBALANCED;
5557
}
5658

5759
fprintf (stderr, "can't parse this: %s", type);
@@ -80,6 +82,8 @@ server_type_from_test (const char *type)
8082
return MONGOC_SERVER_RS_GHOST;
8183
} else if (strcmp (type, "Unknown") == 0) {
8284
return MONGOC_SERVER_UNKNOWN;
85+
} else if (strcmp (type, "LoadBalancer") == 0) {
86+
return MONGOC_SERVER_LOADBALANCER;
8387
}
8488
fprintf (stderr, "ERROR: Unknown server type %s\n", type);
8589
BSON_ASSERT (0);
@@ -175,8 +179,7 @@ server_description_by_hostname (mongoc_topology_description_t *topology,
175179
*-----------------------------------------------------------------------
176180
*/
177181
void
178-
process_sdam_test_hello_responses (bson_t *phase,
179-
mongoc_topology_t *topology)
182+
process_sdam_test_hello_responses (bson_t *phase, mongoc_topology_t *topology)
180183
{
181184
mongoc_topology_description_t *td;
182185
mongoc_server_description_t *sd;
@@ -190,7 +193,7 @@ process_sdam_test_hello_responses (bson_t *phase,
190193
description = bson_iter_utf8 (&phase_field_iter, NULL);
191194
MONGOC_DEBUG ("phase: %s", description);
192195
}
193-
/* grab hello responses out and feed them to topology */
196+
/* grab hello responses (if present) and feed them to topology */
194197
if (bson_iter_init_find (&phase_field_iter, phase, "responses")) {
195198
bson_t hellos;
196199
bson_t hello;
@@ -317,9 +320,6 @@ process_sdam_test_hello_responses (bson_t *phase,
317320
generation);
318321
bson_mutex_unlock (&topology->mutex);
319322
}
320-
} else {
321-
test_error (
322-
"test does not have 'responses' or 'applicationErrors' array");
323323
}
324324
}
325325

@@ -1739,9 +1739,8 @@ run_json_general_test (const json_test_config_t *config)
17391739

17401740
/* expect "operation was interrupted", ignore "command not found" or "is
17411741
* not supported" */
1742-
if (!r &&
1743-
(error.domain != MONGOC_ERROR_SERVER ||
1744-
(error.code != 11601 && error.code != 59)) &&
1742+
if (!r && (error.domain != MONGOC_ERROR_SERVER ||
1743+
(error.code != 11601 && error.code != 59)) &&
17451744
(strstr (error.message, "is unsupported") == NULL)) {
17461745
MONGOC_WARNING ("Error in killAllSessions: %s", error.message);
17471746
}

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

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "mongoc/mongoc-client-private.h"
88
#include "mongoc/mongoc-topology-private.h"
9+
#include "mongoc/mongoc-topology-description-apm-private.h"
910
#include "test-libmongoc.h"
1011

1112
#ifdef BSON_HAVE_STRINGS_H
@@ -33,6 +34,8 @@ _topology_has_description (mongoc_topology_description_t *topology,
3334
if (set_name) {
3435
BSON_ASSERT (sd->set_name);
3536
ASSERT_CMPSTR (sd->set_name, set_name);
37+
} else if (sd->set_name) {
38+
test_error ("expected NULL setName, got: %s", sd->set_name);
3639
}
3740
} else if (strcmp ("type", bson_iter_key (&server_iter)) == 0) {
3841
server_type = bson_iter_utf8 (&server_iter, NULL);
@@ -86,6 +89,41 @@ _topology_has_description (mongoc_topology_description_t *topology,
8689
BSON_ITER_HOLDS_INT32 (&iter));
8790
expected_generation = bson_iter_int32 (&iter);
8891
ASSERT_CMPINT32 (expected_generation, ==, sd->generation);
92+
} else if (strcmp ("logicalSessionTimeoutMinutes",
93+
bson_iter_key (&server_iter)) == 0) {
94+
if (BSON_ITER_HOLDS_NULL (&server_iter)) {
95+
if (sd->session_timeout_minutes != MONGOC_NO_SESSIONS) {
96+
test_error ("ERROR: expected unset value for "
97+
"logicalSessionTimeoutMinutes but got: %" PRId64,
98+
sd->session_timeout_minutes);
99+
}
100+
} else {
101+
ASSERT_CMPINT64 (bson_iter_as_int64 (&server_iter),
102+
==,
103+
sd->session_timeout_minutes);
104+
}
105+
} else if (strcmp ("minWireVersion", bson_iter_key (&server_iter)) == 0) {
106+
if (BSON_ITER_HOLDS_NULL (&server_iter)) {
107+
if (sd->min_wire_version != 0) {
108+
test_error ("ERROR: expected unset value for minWireVersion but "
109+
"got: %" PRId32,
110+
sd->min_wire_version);
111+
}
112+
} else {
113+
ASSERT_CMPINT32 (
114+
bson_iter_int32 (&server_iter), ==, sd->min_wire_version);
115+
}
116+
} else if (strcmp ("maxWireVersion", bson_iter_key (&server_iter)) == 0) {
117+
if (BSON_ITER_HOLDS_NULL (&server_iter)) {
118+
if (sd->max_wire_version != 0) {
119+
test_error ("ERROR: expected unset value for maxWireVersion but "
120+
"got: %" PRId32,
121+
sd->max_wire_version);
122+
}
123+
} else {
124+
ASSERT_CMPINT32 (
125+
bson_iter_int32 (&server_iter), ==, sd->max_wire_version);
126+
}
89127
} else {
90128
fprintf (
91129
stderr, "ERROR: unparsed field %s\n", bson_iter_key (&server_iter));
@@ -129,6 +167,11 @@ test_sdam_cb (bson_t *test)
129167
bson_iter_bson (&iter, &phases);
130168
bson_iter_init (&phase_iter, &phases);
131169

170+
/* LoadBalanced topologies change the server from Unknown to LoadBalancer
171+
* when SDAM monitoring begins. Force an opening, which would occur on the
172+
* first operation on the client. */
173+
_mongoc_topology_description_monitor_opening (td);
174+
132175
while (bson_iter_next (&phase_iter)) {
133176
bson_iter_bson (&phase_iter, &phase);
134177

@@ -160,6 +203,10 @@ test_sdam_cb (bson_t *test)
160203
if (set_name) {
161204
BSON_ASSERT (td->set_name);
162205
ASSERT_CMPSTR (td->set_name, set_name);
206+
} else {
207+
if (td->set_name) {
208+
test_error ("expected NULL setName, got: %s", td->set_name);
209+
}
163210
}
164211
} else if (strcmp ("topologyType", bson_iter_key (&outcome_iter)) ==
165212
0) {
@@ -187,13 +234,27 @@ test_sdam_cb (bson_t *test)
187234
}
188235
} else if (strcmp ("maxSetVersion", bson_iter_key (&outcome_iter)) ==
189236
0) {
190-
ASSERT_CMPINT64 (
191-
bson_iter_as_int64 (&outcome_iter), ==, td->max_set_version);
237+
if (BSON_ITER_HOLDS_NULL (&outcome_iter)) {
238+
if (td->max_set_version != MONGOC_NO_SET_VERSION) {
239+
test_error ("ERROR: expected unset value for maxSetVersion "
240+
"but got: %" PRId64,
241+
td->max_set_version);
242+
}
243+
} else {
244+
ASSERT_CMPINT64 (
245+
bson_iter_as_int64 (&outcome_iter), ==, td->max_set_version);
246+
}
192247
} else if (strcmp ("maxElectionId", bson_iter_key (&outcome_iter)) ==
193248
0) {
194249
const bson_oid_t *expected_oid;
250+
bson_oid_t zeroed = {0};
251+
195252
expected_oid = bson_iter_oid (&outcome_iter);
196253

254+
if (expected_oid == NULL) {
255+
expected_oid = &zeroed;
256+
}
257+
197258
if (!bson_oid_equal (expected_oid, &td->max_election_id)) {
198259
char expected_oid_str[25];
199260
char actual_oid_str[25];
@@ -205,6 +266,19 @@ test_sdam_cb (bson_t *test)
205266
expected_oid_str,
206267
actual_oid_str);
207268
}
269+
} else if (strcmp ("logicalSessionTimeoutMinutes",
270+
bson_iter_key (&outcome_iter)) == 0) {
271+
if (BSON_ITER_HOLDS_NULL (&outcome_iter)) {
272+
if (td->session_timeout_minutes != MONGOC_NO_SESSIONS) {
273+
test_error ("ERROR: expected unset value for "
274+
"logicalSessionTimeoutMinutes but got: %" PRId64,
275+
td->session_timeout_minutes);
276+
}
277+
} else {
278+
ASSERT_CMPINT64 (bson_iter_as_int64 (&outcome_iter),
279+
==,
280+
td->session_timeout_minutes);
281+
}
208282
} else {
209283
fprintf (stderr,
210284
"ERROR: unparsed test field %s\n",
@@ -481,6 +555,10 @@ test_all_spec_tests (TestSuite *suite)
481555
TestSuite_CheckLive,
482556
test_framework_skip_if_no_crypto,
483557
test_framework_skip_if_slow);
558+
559+
ASSERT (realpath (JSON_DIR "/server_discovery_and_monitoring/load-balanced",
560+
resolved));
561+
install_json_test_suite (suite, resolved, &test_sdam_cb);
484562
}
485563

486564
static void
@@ -773,8 +851,9 @@ test_prose_rtt (void *unused)
773851
* RTT_TEST_TIMEOUT_SEC seconds, consider it a failure. */
774852
satisfied = false;
775853
start_us = bson_get_monotonic_time ();
776-
while (!satisfied && bson_get_monotonic_time () <
777-
start_us + RTT_TEST_TIMEOUT_SEC * 1000 * 1000) {
854+
while (!satisfied &&
855+
bson_get_monotonic_time () <
856+
start_us + RTT_TEST_TIMEOUT_SEC * 1000 * 1000) {
778857
mongoc_server_description_t *sd;
779858

780859
sd = mongoc_client_select_server (

0 commit comments

Comments
 (0)