@@ -2362,9 +2362,6 @@ mongoc_cluster_stream_for_server (mongoc_cluster_t *cluster,
2362
2362
BSON_ASSERT (reply || true);
2363
2363
BSON_ASSERT (error || true);
2364
2364
2365
- mongoc_server_stream_t * server_stream = NULL ;
2366
- bson_error_t err_local = {0 };
2367
-
2368
2365
ENTRY ;
2369
2366
2370
2367
BSON_ASSERT (cluster );
@@ -2378,12 +2375,10 @@ mongoc_cluster_stream_for_server (mongoc_cluster_t *cluster,
2378
2375
RETURN (NULL );
2379
2376
}
2380
2377
2381
- if (!error ) {
2382
- error = & err_local ;
2383
- }
2384
2378
2385
- server_stream = _mongoc_cluster_stream_for_server (
2386
- cluster , server_id , reconnect_ok , cs , reply , error );
2379
+ mongoc_server_stream_t * const server_stream =
2380
+ _mongoc_cluster_stream_for_server (
2381
+ cluster , server_id , reconnect_ok , cs , reply , error );
2387
2382
2388
2383
if (_in_sharded_txn (cs )) {
2389
2384
_mongoc_client_session_pin (cs , server_id );
@@ -2801,6 +2796,7 @@ _mongoc_cluster_stream_for_optype (mongoc_cluster_t *cluster,
2801
2796
mongoc_ss_optype_t optype ,
2802
2797
const mongoc_read_prefs_t * read_prefs ,
2803
2798
mongoc_client_session_t * cs ,
2799
+ bool is_retryable ,
2804
2800
bson_t * reply ,
2805
2801
bson_error_t * error )
2806
2802
{
@@ -2844,14 +2840,73 @@ _mongoc_cluster_stream_for_optype (mongoc_cluster_t *cluster,
2844
2840
}
2845
2841
}
2846
2842
2847
- /* connect or reconnect to server if necessary */
2848
- server_stream = _mongoc_cluster_stream_for_server (
2849
- cluster , server_id , true /* reconnect_ok */ , cs , reply , error );
2843
+ bson_t first_reply ;
2844
+ bson_error_t first_error = {0 };
2845
+
2846
+ server_stream = _mongoc_cluster_stream_for_server (cluster ,
2847
+ server_id ,
2848
+ true /* reconnect_ok */ ,
2849
+ cs ,
2850
+ & first_reply ,
2851
+ & first_error );
2852
+
2850
2853
if (server_stream ) {
2851
2854
server_stream -> must_use_primary = must_use_primary ;
2855
+ RETURN (server_stream );
2852
2856
}
2853
2857
2854
- RETURN (server_stream );
2858
+ // Important: authentication errors are also considered retryable even if
2859
+ // they not considered a network error.
2860
+ const bool retryable_error = _mongoc_error_is_network (& first_error ) ||
2861
+ _mongoc_error_is_auth (& first_error );
2862
+
2863
+ if (is_retryable && retryable_error ) {
2864
+ bson_t retry_reply ;
2865
+ bson_error_t retry_error = {0 };
2866
+
2867
+ server_stream =
2868
+ _mongoc_cluster_stream_for_server (cluster ,
2869
+ server_id ,
2870
+ true /* reconnect_ok */ ,
2871
+ cs ,
2872
+ & retry_reply ,
2873
+ & retry_error );
2874
+
2875
+ if (server_stream ) {
2876
+ server_stream -> must_use_primary = must_use_primary ;
2877
+ server_stream -> retry_attempted = true;
2878
+ bson_destroy (& first_reply );
2879
+ RETURN (server_stream );
2880
+ }
2881
+
2882
+ if (optype != MONGOC_SS_READ ) {
2883
+ // Retryable Writes Spec: When the driver encounters a network error
2884
+ // establishing an initial connection to a server, it MUST add a
2885
+ // RetryableWriteError label to that error if the MongoClient
2886
+ // performing the operation has the retryWrites configuration option
2887
+ // set to true.
2888
+ _mongoc_write_error_append_retryable_label (& first_reply );
2889
+ }
2890
+
2891
+ bson_destroy (& retry_reply );
2892
+ }
2893
+
2894
+ // Retryable Writes Spec: If the driver cannot select a server for the retry
2895
+ // attempt [...], retrying is not possible and drivers MUST raise the
2896
+ // original retryable error.
2897
+ {
2898
+ if (reply ) {
2899
+ bson_steal (reply , & first_reply );
2900
+ } else {
2901
+ bson_destroy (& first_reply );
2902
+ }
2903
+
2904
+ if (error ) {
2905
+ * error = first_error ;
2906
+ }
2907
+ }
2908
+
2909
+ RETURN (NULL );
2855
2910
}
2856
2911
2857
2912
mongoc_server_stream_t *
@@ -2864,8 +2919,14 @@ mongoc_cluster_stream_for_reads (mongoc_cluster_t *cluster,
2864
2919
const mongoc_read_prefs_t * const prefs_override =
2865
2920
_mongoc_client_session_in_txn (cs ) ? cs -> txn .opts .read_prefs : read_prefs ;
2866
2921
2922
+ // Retryable Reads Spec: This boolean option determines whether retryable
2923
+ // behavior will be applied to all read operations executed within the
2924
+ // MongoClient.
2925
+ const bool is_retryable = mongoc_uri_get_option_as_bool (
2926
+ cluster -> uri , MONGOC_URI_RETRYREADS , MONGOC_DEFAULT_RETRYREADS );
2927
+
2867
2928
return _mongoc_cluster_stream_for_optype (
2868
- cluster , MONGOC_SS_READ , prefs_override , cs , reply , error );
2929
+ cluster , MONGOC_SS_READ , prefs_override , cs , is_retryable , reply , error );
2869
2930
}
2870
2931
2871
2932
mongoc_server_stream_t *
@@ -2874,8 +2935,11 @@ mongoc_cluster_stream_for_writes (mongoc_cluster_t *cluster,
2874
2935
bson_t * reply ,
2875
2936
bson_error_t * error )
2876
2937
{
2938
+ const bool is_retryable = mongoc_uri_get_option_as_bool (
2939
+ cluster -> uri , MONGOC_URI_RETRYWRITES , MONGOC_DEFAULT_RETRYWRITES );
2940
+
2877
2941
return _mongoc_cluster_stream_for_optype (
2878
- cluster , MONGOC_SS_WRITE , NULL , cs , reply , error );
2942
+ cluster , MONGOC_SS_WRITE , NULL , cs , is_retryable , reply , error );
2879
2943
}
2880
2944
2881
2945
mongoc_server_stream_t *
@@ -2889,10 +2953,14 @@ mongoc_cluster_stream_for_aggr_with_write (
2889
2953
const mongoc_read_prefs_t * const prefs_override =
2890
2954
_mongoc_client_session_in_txn (cs ) ? cs -> txn .opts .read_prefs : read_prefs ;
2891
2955
2956
+ const bool is_retryable = mongoc_uri_get_option_as_bool (
2957
+ cluster -> uri , MONGOC_URI_RETRYWRITES , MONGOC_DEFAULT_RETRYWRITES );
2958
+
2892
2959
return _mongoc_cluster_stream_for_optype (cluster ,
2893
2960
MONGOC_SS_AGGREGATE_WITH_WRITE ,
2894
2961
prefs_override ,
2895
2962
cs ,
2963
+ is_retryable ,
2896
2964
reply ,
2897
2965
error );
2898
2966
}
0 commit comments