Skip to content

Commit f3c2dac

Browse files
committed
Add retry sleep, refactor retry control flow
1 parent 4c357bd commit f3c2dac

File tree

1 file changed

+91
-71
lines changed

1 file changed

+91
-71
lines changed

src/libmongoc/src/mongoc/mongoc-crypt.c

Lines changed: 91 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,27 @@ _get_stream (const char *endpoint, int32_t connecttimeoutms, const mongoc_ssl_op
527527
return tls_stream;
528528
}
529529

530+
static int64_t
531+
backoff_time_usec (int64_t attempts)
532+
{
533+
static bool seeded = false;
534+
if (!seeded) {
535+
srand ((uint32_t) time (NULL));
536+
seeded = true;
537+
}
538+
539+
/* Exponential backoff with jitter. */
540+
const int64_t base = 200000; /* 0.2 seconds */
541+
const int64_t max = 20000000; /* 20 seconds */
542+
int64_t backoff = base * (1LL << (attempts - 1));
543+
if (backoff > max) {
544+
backoff = max;
545+
}
546+
547+
/* Full jitter: between 1 and current max */
548+
return (int64_t) ((double) rand () / (double) RAND_MAX * (double) backoff) + 1;
549+
}
550+
530551
static bool
531552
_state_need_kms (_state_machine_t *state_machine, bson_error_t *error)
532553
{
@@ -575,94 +596,93 @@ _state_need_kms (_state_machine_t *state_machine, bson_error_t *error)
575596
}
576597

577598
retry:
578-
while (true) {
579-
mongoc_stream_destroy (tls_stream);
580-
tls_stream = _get_stream (endpoint, sockettimeout, ssl_opt, error);
599+
if (retry_count > 0) {
600+
int64_t sleep_usec = backoff_time_usec (retry_count);
601+
_mongoc_usleep (sleep_usec);
602+
}
603+
604+
mongoc_stream_destroy (tls_stream);
605+
tls_stream = _get_stream (endpoint, sockettimeout, ssl_opt, error);
581606
#ifdef MONGOC_ENABLE_SSL_SECURE_CHANNEL
582-
/* Retry once with schannel as a workaround for CDRIVER-3566. */
583-
if (!tls_stream) {
584-
tls_stream = _get_stream (endpoint, sockettimeout, ssl_opt, error);
585-
}
607+
/* Retry once with schannel as a workaround for CDRIVER-3566. */
608+
if (!tls_stream) {
609+
tls_stream = _get_stream (endpoint, sockettimeout, ssl_opt, error);
610+
}
586611
#endif
587-
if (!tls_stream) {
588-
retry_count++;
589-
if (retry_count < max_tcp_retries) {
590-
/* Always safe to retry stream creation. */
591-
continue;
592-
} else {
593-
/* TLS errors are set in _get_stream */
594-
goto fail;
595-
}
612+
if (!tls_stream) {
613+
retry_count++;
614+
if (retry_count < max_tcp_retries) {
615+
/* Always safe to retry stream creation. */
616+
goto retry;
617+
} else {
618+
/* TLS errors are set in _get_stream */
619+
goto fail;
596620
}
621+
}
597622

598-
iov.iov_base = (char *) mongocrypt_binary_data (http_req);
599-
iov.iov_len = mongocrypt_binary_len (http_req);
623+
iov.iov_base = (char *) mongocrypt_binary_data (http_req);
624+
iov.iov_len = mongocrypt_binary_len (http_req);
600625

601-
if (!_mongoc_stream_writev_full (tls_stream, &iov, 1, sockettimeout, error)) {
602-
retry_count++;
603-
if (retry_count < max_tcp_retries) {
604-
/* Always safe to retry unsuccessful writes. */
605-
continue;
606-
} else {
607-
bson_set_error (error,
608-
MONGOC_ERROR_STREAM,
609-
MONGOC_ERROR_STREAM_SOCKET,
610-
"Failed to write to KMS stream: %s",
611-
endpoint);
612-
goto fail;
613-
}
626+
if (!_mongoc_stream_writev_full (tls_stream, &iov, 1, sockettimeout, error)) {
627+
retry_count++;
628+
if (retry_count < max_tcp_retries && mongocrypt_kms_ctx_fail (kms_ctx)) {
629+
goto retry;
630+
} else {
631+
bson_set_error (
632+
error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_SOCKET, "Failed to write to KMS stream: %s", endpoint);
633+
goto fail;
614634
}
635+
}
615636

616-
/* Read and feed reply. */
617-
while (mongocrypt_kms_ctx_bytes_needed (kms_ctx) > 0) {
637+
/* Read and feed reply. */
638+
while (mongocrypt_kms_ctx_bytes_needed (kms_ctx) > 0) {
618639
#define BUFFER_SIZE 1024
619-
uint8_t buf[BUFFER_SIZE];
620-
uint32_t bytes_needed = mongocrypt_kms_ctx_bytes_needed (kms_ctx);
621-
ssize_t read_ret;
640+
uint8_t buf[BUFFER_SIZE];
641+
uint32_t bytes_needed = mongocrypt_kms_ctx_bytes_needed (kms_ctx);
642+
ssize_t read_ret;
622643

623-
int64_t sleep_usec = mongocrypt_kms_ctx_usleep (kms_ctx);
624-
if (sleep_usec > 0) {
625-
_mongoc_usleep (sleep_usec);
626-
}
644+
int64_t sleep_usec = mongocrypt_kms_ctx_usleep (kms_ctx);
645+
if (sleep_usec > 0) {
646+
_mongoc_usleep (sleep_usec);
647+
}
627648

628-
/* Cap the bytes requested at the buffer size. */
629-
if (bytes_needed > BUFFER_SIZE) {
630-
bytes_needed = BUFFER_SIZE;
631-
}
649+
/* Cap the bytes requested at the buffer size. */
650+
if (bytes_needed > BUFFER_SIZE) {
651+
bytes_needed = BUFFER_SIZE;
652+
}
632653

633-
read_ret = mongoc_stream_read (tls_stream, buf, bytes_needed, 1 /* min_bytes. */, sockettimeout);
634-
if (read_ret <= 0) {
635-
retry_count++;
636-
if (retry_count < max_tcp_retries && mongocrypt_kms_ctx_fail (kms_ctx)) {
637-
goto retry;
654+
read_ret = mongoc_stream_read (tls_stream, buf, bytes_needed, 1 /* min_bytes. */, sockettimeout);
655+
if (read_ret <= 0) {
656+
retry_count++;
657+
if (retry_count < max_tcp_retries && mongocrypt_kms_ctx_fail (kms_ctx)) {
658+
goto retry;
659+
} else {
660+
if (read_ret == -1) {
661+
bson_set_error (error,
662+
MONGOC_ERROR_STREAM,
663+
MONGOC_ERROR_STREAM_SOCKET,
664+
"failed to read from KMS stream: %d",
665+
errno);
666+
goto fail;
638667
} else {
639-
if (read_ret == -1) {
640-
bson_set_error (error,
641-
MONGOC_ERROR_STREAM,
642-
MONGOC_ERROR_STREAM_SOCKET,
643-
"failed to read from KMS stream: %d",
644-
errno);
645-
goto fail;
646-
} else {
647-
bson_set_error (
648-
error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_SOCKET, "unexpected EOF from KMS stream");
649-
goto fail;
650-
}
668+
bson_set_error (
669+
error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_SOCKET, "unexpected EOF from KMS stream");
670+
goto fail;
651671
}
652672
}
653-
mongocrypt_binary_destroy (http_reply);
673+
}
674+
mongocrypt_binary_destroy (http_reply);
654675

655-
BSON_ASSERT (bson_in_range_signed (uint32_t, read_ret));
656-
http_reply = mongocrypt_binary_new_from_data (buf, (uint32_t) read_ret);
657-
if (!mongocrypt_kms_ctx_feed (kms_ctx, http_reply)) {
658-
_kms_ctx_check_error (kms_ctx, error, true);
659-
goto fail;
660-
}
676+
BSON_ASSERT (bson_in_range_signed (uint32_t, read_ret));
677+
http_reply = mongocrypt_binary_new_from_data (buf, (uint32_t) read_ret);
678+
if (!mongocrypt_kms_ctx_feed (kms_ctx, http_reply)) {
679+
_kms_ctx_check_error (kms_ctx, error, true);
680+
goto fail;
661681
}
662-
kms_ctx = mongocrypt_ctx_next_kms_ctx (state_machine->ctx);
663-
retry_count = 0;
664-
break;
665682
}
683+
kms_ctx = mongocrypt_ctx_next_kms_ctx (state_machine->ctx);
684+
retry_count = 0;
685+
break;
666686
}
667687
/* When NULL is returned by mongocrypt_ctx_next_kms_ctx, this can either be
668688
* an error or end-of-list. */

0 commit comments

Comments
 (0)