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
92 lines
3.0 KiB
Dart
92 lines
3.0 KiB
Dart
import 'package:logging/logging.dart';
|
|
|
|
/// Circuit breaker pattern implementation for authentication service failures
|
|
class AuthCircuitBreaker {
|
|
static final _logger = Logger('service.auth_circuit_breaker');
|
|
|
|
static int _failureCount = 0;
|
|
static DateTime? _lastFailure;
|
|
static const int _failureThreshold = 3;
|
|
static const Duration _recoveryTime = Duration(minutes: 5);
|
|
|
|
/// Returns true if the circuit breaker is open (blocking operations)
|
|
static bool get isOpen {
|
|
if (_failureCount < _failureThreshold) return false;
|
|
if (_lastFailure == null) return false;
|
|
|
|
final isOpen = DateTime.now().difference(_lastFailure!) < _recoveryTime;
|
|
if (isOpen) {
|
|
_logger.warning('Circuit breaker is OPEN. Failure count: $_failureCount, last failure: $_lastFailure');
|
|
}
|
|
return isOpen;
|
|
}
|
|
|
|
/// Returns true if the circuit breaker is in half-open state (allowing test requests)
|
|
static bool get isHalfOpen {
|
|
if (_failureCount < _failureThreshold) return false;
|
|
if (_lastFailure == null) return false;
|
|
|
|
return DateTime.now().difference(_lastFailure!) >= _recoveryTime;
|
|
}
|
|
|
|
/// Executes an operation with circuit breaker protection
|
|
static Future<T> execute<T>(Future<T> Function() operation) async {
|
|
if (isOpen) {
|
|
final timeSinceFailure = _lastFailure != null
|
|
? DateTime.now().difference(_lastFailure!)
|
|
: Duration.zero;
|
|
final timeUntilRecovery = _recoveryTime - timeSinceFailure;
|
|
|
|
_logger.warning('Circuit breaker blocking operation. Recovery in: ${timeUntilRecovery.inSeconds}s');
|
|
throw Exception('Auth service temporarily unavailable. Try again in ${timeUntilRecovery.inMinutes} minutes.');
|
|
}
|
|
|
|
try {
|
|
_logger.fine('Executing operation through circuit breaker');
|
|
final result = await operation();
|
|
_reset();
|
|
return result;
|
|
} catch (e) {
|
|
_recordFailure();
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// Records a failure and updates the circuit breaker state
|
|
static void _recordFailure() {
|
|
_failureCount++;
|
|
_lastFailure = DateTime.now();
|
|
_logger.warning('Auth circuit breaker recorded failure #$_failureCount at $_lastFailure');
|
|
|
|
if (_failureCount >= _failureThreshold) {
|
|
_logger.severe('Auth circuit breaker OPENED after $_failureCount failures');
|
|
}
|
|
}
|
|
|
|
/// Resets the circuit breaker to closed state
|
|
static void _reset() {
|
|
if (_failureCount > 0) {
|
|
_logger.info('Auth circuit breaker CLOSED - resetting failure count from $_failureCount to 0');
|
|
}
|
|
_failureCount = 0;
|
|
_lastFailure = null;
|
|
}
|
|
|
|
/// Manual reset (for testing or administrative purposes)
|
|
static void manualReset() {
|
|
_logger.info('Auth circuit breaker manually reset');
|
|
_reset();
|
|
}
|
|
|
|
/// Get current status for debugging
|
|
static Map<String, dynamic> getStatus() {
|
|
return {
|
|
'failureCount': _failureCount,
|
|
'lastFailure': _lastFailure?.toIso8601String(),
|
|
'isOpen': isOpen,
|
|
'isHalfOpen': isHalfOpen,
|
|
'threshold': _failureThreshold,
|
|
'recoveryTime': _recoveryTime.inSeconds,
|
|
};
|
|
}
|
|
} |