Files
sendico/frontend/pshared/lib/service/authorization/retry_helper.dart
Stephan D c6a56071b5
Some checks failed
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
ci/woodpecker/push/bump_version Pipeline failed
+signup +login
2025-11-17 20:16:45 +01:00

87 lines
2.7 KiB
Dart

import 'dart:math';
import 'package:logging/logging.dart';
import 'package:pshared/utils/exception.dart';
class RetryHelper {
static final _logger = Logger('auth.retry');
/// Executes an operation with exponential backoff retry logic
static Future<T> withExponentialBackoff<T>(
Future<T> Function() operation, {
int maxRetries = 3,
Duration initialDelay = const Duration(milliseconds: 500),
double backoffMultiplier = 2.0,
Duration maxDelay = const Duration(seconds: 30),
bool Function(Exception)? shouldRetry,
}) async {
Exception? lastException;
// Total attempts = initial attempt + maxRetries
final totalAttempts = maxRetries + 1;
for (int attempt = 1; attempt <= totalAttempts; attempt++) {
try {
_logger.fine('Attempting operation (attempt $attempt/$totalAttempts)');
return await operation();
} catch (e) {
lastException = toException(e);
// Don't retry if we've reached max attempts
if (attempt == totalAttempts) {
_logger.warning('Operation failed after $totalAttempts attempts: $lastException');
rethrow;
}
// Check if we should retry this specific error
if (shouldRetry != null && !shouldRetry(lastException)) {
_logger.fine('Operation failed with non-retryable error: $lastException');
rethrow;
}
// Calculate delay with exponential backoff
final delayMs = min(
initialDelay.inMilliseconds * pow(backoffMultiplier, attempt - 1).toInt(),
maxDelay.inMilliseconds,
);
final delay = Duration(milliseconds: delayMs);
_logger.fine('Operation failed (attempt $attempt), retrying in ${delay.inMilliseconds}ms: $lastException');
await Future.delayed(delay);
}
}
// This should never be reached due to rethrow above, but just in case
throw lastException ?? Exception('Retry logic error');
}
/// Determines if an error is retryable (network/temporary errors)
static bool isRetryableError(Exception error) {
final errorString = error.toString().toLowerCase();
// Network connectivity issues
if (errorString.contains('socket') ||
errorString.contains('connection') ||
errorString.contains('timeout') ||
errorString.contains('network')) {
return true;
}
// Server temporary errors (5xx)
if (errorString.contains('500') ||
errorString.contains('502') ||
errorString.contains('503') ||
errorString.contains('504')) {
return true;
}
// Rate limiting
if (errorString.contains('429')) {
return true;
}
return false;
}
}