Skip to content

Commit 6873346

Browse files
committed
CDRIVER-894: Race condition in connecting to node
1 parent af2eece commit 6873346

File tree

3 files changed

+111
-2
lines changed

3 files changed

+111
-2
lines changed

src/mongoc/mongoc-cluster.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,8 +1236,8 @@ _mongoc_cluster_add_node (mongoc_cluster_t *cluster,
12361236
bson_error_t *error /* OUT */)
12371237
{
12381238
mongoc_cluster_node_t *cluster_node;
1239-
12401239
mongoc_stream_t *stream;
1240+
int64_t expire_at;
12411241

12421242
ENTRY;
12431243

@@ -1252,6 +1252,18 @@ _mongoc_cluster_add_node (mongoc_cluster_t *cluster,
12521252
RETURN (NULL);
12531253
}
12541254

1255+
expire_at = bson_get_monotonic_time() + cluster->client->topology->connect_timeout_msec * 1000;
1256+
if (!mongoc_stream_wait (stream, expire_at)) {
1257+
bson_set_error (error,
1258+
MONGOC_ERROR_STREAM,
1259+
MONGOC_ERROR_STREAM_CONNECT,
1260+
"Failed to connect to target host: '%s'",
1261+
sd->host.host_and_port);
1262+
memcpy (&sd->error, error, sizeof sd->error);
1263+
mongoc_stream_failed (stream);
1264+
return NULL;
1265+
}
1266+
12551267
/* take critical fields from a fresh ismaster */
12561268
cluster_node = _mongoc_cluster_node_new (stream);
12571269
if (!_mongoc_cluster_run_ismaster (cluster, cluster_node)) {
@@ -1396,6 +1408,7 @@ mongoc_cluster_fetch_stream_single (mongoc_cluster_t *cluster,
13961408
{
13971409
mongoc_stream_t *stream = NULL;
13981410
mongoc_topology_scanner_node_t *scanner_node;
1411+
int64_t expire_at;
13991412
bson_t reply;
14001413

14011414
scanner_node = mongoc_topology_scanner_get_node (topology->scanner, sd->id);
@@ -1411,8 +1424,20 @@ mongoc_cluster_fetch_stream_single (mongoc_cluster_t *cluster,
14111424
if (!mongoc_topology_scanner_node_setup (scanner_node, error)) {
14121425
return NULL;
14131426
}
1414-
14151427
stream = scanner_node->stream;
1428+
1429+
expire_at = bson_get_monotonic_time() + topology->connect_timeout_msec * 1000;
1430+
if (!mongoc_stream_wait (stream, expire_at)) {
1431+
bson_set_error (error,
1432+
MONGOC_ERROR_STREAM,
1433+
MONGOC_ERROR_STREAM_CONNECT,
1434+
"Failed to connect to target host: '%s'",
1435+
sd->host.host_and_port);
1436+
memcpy (&sd->error, error, sizeof sd->error);
1437+
mongoc_topology_scanner_node_disconnect (scanner_node, true);
1438+
return NULL;
1439+
}
1440+
14161441
if (!_mongoc_stream_run_ismaster (cluster, stream, &reply, error)) {
14171442
mongoc_topology_scanner_node_disconnect (scanner_node, true);
14181443
return NULL;

src/mongoc/mongoc-stream-private.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ BSON_BEGIN_DECLS
3434
#define MONGOC_STREAM_GRIDFS 4
3535
#define MONGOC_STREAM_TLS 5
3636

37+
bool
38+
mongoc_stream_wait (mongoc_stream_t *stream,
39+
int64_t expire_at);
40+
3741
bool
3842
_mongoc_stream_writev_full (mongoc_stream_t *stream,
3943
mongoc_iovec_t *iov,

src/mongoc/mongoc-stream.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,86 @@ mongoc_stream_poll (mongoc_stream_poll_t *streams,
357357
}
358358

359359

360+
/*
361+
*--------------------------------------------------------------------------
362+
*
363+
* mongoc_stream_wait --
364+
*
365+
* Internal helper, poll a single stream until it connects.
366+
*
367+
* For now, only the POLLOUT (connected) event is supported.
368+
*
369+
* @expire_at should be an absolute time at which to expire using
370+
* the monotonic clock (bson_get_monotonic_time(), which is in
371+
* microseconds). expire_at of 0 or -1 is prohibited.
372+
*
373+
* Returns:
374+
* true if an event matched. otherwise false.
375+
* a timeout will return false.
376+
*
377+
* Side effects:
378+
* None.
379+
*
380+
*--------------------------------------------------------------------------
381+
*/
382+
383+
bool
384+
mongoc_stream_wait (mongoc_stream_t *stream,
385+
int64_t expire_at)
386+
{
387+
mongoc_stream_poll_t poller;
388+
int64_t now;
389+
int32_t timeout_msec;
390+
ssize_t ret;
391+
392+
ENTRY;
393+
394+
BSON_ASSERT (stream);
395+
BSON_ASSERT (expire_at > 0);
396+
397+
poller.stream = stream;
398+
poller.events = POLLOUT;
399+
poller.revents = 0;
400+
401+
now = bson_get_monotonic_time();
402+
403+
for (;;) {
404+
/* TODO CDRIVER-804 use int64_t for timeouts consistently */
405+
timeout_msec = (int32_t) BSON_MIN ((expire_at - now) / 1000L, INT32_MAX);
406+
if (timeout_msec < 0) {
407+
timeout_msec = 0;
408+
}
409+
410+
ret = mongoc_stream_poll (&poller, 1, timeout_msec);
411+
412+
if (ret > 0) {
413+
/* an event happened, return true if POLLOUT else false */
414+
RETURN (0 != (poller.revents & POLLOUT));
415+
} else if (ret < 0) {
416+
/* poll itself failed */
417+
418+
TRACE("errno is: %d", errno);
419+
if (MONGOC_ERRNO_IS_AGAIN(errno)) {
420+
now = bson_get_monotonic_time();
421+
422+
if (expire_at < now) {
423+
RETURN (false);
424+
} else {
425+
continue;
426+
}
427+
} else {
428+
/* poll failed for some non-transient reason */
429+
RETURN (false);
430+
}
431+
} else {
432+
/* poll timed out */
433+
RETURN (false);
434+
}
435+
}
436+
437+
return true;
438+
}
439+
360440
bool
361441
mongoc_stream_check_closed (mongoc_stream_t *stream)
362442
{

0 commit comments

Comments
 (0)