+signup +login
Some checks failed
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db 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
Some checks failed
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db 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
This commit is contained in:
103
frontend/pshared/lib/service/authorization/token_mutex.dart
Normal file
103
frontend/pshared/lib/service/authorization/token_mutex.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
/// Mutex to prevent concurrent token refresh operations
|
||||
/// This ensures only one refresh operation happens at a time,
|
||||
/// preventing race conditions during app startup when multiple
|
||||
/// providers try to refresh tokens simultaneously.
|
||||
class TokenRefreshMutex {
|
||||
static final _instance = TokenRefreshMutex._();
|
||||
factory TokenRefreshMutex() => _instance;
|
||||
TokenRefreshMutex._();
|
||||
|
||||
static final _logger = Logger('service.authorization.token_mutex');
|
||||
|
||||
Completer<String>? _currentRefresh;
|
||||
Completer<void>? _currentRotation;
|
||||
|
||||
/// Execute a token refresh operation with mutex protection
|
||||
/// If another refresh is in progress, wait for it to complete
|
||||
Future<String> executeRefresh(Future<String> Function() refreshOperation) async {
|
||||
if (_currentRefresh != null) {
|
||||
_logger.fine('Token refresh already in progress, waiting for completion');
|
||||
return await _currentRefresh!.future;
|
||||
}
|
||||
|
||||
_logger.fine('Starting new token refresh operation');
|
||||
_currentRefresh = Completer<String>();
|
||||
|
||||
try {
|
||||
final result = await refreshOperation();
|
||||
if (_currentRefresh != null) {
|
||||
_currentRefresh!.complete(result);
|
||||
_logger.fine('Token refresh completed successfully');
|
||||
}
|
||||
return result;
|
||||
} catch (e, st) {
|
||||
_logger.warning('Token refresh failed', e, st);
|
||||
if (_currentRefresh != null) {
|
||||
_currentRefresh!.completeError(e, st);
|
||||
}
|
||||
rethrow;
|
||||
} finally {
|
||||
_currentRefresh = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a token rotation operation with mutex protection
|
||||
/// If another rotation is in progress, wait for it to complete
|
||||
Future<void> executeRotation(Future<void> Function() rotationOperation) async {
|
||||
if (_currentRotation != null) {
|
||||
_logger.fine('Token rotation already in progress, waiting for completion');
|
||||
return await _currentRotation!.future;
|
||||
}
|
||||
|
||||
_logger.fine('Starting new token rotation operation');
|
||||
_currentRotation = Completer<void>();
|
||||
|
||||
try {
|
||||
await rotationOperation();
|
||||
if (_currentRotation != null) {
|
||||
_currentRotation!.complete();
|
||||
_logger.fine('Token rotation completed successfully');
|
||||
}
|
||||
} catch (e, st) {
|
||||
_logger.warning('Token rotation failed', e, st);
|
||||
if (_currentRotation != null) {
|
||||
_currentRotation!.completeError(e, st);
|
||||
}
|
||||
rethrow;
|
||||
} finally {
|
||||
_currentRotation = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a refresh operation is currently in progress
|
||||
bool get isRefreshInProgress => _currentRefresh != null;
|
||||
|
||||
/// Check if a rotation operation is currently in progress
|
||||
bool get isRotationInProgress => _currentRotation != null;
|
||||
|
||||
/// Get current status for debugging
|
||||
Map<String, dynamic> getStatus() {
|
||||
return {
|
||||
'refreshInProgress': isRefreshInProgress,
|
||||
'rotationInProgress': isRotationInProgress,
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Force reset the mutex (for testing or emergency situations)
|
||||
void forceReset() {
|
||||
_logger.warning('Force resetting token refresh mutex');
|
||||
if (_currentRefresh != null && !_currentRefresh!.isCompleted) {
|
||||
_currentRefresh!.completeError(Exception('Mutex force reset'));
|
||||
}
|
||||
if (_currentRotation != null && !_currentRotation!.isCompleted) {
|
||||
_currentRotation!.completeError(Exception('Mutex force reset'));
|
||||
}
|
||||
_currentRefresh = null;
|
||||
_currentRotation = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user