Skip to content

Commit 72687f6

Browse files
committed
CDRIVER-2706 set error on invalidated server
1 parent c4af4aa commit 72687f6

File tree

7 files changed

+203
-112
lines changed

7 files changed

+203
-112
lines changed

src/libmongoc/src/mongoc/mongoc-change-stream.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,29 +41,29 @@
4141
static bool
4242
_is_resumable_error (const bson_t *reply)
4343
{
44-
const char *msg = "";
45-
uint32_t code;
44+
bson_error_t error;
4645

4746
/* Change Streams Spec resumable criteria: "any error encountered which is
4847
* not a server error (e.g. a timeout error or network error)" */
4948
if (bson_empty (reply)) {
5049
return true;
5150
}
5251

53-
if (!_mongoc_parse_error_reply (reply, false /* check_wce */, &code, &msg)) {
52+
if (_mongoc_cmd_check_ok (reply, MONGOC_ERROR_API_VERSION_2, &error)) {
5453
return true;
5554
}
5655

5756
/* Change Streams Spec resumable criteria: "a server error response with an
5857
* error message containing the substring 'not master' or 'node is
5958
* recovering' */
60-
if (strstr (msg, "not master") || strstr (msg, "node is recovering")) {
59+
if (strstr (error.message, "not master") ||
60+
strstr (error.message, "node is recovering")) {
6161
return true;
6262
}
6363

6464
/* Change Streams Spec resumable criteria: "any server error response from a
6565
* getMore command excluding those containing the following error codes" */
66-
switch (code) {
66+
switch (error.code) {
6767
case 11601: /* Interrupted */
6868
case 136: /* CappedPositionLost */
6969
case 237: /* CursorKilled */

src/libmongoc/src/mongoc/mongoc-cluster.c

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -412,16 +412,29 @@ typedef enum {
412412
} reply_error_type_t;
413413

414414

415+
/*---------------------------------------------------------------------------
416+
*
417+
* _check_not_master_or_recovering_error --
418+
*
419+
* Checks @reply for a "not master" or "node is recovering" error and
420+
* sets @error.
421+
*
422+
* Return:
423+
* A reply_error_type_t indicating if @reply contained a "not master"
424+
* or "node is recovering" error.
425+
*
426+
*--------------------------------------------------------------------------
427+
*/
415428
static reply_error_type_t
416-
_check_not_master_or_recovering_error (const bson_t *reply)
429+
_check_not_master_or_recovering_error (const mongoc_client_t *client,
430+
const bson_t *reply,
431+
bson_error_t *error)
417432
{
418-
const char *msg = "";
419-
uint32_t code;
420-
if (!_mongoc_parse_error_reply (reply, true /* check_wce */, &code, &msg)) {
433+
if (_mongoc_cmd_check_ok_no_wce (reply, client->error_api_version, error)) {
421434
return MONGOC_REPLY_ERR_TYPE_NONE;
422435
}
423436

424-
switch (code) {
437+
switch (error->code) {
425438
case 11600: /* InterruptedAtShutdown */
426439
case 11602: /* InterruptedDueToReplStateChange */
427440
case 13436: /* NotMasterOrSecondary */
@@ -432,9 +445,9 @@ _check_not_master_or_recovering_error (const bson_t *reply)
432445
case 13435: /* NotMasterNoSlaveOk */
433446
return MONGOC_REPLY_ERR_TYPE_NOT_MASTER;
434447
default:
435-
if (strstr (msg, "not master")) {
448+
if (strstr (error->message, "not master")) {
436449
return MONGOC_REPLY_ERR_TYPE_NOT_MASTER;
437-
} else if (strstr (msg, "node is recovering")) {
450+
} else if (strstr (error->message, "node is recovering")) {
438451
return MONGOC_REPLY_ERR_TYPE_NODE_IS_RECOVERING;
439452
}
440453
return MONGOC_REPLY_ERR_TYPE_NONE;
@@ -445,19 +458,19 @@ _check_not_master_or_recovering_error (const bson_t *reply)
445458
static void
446459
handle_not_master_error (mongoc_cluster_t *cluster,
447460
uint32_t server_id,
448-
const bson_error_t *error,
449461
const bson_t *reply)
450462
{
451463
mongoc_topology_t *topology = cluster->client->topology;
464+
bson_error_t error;
452465
reply_error_type_t error_type =
453-
_check_not_master_or_recovering_error (reply);
466+
_check_not_master_or_recovering_error (cluster->client, reply, &error);
454467

455468
if (error_type != MONGOC_REPLY_ERR_TYPE_NONE) {
456469
/* Server Discovery and Monitoring Spec: "When the client sees a 'not
457470
* master' or 'node is recovering' error it MUST replace the server's
458471
* description with a default ServerDescription of type Unknown."
459472
*/
460-
mongoc_topology_invalidate_server (topology, server_id, error);
473+
mongoc_topology_invalidate_server (topology, server_id, &error);
461474
if (topology->single_threaded) {
462475
/* SDAM Spec: "For single-threaded clients, in the case of a 'not
463476
* master' error, the client MUST check the server immediately... For a
@@ -572,7 +585,7 @@ mongoc_cluster_run_command_monitored (mongoc_cluster_t *cluster,
572585
mongoc_apm_command_failed_cleanup (&failed_event);
573586
}
574587

575-
handle_not_master_error (cluster, server_id, error, reply);
588+
handle_not_master_error (cluster, server_id, reply);
576589

577590
if (reply == &reply_local) {
578591
bson_destroy (&reply_local);
@@ -627,7 +640,7 @@ mongoc_cluster_run_command_private (mongoc_cluster_t *cluster,
627640
retval = mongoc_cluster_run_command_opquery (
628641
cluster, cmd, cmd->server_stream->stream, -1, reply, error);
629642
}
630-
handle_not_master_error (cluster, server_stream->sd->id, error, reply);
643+
handle_not_master_error (cluster, server_stream->sd->id, reply);
631644
if (reply == &reply_local) {
632645
bson_destroy (&reply_local);
633646
}

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ _mongoc_cmd_check_ok (const bson_t *doc,
166166
int32_t error_api_version,
167167
bson_error_t *error);
168168

169+
bool
170+
_mongoc_cmd_check_ok_no_wce (const bson_t *doc,
171+
int32_t error_api_version,
172+
bson_error_t *error);
173+
169174
bool
170175
_mongoc_rpc_decompress (mongoc_rpc_t *rpc_le, uint8_t *buf, size_t buflen);
171176

@@ -175,12 +180,6 @@ _mongoc_rpc_compress (struct _mongoc_cluster_t *cluster,
175180
mongoc_rpc_t *rpc_le,
176181
bson_error_t *error);
177182

178-
bool
179-
_mongoc_parse_error_reply (const bson_t *doc,
180-
bool check_wce,
181-
uint32_t *code,
182-
const char **msg);
183-
184183
BSON_END_DECLS
185184

186185

src/libmongoc/src/mongoc/mongoc-rpc.c

Lines changed: 99 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,67 @@ _mongoc_rpc_prep_command (mongoc_rpc_t *rpc,
10651065
}
10661066

10671067

1068+
/* returns true if an error was found. */
1069+
static bool
1070+
_parse_error_reply (const bson_t *doc,
1071+
bool check_wce,
1072+
uint32_t *code,
1073+
const char **msg)
1074+
{
1075+
bson_iter_t iter;
1076+
bool found_error = false;
1077+
1078+
ENTRY;
1079+
1080+
BSON_ASSERT (doc);
1081+
BSON_ASSERT (code);
1082+
*code = 0;
1083+
1084+
if (bson_iter_init_find (&iter, doc, "code") &&
1085+
BSON_ITER_HOLDS_INT32 (&iter)) {
1086+
*code = (uint32_t) bson_iter_int32 (&iter);
1087+
found_error = true;
1088+
}
1089+
1090+
if (bson_iter_init_find (&iter, doc, "errmsg") &&
1091+
BSON_ITER_HOLDS_UTF8 (&iter)) {
1092+
*msg = bson_iter_utf8 (&iter, NULL);
1093+
found_error = true;
1094+
} else if (bson_iter_init_find (&iter, doc, "$err") &&
1095+
BSON_ITER_HOLDS_UTF8 (&iter)) {
1096+
*msg = bson_iter_utf8 (&iter, NULL);
1097+
found_error = true;
1098+
}
1099+
1100+
if (found_error) {
1101+
/* there was a command error */
1102+
RETURN (true);
1103+
}
1104+
1105+
if (check_wce) {
1106+
/* check for a write concern error */
1107+
if (bson_iter_init_find (&iter, doc, "writeConcernError") &&
1108+
BSON_ITER_HOLDS_DOCUMENT (&iter)) {
1109+
bson_iter_t child;
1110+
BSON_ASSERT (bson_iter_recurse (&iter, &child));
1111+
if (bson_iter_find (&child, "code") &&
1112+
BSON_ITER_HOLDS_INT32 (&child)) {
1113+
*code = (uint32_t) bson_iter_int32 (&child);
1114+
found_error = true;
1115+
}
1116+
BSON_ASSERT (bson_iter_recurse (&iter, &child));
1117+
if (bson_iter_find (&child, "errmsg") &&
1118+
BSON_ITER_HOLDS_UTF8 (&child)) {
1119+
*msg = bson_iter_utf8 (&child, NULL);
1120+
found_error = true;
1121+
}
1122+
}
1123+
}
1124+
1125+
RETURN (found_error);
1126+
}
1127+
1128+
10681129
/*
10691130
*--------------------------------------------------------------------------
10701131
*
@@ -1104,7 +1165,7 @@ _mongoc_cmd_check_ok (const bson_t *doc,
11041165
RETURN (true);
11051166
}
11061167

1107-
if (!_mongoc_parse_error_reply (doc, false /* check_wce */, &code, &msg)) {
1168+
if (!_parse_error_reply (doc, false /* check_wce */, &code, &msg)) {
11081169
RETURN (true);
11091170
}
11101171

@@ -1120,66 +1181,57 @@ _mongoc_cmd_check_ok (const bson_t *doc,
11201181
RETURN (false);
11211182
}
11221183

1123-
/* returns true if an error was found. */
1184+
/*
1185+
*--------------------------------------------------------------------------
1186+
*
1187+
* _mongoc_cmd_check_ok_no_wce --
1188+
*
1189+
* Check if a server reply document is an error message.
1190+
* Optionally fill out a bson_error_t from the server error.
1191+
* If the response contains a writeConcernError, this is considered
1192+
* an error and returns false.
1193+
*
1194+
* Returns:
1195+
* false if @doc is an error message, true otherwise.
1196+
*
1197+
* Side effects:
1198+
* If @doc is an error reply and @error is not NULL, set its
1199+
* domain, code, and message.
1200+
*
1201+
*--------------------------------------------------------------------------
1202+
*/
11241203
bool
1125-
_mongoc_parse_error_reply (const bson_t *doc,
1126-
bool check_wce,
1127-
uint32_t *code,
1128-
const char **msg)
1204+
_mongoc_cmd_check_ok_no_wce (const bson_t *doc,
1205+
int32_t error_api_version,
1206+
bson_error_t *error)
11291207
{
1130-
bson_iter_t iter;
1131-
bool found_error = false;
1208+
mongoc_error_domain_t domain =
1209+
error_api_version >= MONGOC_ERROR_API_VERSION_2 ? MONGOC_ERROR_SERVER
1210+
: MONGOC_ERROR_QUERY;
1211+
uint32_t code;
1212+
const char *msg = "Unknown command error";
11321213

11331214
ENTRY;
11341215

11351216
BSON_ASSERT (doc);
1136-
BSON_ASSERT (code);
1137-
*code = 0;
1138-
1139-
if (bson_iter_init_find (&iter, doc, "code") &&
1140-
BSON_ITER_HOLDS_INT32 (&iter)) {
1141-
*code = (uint32_t) bson_iter_int32 (&iter);
1142-
found_error = true;
1143-
}
1144-
1145-
if (bson_iter_init_find (&iter, doc, "errmsg") &&
1146-
BSON_ITER_HOLDS_UTF8 (&iter)) {
1147-
*msg = bson_iter_utf8 (&iter, NULL);
1148-
found_error = true;
1149-
} else if (bson_iter_init_find (&iter, doc, "$err") &&
1150-
BSON_ITER_HOLDS_UTF8 (&iter)) {
1151-
*msg = bson_iter_utf8 (&iter, NULL);
1152-
found_error = true;
1153-
}
11541217

1155-
if (found_error) {
1156-
/* there was a command error */
1218+
if (!_parse_error_reply (doc, true /* check_wce */, &code, &msg)) {
11571219
RETURN (true);
11581220
}
11591221

1160-
if (check_wce) {
1161-
/* check for a write concern error */
1162-
if (bson_iter_init_find (&iter, doc, "writeConcernError") &&
1163-
BSON_ITER_HOLDS_DOCUMENT (&iter)) {
1164-
bson_iter_t child;
1165-
BSON_ASSERT (bson_iter_recurse (&iter, &child));
1166-
if (bson_iter_find (&child, "code") &&
1167-
BSON_ITER_HOLDS_INT32 (&child)) {
1168-
*code = (uint32_t) bson_iter_int32 (&child);
1169-
found_error = true;
1170-
}
1171-
BSON_ASSERT (bson_iter_recurse (&iter, &child));
1172-
if (bson_iter_find (&child, "errmsg") &&
1173-
BSON_ITER_HOLDS_UTF8 (&child)) {
1174-
*msg = bson_iter_utf8 (&child, NULL);
1175-
found_error = true;
1176-
}
1177-
}
1222+
if (code == MONGOC_ERROR_PROTOCOL_ERROR || code == 13390) {
1223+
code = MONGOC_ERROR_QUERY_COMMAND_NOT_FOUND;
1224+
} else if (code == 0) {
1225+
code = MONGOC_ERROR_QUERY_FAILURE;
11781226
}
11791227

1180-
RETURN (found_error);
1228+
bson_set_error (error, domain, code, "%s", msg);
1229+
1230+
/* there was a command error */
1231+
RETURN (false);
11811232
}
11821233

1234+
11831235
/* helper function to parse error reply document to an OP_QUERY */
11841236
static void
11851237
_mongoc_populate_query_error (const bson_t *doc,

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -530,12 +530,10 @@ mongoc_server_description_handle_ismaster (mongoc_server_description_t *sd,
530530
num_keys++;
531531
if (strcmp ("ok", bson_iter_key (&iter)) == 0) {
532532
if (!bson_iter_as_bool (&iter)) {
533-
uint32_t unused;
534-
const char *msg = NULL;
535-
_mongoc_parse_error_reply (ismaster_response, false, &unused, &msg);
536-
if (msg) {
537-
bson_strncpy (sd->error.message, msg, sizeof (error->message));
538-
}
533+
/* it doesn't really matter what error API we use. the code and
534+
* domain will be overwritten. */
535+
(void) _mongoc_cmd_check_ok (
536+
ismaster_response, MONGOC_ERROR_API_VERSION_2, &sd->error);
539537
/* ismaster response returned ok: 0. According to auth spec: "If the
540538
* isMaster of the MongoDB Handshake fails with an error, drivers
541539
* MUST treat this an an authentication error." */

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,13 +1442,13 @@ _mongoc_write_result_complete (
14421442
bool
14431443
_mongoc_write_is_retryable_error (const bson_t *reply)
14441444
{
1445-
const char *msg = "";
1446-
uint32_t code;
1447-
if (!_mongoc_parse_error_reply (reply, true /* check_wce */, &code, &msg)) {
1445+
bson_error_t error;
1446+
if (_mongoc_cmd_check_ok_no_wce (
1447+
reply, MONGOC_ERROR_API_VERSION_2, &error)) {
14481448
return false;
14491449
}
14501450

1451-
switch (code) {
1451+
switch (error.code) {
14521452
case 11600: /* InterruptedAtShutdown */
14531453
case 11602: /* InterruptedDueToReplStateChange */
14541454
case 10107: /* NotMaster */
@@ -1463,7 +1463,8 @@ _mongoc_write_is_retryable_error (const bson_t *reply)
14631463
case 9001: /* SocketException */
14641464
return true;
14651465
default:
1466-
if (strstr (msg, "not master") || strstr (msg, "node is recovering")) {
1466+
if (strstr (error.message, "not master") ||
1467+
strstr (error.message, "node is recovering")) {
14671468
return true;
14681469
}
14691470
return false;

0 commit comments

Comments
 (0)