@@ -97,79 +97,89 @@ private CompletableFuture<ApiResponse<T>> attemptHttpRequest(
9797 .handle ((response , throwable ) -> {
9898 if (throwable != null ) {
9999 // Handle network errors (no HTTP response received)
100- if (retryNumber < configuration .getMaxRetries ()) {
101- // Network errors should be retried with exponential backoff (no Retry-After header available)
102- Duration retryDelay = RetryStrategy .calculateRetryDelay (Optional .empty (), retryNumber );
103- HttpClient delayingClient = getDelayedHttpClient (retryDelay );
104- return attemptHttpRequest (delayingClient , retryNumber + 1 , throwable );
105- } else {
106- // Max retries exceeded, fail with the network error
107- return CompletableFuture .<ApiResponse <T >>failedFuture (new ApiException (throwable ));
108- }
100+ return handleNetworkError (throwable , retryNumber );
109101 }
110102 // No network error, proceed with normal HTTP response handling
111103 return processHttpResponse (response , retryNumber , previousError );
112104 })
113105 .thenCompose (future -> future );
114106 }
115107
116- private CompletableFuture <ApiResponse <T >> processHttpResponse (
117- HttpResponse <String > response , int retryNumber , Throwable previousError ) {
118- return CompletableFuture .completedFuture (response ).thenCompose (httpResponse -> {
119- Optional <FgaError > fgaError =
120- FgaError .getError (name , request , configuration , response , previousError );
108+ private CompletableFuture <ApiResponse <T >> handleNetworkError (Throwable throwable , int retryNumber ) {
109+ if (retryNumber < configuration .getMaxRetries ()) {
110+ // Network errors should be retried with exponential backoff (no Retry-After header available)
111+ Duration retryDelay = RetryStrategy .calculateRetryDelay (Optional .empty (), retryNumber );
121112
122- if (fgaError .isPresent ()) {
123- FgaError error = fgaError .get ();
124- int statusCode = error .getStatusCode ();
113+ // Add telemetry for network error retry
114+ addTelemetryAttribute (Attributes .HTTP_REQUEST_RESEND_COUNT , String .valueOf (retryNumber + 1 ));
125115
126- if (retryNumber < configuration .getMaxRetries ()) {
127- // Parse Retry-After header if present
128- Optional <Duration > retryAfterDelay = response .headers ()
129- .firstValue ("retry-after" )
130- .flatMap (RetryAfterHeaderParser ::parseRetryAfter );
116+ // Create delayed client and retry asynchronously without blocking
117+ HttpClient delayingClient = getDelayedHttpClient (retryDelay );
118+ return attemptHttpRequest (delayingClient , retryNumber + 1 , throwable );
119+ } else {
120+ // Max retries exceeded, fail with the network error
121+ return CompletableFuture .failedFuture (new ApiException (throwable ));
122+ }
123+ }
131124
132- boolean hasValidRetryAfter = retryAfterDelay .isPresent ();
125+ private CompletableFuture <ApiResponse <T >> handleHttpErrorRetry (
126+ Optional <Duration > retryAfterDelay , int retryNumber , FgaError error ) {
127+ // Calculate appropriate delay
128+ Duration retryDelay = RetryStrategy .calculateRetryDelay (retryAfterDelay , retryNumber );
133129
134- // Check if we should retry based on the new strategy
135- if ( RetryStrategy . shouldRetry ( request , statusCode , hasValidRetryAfter )) {
136- // Calculate appropriate delay
137- Duration retryDelay = RetryStrategy . calculateRetryDelay ( retryAfterDelay , retryNumber );
130+ // Create delayed client and retry asynchronously without blocking
131+ HttpClient delayingClient = getDelayedHttpClient ( retryDelay );
132+ return attemptHttpRequest ( delayingClient , retryNumber + 1 , error );
133+ }
138134
139- HttpClient delayingClient = getDelayedHttpClient (retryDelay );
135+ private CompletableFuture <ApiResponse <T >> processHttpResponse (
136+ HttpResponse <String > response , int retryNumber , Throwable previousError ) {
137+ Optional <FgaError > fgaError = FgaError .getError (name , request , configuration , response , previousError );
140138
141- return attemptHttpRequest ( delayingClient , retryNumber + 1 , error );
142- }
143- }
139+ if ( fgaError . isPresent ()) {
140+ FgaError error = fgaError . get ();
141+ int statusCode = error . getStatusCode ();
144142
145- return CompletableFuture .failedFuture (error );
146- }
143+ if (retryNumber < configuration .getMaxRetries ()) {
144+ // Parse Retry-After header if present
145+ Optional <Duration > retryAfterDelay =
146+ response .headers ().firstValue ("retry-after" ).flatMap (RetryAfterHeaderParser ::parseRetryAfter );
147147
148- addTelemetryAttributes ( Attributes . fromHttpResponse ( response , this . configuration . getCredentials ()) );
148+ boolean hasValidRetryAfter = retryAfterDelay . isPresent ( );
149149
150- if (retryNumber > 0 ) {
151- addTelemetryAttribute (Attributes .HTTP_REQUEST_RESEND_COUNT , String .valueOf (retryNumber ));
152- }
150+ // Check if we should retry based on the new strategy
151+ if (RetryStrategy .shouldRetry (request , statusCode , hasValidRetryAfter )) {
152+ return handleHttpErrorRetry (retryAfterDelay , retryNumber , error );
153+ } else {
154+ }
155+ }
153156
154- if (response .headers ().firstValue ("fga-query-duration-ms" ).isPresent ()) {
155- String queryDuration = response .headers ()
156- .firstValue ("fga-query-duration-ms" )
157- .orElse (null );
157+ return CompletableFuture .failedFuture (error );
158+ }
158159
159- if (!isNullOrWhitespace (queryDuration )) {
160- double queryDurationDouble = Double .parseDouble (queryDuration );
161- telemetry .metrics ().queryDuration (queryDurationDouble , this .getTelemetryAttributes ());
162- }
163- }
160+ addTelemetryAttributes (Attributes .fromHttpResponse (response , this .configuration .getCredentials ()));
161+
162+ if (retryNumber > 0 ) {
163+ addTelemetryAttribute (Attributes .HTTP_REQUEST_RESEND_COUNT , String .valueOf (retryNumber ));
164+ }
165+
166+ if (response .headers ().firstValue ("fga-query-duration-ms" ).isPresent ()) {
167+ String queryDuration =
168+ response .headers ().firstValue ("fga-query-duration-ms" ).orElse (null );
169+
170+ if (!isNullOrWhitespace (queryDuration )) {
171+ double queryDurationDouble = Double .parseDouble (queryDuration );
172+ telemetry .metrics ().queryDuration (queryDurationDouble , this .getTelemetryAttributes ());
173+ }
174+ }
164175
165- Double requestDuration = (double ) (System .currentTimeMillis () - requestStarted );
176+ Double requestDuration = (double ) (System .currentTimeMillis () - requestStarted );
166177
167- telemetry .metrics ().requestDuration (requestDuration , this .getTelemetryAttributes ());
178+ telemetry .metrics ().requestDuration (requestDuration , this .getTelemetryAttributes ());
168179
169- return deserializeResponse (response )
170- .thenApply (modeledResponse -> new ApiResponse <>(
171- response .statusCode (), response .headers ().map (), response .body (), modeledResponse ));
172- });
180+ return deserializeResponse (response )
181+ .thenApply (modeledResponse -> new ApiResponse <>(
182+ response .statusCode (), response .headers ().map (), response .body (), modeledResponse ));
173183 }
174184
175185 private CompletableFuture <T > deserializeResponse (HttpResponse <String > response ) {
0 commit comments