Retry with separated Dio to avoid Deadlock in error queue

This commit is contained in:
Dat H. Pham
2026-02-10 09:48:32 +07:00
committed by Dat H. Pham
parent 16131e01ad
commit d02676dfb7
2 changed files with 261 additions and 29 deletions
@@ -179,36 +179,18 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
}
if (isRetryRequest) {
if (extraInRequest.containsKey(FileUploader.uploadAttachmentExtraKey)) {
log('AuthorizationInterceptors::onError: Retry upload request with TokenId = ${_token?.tokenIdHash}');
final uploadExtra = extraInRequest[FileUploader.uploadAttachmentExtraKey];
requestOptions.headers[HttpHeaders.authorizationHeader] = _getTokenAsBearerHeader(_token!.token);
// Mark as attempted to prevent infinite retry loops
requestOptions.extra[_refreshAttemptedKey] = true;
final newOptions = Options(
method: requestOptions.method,
headers: requestOptions.headers,
extra: requestOptions.extra,
);
final response = await _dio.request(
requestOptions.path,
data: _getDataUploadRequest(uploadExtra),
queryParameters: requestOptions.queryParameters,
options: newOptions,
);
return handler.resolve(response);
} else {
log('AuthorizationInterceptors::onError: Retry request with TokenId = ${_token?.tokenIdHash}');
requestOptions.headers[HttpHeaders.authorizationHeader] = _getTokenAsBearerHeader(_token!.token);
// Mark as attempted to prevent infinite retry loops
requestOptions.extra[_refreshAttemptedKey] = true;
final response = await _dio.fetch(requestOptions);
try {
final response = await _retryRequest(requestOptions, extraInRequest);
return handler.resolve(response);
} catch (retryError) {
logError(
'AuthorizationInterceptors::onError: '
'Retry failed with error=$retryError',
);
return super.onError(
err.copyWith(error: retryError),
handler,
);
}
} else {
return super.onError(err, handler);
@@ -379,6 +361,44 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
return _invokeRefreshTokenFromServer();
}
Future<Response> _retryRequest(
RequestOptions requestOptions,
Map<String, dynamic> extraInRequest,
) {
requestOptions.headers[HttpHeaders.authorizationHeader] =
_getTokenAsBearerHeader(_token!.token);
requestOptions.extra[_refreshAttemptedKey] = true;
final retryDio = _createRetryDio();
if (extraInRequest.containsKey(FileUploader.uploadAttachmentExtraKey)) {
log('AuthorizationInterceptors::_retryRequest: '
'Retry upload request with TokenId = ${_token?.tokenIdHash}');
final uploadExtra =
extraInRequest[FileUploader.uploadAttachmentExtraKey];
return retryDio.request(
requestOptions.path,
data: _getDataUploadRequest(uploadExtra),
queryParameters: requestOptions.queryParameters,
options: Options(
method: requestOptions.method,
headers: requestOptions.headers,
extra: requestOptions.extra,
),
);
} else {
log('AuthorizationInterceptors::_retryRequest: '
'Retry request with TokenId = ${_token?.tokenIdHash}');
return retryDio.fetch(requestOptions);
}
}
/// Creates a separate Dio instance without interceptors for retry requests.
/// This avoids deadlock when retrying inside [onError] of [QueuedInterceptorsWrapper].
Dio _createRetryDio() => Dio(_dio.options)
..httpClientAdapter = _dio.httpClientAdapter;
void clear() {
_authorization = null;
_token = null;