Skip to content

Commit 678c7b0

Browse files
committed
CDRIVER-3270 update err msg for retry writes
If a retryable write fails with error code 20 and message starting with "Transaction numbers", rewrite it to an actionable error message.
1 parent e4b18c2 commit 678c7b0

File tree

5 files changed

+113
-0
lines changed

5 files changed

+113
-0
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,6 +1602,10 @@ _mongoc_client_retryable_write_command_with_stream (
16021602
ret = mongoc_cluster_run_command_monitored (
16031603
&client->cluster, &parts->assembled, reply, error);
16041604

1605+
if (is_retryable) {
1606+
_mongoc_write_error_update_if_unsupported_storage_engine (ret, error, reply);
1607+
}
1608+
16051609
/* If a retryable error is encountered and the write is retryable, select
16061610
* a new writable stream and retry. If server selection fails or the selected
16071611
* server does not support retryable writes, fall through and allow the

src/libmongoc/src/mongoc/mongoc-collection.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3160,6 +3160,11 @@ mongoc_collection_find_and_modify_with_opts (
31603160
ret = mongoc_cluster_run_command_monitored (
31613161
cluster, &parts.assembled, reply_ptr, error);
31623162

3163+
if (is_retryable) {
3164+
_mongoc_write_error_update_if_unsupported_storage_engine (
3165+
ret, error, reply_ptr);
3166+
}
3167+
31633168
/* If a retryable error is encountered and the write is retryable, select
31643169
* a new writable stream and retry. If server selection fails or the selected
31653170
* server does not support retryable writes, fall through and allow the

src/libmongoc/src/mongoc/mongoc-write-command-private.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ _mongoc_write_error_get_type (bool cmd_ret,
216216
const bson_error_t *cmd_err,
217217
const bson_t *reply);
218218

219+
bool
220+
_mongoc_write_error_update_if_unsupported_storage_engine (bool cmd_ret,
221+
bson_error_t *cmd_err,
222+
bson_t *reply);
223+
219224
BSON_END_DECLS
220225

221226

src/libmongoc/src/mongoc/mongoc-write-command.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,10 @@ _mongoc_write_opmsg (mongoc_write_command_t *command,
594594
* the selected server does not support retryable writes, fall through
595595
* and allow the original error to be reported. */
596596
error_type = _mongoc_write_error_get_type (ret, error, &reply);
597+
if (is_retryable) {
598+
_mongoc_write_error_update_if_unsupported_storage_engine (
599+
ret, error, &reply);
600+
}
597601
if (is_retryable && error_type == MONGOC_WRITE_ERR_RETRY) {
598602
bson_error_t ignored_error;
599603

@@ -1492,3 +1496,41 @@ _mongoc_write_error_get_type (bool cmd_ret,
14921496
return MONGOC_WRITE_ERR_OTHER;
14931497
}
14941498
}
1499+
1500+
/* Returns true and modifies reply and cmd_err. */
1501+
bool
1502+
_mongoc_write_error_update_if_unsupported_storage_engine (bool cmd_ret,
1503+
bson_error_t *cmd_err,
1504+
bson_t *reply)
1505+
{
1506+
bson_error_t server_error;
1507+
1508+
if (cmd_ret) {
1509+
return false;
1510+
}
1511+
1512+
if (_mongoc_cmd_check_ok_no_wce (
1513+
reply, MONGOC_ERROR_API_VERSION_2, &server_error)) {
1514+
return false;
1515+
}
1516+
1517+
if (server_error.code == 20 &&
1518+
strstr (server_error.message, "Transaction numbers") ==
1519+
server_error.message) {
1520+
const char *replacement = "This MongoDB deployment does not support "
1521+
"retryable writes. Please add "
1522+
"retryWrites=false to your connection string.";
1523+
1524+
strcpy (cmd_err->message, replacement);
1525+
1526+
if (reply) {
1527+
bson_t *new_reply = bson_new ();
1528+
bson_copy_to_excluding_noinit (reply, new_reply, "errmsg", NULL);
1529+
BSON_APPEND_UTF8 (new_reply, "errmsg", replacement);
1530+
bson_destroy (reply);
1531+
bson_steal (reply, new_reply);
1532+
}
1533+
return true;
1534+
}
1535+
return false;
1536+
}

src/libmongoc/tests/test-mongoc-retryable-writes.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,59 @@ test_retry_no_crypto (void *ctx)
496496
mongoc_uri_destroy (uri);
497497
}
498498

499+
static void
500+
test_unsupported_storage_engine_error (void)
501+
{
502+
mock_rs_t *rs;
503+
mongoc_client_t *client;
504+
mongoc_collection_t *coll;
505+
bson_t reply;
506+
bson_error_t error;
507+
future_t *future;
508+
request_t *request;
509+
mongoc_client_session_t *session;
510+
bson_t opts;
511+
const char *expected_msg = "This MongoDB deployment does not support "
512+
"retryable writes. Please add retryWrites=false "
513+
"to your connection string.";
514+
515+
rs = mock_rs_with_autoismaster (WIRE_VERSION_RETRY_WRITES, true, 0, 0);
516+
mock_rs_run (rs);
517+
client = mongoc_client_new_from_uri (mock_rs_get_uri (rs));
518+
session = mongoc_client_start_session (client, NULL, &error);
519+
ASSERT_OR_PRINT (session, error);
520+
mongoc_client_set_error_api (client, MONGOC_ERROR_API_VERSION_2);
521+
coll = mongoc_client_get_collection (client, "test", "test");
522+
bson_init (&opts);
523+
ASSERT_OR_PRINT (mongoc_client_session_append (session, &opts, &error),
524+
error);
525+
/* findandmodify is retryable through mongoc_client_write_command_with_opts.
526+
*/
527+
future = future_client_write_command_with_opts (
528+
client,
529+
"test",
530+
tmp_bson ("{'findandmodify': 'coll' }"),
531+
&opts,
532+
&reply,
533+
&error);
534+
request = mock_rs_receives_request (rs);
535+
mock_server_replies_simple (
536+
request,
537+
"{'ok': 0, 'code': 20, 'errmsg': 'Transaction numbers are great'}");
538+
request_destroy (request);
539+
540+
BSON_ASSERT (!future_get_bool (future));
541+
ASSERT_ERROR_CONTAINS (error, MONGOC_ERROR_SERVER, 20, expected_msg);
542+
ASSERT_MATCH (&reply, "{'code': 20, 'errmsg': '%s'}", expected_msg);
499543

544+
bson_destroy (&opts);
545+
mongoc_client_session_destroy (session);
546+
bson_destroy (&reply);
547+
future_destroy (future);
548+
mongoc_collection_destroy (coll);
549+
mongoc_client_destroy (client);
550+
mock_rs_destroy (rs);
551+
}
500552
/*
501553
*-----------------------------------------------------------------------
502554
*
@@ -558,4 +610,9 @@ test_retryable_writes_install (TestSuite *suite)
558610
NULL,
559611
NULL,
560612
test_framework_skip_if_crypto);
613+
TestSuite_AddMockServerTest (
614+
suite,
615+
"/retryable_writes/unsupported_storage_engine_error",
616+
test_unsupported_storage_engine_error,
617+
test_framework_skip_if_no_crypto);
561618
}

0 commit comments

Comments
 (0)