Skip to content

Commit 935b379

Browse files
committed
CDRIVER-5589 add option to prefer TCP for SRV lookup (#1625)
Check for the environment variable `MONGOC_EXPERIMENTAL_SRV_PREFER_TCP`. Option is documented as experimental and subject to change. This option is not portable among drivers. This option does not apply to the implementation using `res_search`
1 parent 0aefe09 commit 935b379

File tree

6 files changed

+62
-13
lines changed

6 files changed

+62
-13
lines changed

src/libmongoc/doc/mongoc_uri_t.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ The driver prefixes the service name with "_mongodb._tcp.", then performs a DNS
7979

8080
On Unix, the MongoDB C Driver relies on libresolv to look up SRV and TXT records. If libresolv is unavailable, then using a "mongodb+srv" URI will cause an error. If your libresolv lacks ``res_nsearch`` then the driver will fall back to ``res_search``, which is not thread-safe.
8181

82+
Set the environment variable ``MONGOC_EXPERIMENTAL_SRV_PREFER_TCP`` to prefer TCP for the initial queries. The environment variable is ignored for ``res_search``. Large DNS responses over UDP may be truncated due to UDP size limitations. DNS resolvers are expected to retry over TCP if the UDP response indicates truncation. Some observed DNS environments do not set the truncation flag (TC), preventing the TCP retry. This environment variable is currently experimental and subject to change.
83+
8284
IPv4 and IPv6
8385
-------------
8486

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ _mongoc_client_get_rr (const char *hostname,
146146
mongoc_rr_type_t rr_type,
147147
mongoc_rr_data_t *rr_data,
148148
size_t initial_buffer_size,
149+
bool prefer_tcp,
149150
bson_error_t *error);
150151

151152
mongoc_client_t *

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ txt_callback (const char *hostname, PDNS_RECORD pdns, mongoc_rr_data_t *rr_data,
170170
*/
171171

172172
static bool
173-
_mongoc_get_rr_dnsapi (const char *hostname, mongoc_rr_type_t rr_type, mongoc_rr_data_t *rr_data, bson_error_t *error)
173+
_mongoc_get_rr_dnsapi (
174+
const char *hostname, mongoc_rr_type_t rr_type, mongoc_rr_data_t *rr_data, bool prefer_tcp, bson_error_t *error)
174175
{
175176
const char *rr_type_name;
176177
WORD nst;
@@ -198,7 +199,11 @@ _mongoc_get_rr_dnsapi (const char *hostname, mongoc_rr_type_t rr_type, mongoc_rr
198199
callback = txt_callback;
199200
}
200201

201-
res = DnsQuery_UTF8 (hostname, nst, DNS_QUERY_BYPASS_CACHE, NULL /* IP Address */, &pdns, 0 /* reserved */);
202+
DWORD options = DNS_QUERY_BYPASS_CACHE;
203+
if (prefer_tcp) {
204+
options |= DNS_QUERY_USE_TCP_ONLY;
205+
}
206+
res = DnsQuery_UTF8 (hostname, nst, options, NULL /* IP Address */, &pdns, 0 /* reserved */);
202207

203208
if (res) {
204209
DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
@@ -386,6 +391,7 @@ _mongoc_get_rr_search (const char *hostname,
386391
mongoc_rr_type_t rr_type,
387392
mongoc_rr_data_t *rr_data,
388393
size_t initial_buffer_size,
394+
bool prefer_tcp,
389395
bson_error_t *error)
390396
{
391397
#ifdef MONGOC_HAVE_RES_NSEARCH
@@ -438,6 +444,9 @@ _mongoc_get_rr_search (const char *hostname,
438444
#ifdef MONGOC_HAVE_RES_NSEARCH
439445
/* thread-safe */
440446
res_ninit (&state);
447+
if (prefer_tcp) {
448+
state.options |= RES_USEVC;
449+
}
441450
size = res_nsearch (&state, hostname, ns_c_in, nst, search_buf, buffer_size);
442451
#elif defined(MONGOC_HAVE_RES_SEARCH)
443452
size = res_search (hostname, ns_c_in, nst, search_buf, buffer_size);
@@ -551,6 +560,7 @@ _mongoc_client_get_rr (const char *hostname,
551560
mongoc_rr_type_t rr_type,
552561
mongoc_rr_data_t *rr_data,
553562
size_t initial_buffer_size,
563+
bool prefer_tcp,
554564
bson_error_t *error)
555565
{
556566
BSON_ASSERT (rr_data);
@@ -563,9 +573,9 @@ _mongoc_client_get_rr (const char *hostname,
563573
"libresolv unavailable, cannot use mongodb+srv URI");
564574
return false;
565575
#elif defined(MONGOC_HAVE_DNSAPI)
566-
return _mongoc_get_rr_dnsapi (hostname, rr_type, rr_data, error);
576+
return _mongoc_get_rr_dnsapi (hostname, rr_type, rr_data, prefer_tcp, error);
567577
#elif (defined(MONGOC_HAVE_RES_NSEARCH) || defined(MONGOC_HAVE_RES_SEARCH))
568-
return _mongoc_get_rr_search (hostname, rr_type, rr_data, initial_buffer_size, error);
578+
return _mongoc_get_rr_search (hostname, rr_type, rr_data, initial_buffer_size, prefer_tcp, error);
569579
#else
570580
#error No SRV library is available, but ENABLE_SRV is true!
571581
#endif

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ typedef bool (*_mongoc_rr_resolver_fn) (const char *hostname,
8585
mongoc_rr_type_t rr_type,
8686
mongoc_rr_data_t *rr_data,
8787
size_t initial_buffer_size,
88+
bool prefer_tcp,
8889
bson_error_t *error);
8990

9091
/**
@@ -217,6 +218,11 @@ typedef struct _mongoc_topology_t {
217218
// `mongoc_client_set_usleep_impl`.
218219
mongoc_usleep_func_t usleep_fn;
219220
void *usleep_data;
221+
222+
// `srv_prefer_tcp` determines if DNS lookup for SRV tries TCP first instead of UDP.
223+
// DNS implementations are expected to try UDP first, then retry with TCP if the UDP response indicates truncation.
224+
// Some DNS servers truncate UDP responses without setting the truncated (TC) flag. This may result in no TCP retry.
225+
bool srv_prefer_tcp;
220226
} mongoc_topology_t;
221227

222228
mongoc_topology_t *

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

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,14 @@ mongoc_topology_new (const mongoc_uri_t *uri, bool single_threaded)
383383
#endif
384384

385385
topology = (mongoc_topology_t *) bson_malloc0 (sizeof *topology);
386+
// Check if requested to use TCP for SRV lookup.
387+
{
388+
char *srv_prefer_tcp = _mongoc_getenv ("MONGOC_EXPERIMENTAL_SRV_PREFER_TCP");
389+
if (srv_prefer_tcp) {
390+
topology->srv_prefer_tcp = true;
391+
}
392+
bson_free (srv_prefer_tcp);
393+
}
386394
topology->usleep_fn = mongoc_usleep_default_impl;
387395
topology->session_pool = mongoc_server_session_pool_new_with_params (
388396
_server_session_init, _server_session_destroy, _server_session_should_prune, topology);
@@ -476,16 +484,24 @@ mongoc_topology_new (const mongoc_uri_t *uri, bool single_threaded)
476484

477485
/* a mongodb+srv URI. try SRV lookup, if no error then also try TXT */
478486
prefixed_hostname = bson_strdup_printf ("_%s._tcp.%s", mongoc_uri_get_srv_service_name (uri), srv_hostname);
479-
if (!topology->rr_resolver (
480-
prefixed_hostname, MONGOC_RR_SRV, &rr_data, MONGOC_RR_DEFAULT_BUFFER_SIZE, &topology->scanner->error)) {
487+
if (!topology->rr_resolver (prefixed_hostname,
488+
MONGOC_RR_SRV,
489+
&rr_data,
490+
MONGOC_RR_DEFAULT_BUFFER_SIZE,
491+
topology->srv_prefer_tcp,
492+
&topology->scanner->error)) {
481493
GOTO (srv_fail);
482494
}
483495

484496
/* Failure to find TXT records will not return an error (since it is only
485497
* for options). But _mongoc_client_get_rr may return an error if
486498
* there is more than one TXT record returned. */
487-
if (!topology->rr_resolver (
488-
srv_hostname, MONGOC_RR_TXT, &rr_data, MONGOC_RR_DEFAULT_BUFFER_SIZE, &topology->scanner->error)) {
499+
if (!topology->rr_resolver (srv_hostname,
500+
MONGOC_RR_TXT,
501+
&rr_data,
502+
MONGOC_RR_DEFAULT_BUFFER_SIZE,
503+
topology->srv_prefer_tcp,
504+
&topology->scanner->error)) {
489505
GOTO (srv_fail);
490506
}
491507

@@ -812,8 +828,12 @@ mongoc_topology_rescan_srv (mongoc_topology_t *topology)
812828
prefixed_hostname =
813829
bson_strdup_printf ("_%s._tcp.%s", mongoc_uri_get_srv_service_name (topology->uri), srv_hostname);
814830

815-
ret = topology->rr_resolver (
816-
prefixed_hostname, MONGOC_RR_SRV, &rr_data, MONGOC_RR_DEFAULT_BUFFER_SIZE, &topology->scanner->error);
831+
ret = topology->rr_resolver (prefixed_hostname,
832+
MONGOC_RR_SRV,
833+
&rr_data,
834+
MONGOC_RR_DEFAULT_BUFFER_SIZE,
835+
topology->srv_prefer_tcp,
836+
&topology->scanner->error);
817837

818838
td = mc_tpld_take_ref (topology);
819839
topology->srv_polling_last_scan_ms = bson_get_monotonic_time () / 1000;

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -599,9 +599,9 @@ test_small_initial_buffer (void *unused)
599599
BSON_UNUSED (unused);
600600

601601
memset (&rr_data, 0, sizeof (rr_data));
602-
ASSERT_OR_PRINT (
603-
_mongoc_client_get_rr ("_mongodb._tcp.test1.test.build.10gen.cc", rr_type, &rr_data, small_buffer_size, &error),
604-
error);
602+
ASSERT_OR_PRINT (_mongoc_client_get_rr (
603+
"_mongodb._tcp.test1.test.build.10gen.cc", rr_type, &rr_data, small_buffer_size, false, &error),
604+
error);
605605
ASSERT_CMPINT (rr_data.count, ==, 2);
606606
bson_free (rr_data.txt_record_opts);
607607
_mongoc_host_list_destroy_all (rr_data.hosts);
@@ -612,12 +612,14 @@ _mock_rr_resolver_prose_test_9 (const char *service,
612612
mongoc_rr_type_t rr_type,
613613
mongoc_rr_data_t *rr_data,
614614
size_t initial_buffer_size,
615+
bool prefer_tcp,
615616
bson_error_t *error)
616617
{
617618
BSON_UNUSED (service);
618619
BSON_UNUSED (rr_type);
619620
BSON_UNUSED (rr_data);
620621
BSON_UNUSED (initial_buffer_size);
622+
BSON_UNUSED (prefer_tcp);
621623
BSON_UNUSED (error);
622624

623625
test_error ("Expected mock resolver to not be called");
@@ -853,12 +855,14 @@ _mock_rr_resolver_prose_test_10 (const char *service,
853855
mongoc_rr_type_t rr_type,
854856
mongoc_rr_data_t *rr_data,
855857
size_t initial_buffer_size,
858+
bool prefer_tcp,
856859
bson_error_t *error)
857860
{
858861
BSON_UNUSED (initial_buffer_size);
859862

860863
BSON_ASSERT_PARAM (service);
861864
BSON_ASSERT_PARAM (rr_data);
865+
BSON_UNUSED (prefer_tcp);
862866
BSON_ASSERT_PARAM (error);
863867

864868
if (rr_type == MONGOC_RR_SRV) {
@@ -953,12 +957,14 @@ _mock_rr_resolver_prose_test_11 (const char *service,
953957
mongoc_rr_type_t rr_type,
954958
mongoc_rr_data_t *rr_data,
955959
size_t initial_buffer_size,
960+
bool prefer_tcp,
956961
bson_error_t *error)
957962
{
958963
BSON_UNUSED (initial_buffer_size);
959964

960965
BSON_ASSERT_PARAM (service);
961966
BSON_ASSERT_PARAM (rr_data);
967+
BSON_UNUSED (prefer_tcp);
962968
BSON_ASSERT_PARAM (error);
963969

964970
if (rr_type == MONGOC_RR_SRV) {
@@ -1050,12 +1056,14 @@ _mock_rr_resolver_prose_test_12 (const char *service,
10501056
mongoc_rr_type_t rr_type,
10511057
mongoc_rr_data_t *rr_data,
10521058
size_t initial_buffer_size,
1059+
bool prefer_tcp,
10531060
bson_error_t *error)
10541061
{
10551062
BSON_UNUSED (initial_buffer_size);
10561063

10571064
BSON_ASSERT_PARAM (service);
10581065
BSON_ASSERT_PARAM (rr_data);
1066+
BSON_UNUSED (prefer_tcp);
10591067
BSON_ASSERT_PARAM (error);
10601068

10611069
if (rr_type == MONGOC_RR_SRV) {
@@ -1198,12 +1206,14 @@ _mock_rr_resolver_with_override (const char *service,
11981206
mongoc_rr_type_t rr_type,
11991207
mongoc_rr_data_t *rr_data,
12001208
size_t initial_buffer_size,
1209+
bool prefer_tcp,
12011210
bson_error_t *error)
12021211
{
12031212
BSON_UNUSED (initial_buffer_size);
12041213

12051214
BSON_ASSERT_PARAM (service);
12061215
BSON_ASSERT_PARAM (rr_data);
1216+
BSON_UNUSED (prefer_tcp);
12071217
BSON_ASSERT_PARAM (error);
12081218

12091219
if (rr_type == MONGOC_RR_SRV) {

0 commit comments

Comments
 (0)