reconnect to relay if down

master
gitea 2 months ago
parent a477271d5f
commit 2db3339071

@ -9,11 +9,18 @@ class NostrRelay {
/// Whether the relay is enabled (should be used). /// Whether the relay is enabled (should be used).
bool isEnabled; bool isEnabled;
/// Number of consecutive connection failures.
int retryCount;
/// Maximum number of retry attempts before disabling.
static const int maxRetryAttempts = 3;
/// Creates a [NostrRelay] instance. /// Creates a [NostrRelay] instance.
NostrRelay({ NostrRelay({
required this.url, required this.url,
this.isConnected = false, this.isConnected = false,
this.isEnabled = true, this.isEnabled = true,
this.retryCount = 0,
}); });
/// Creates a [NostrRelay] from a URL string. /// Creates a [NostrRelay] from a URL string.
@ -23,7 +30,7 @@ class NostrRelay {
@override @override
String toString() { String toString() {
return 'NostrRelay(url: $url, connected: $isConnected, enabled: $isEnabled)'; return 'NostrRelay(url: $url, connected: $isConnected, enabled: $isEnabled, retryCount: $retryCount)';
} }
@override @override

@ -29,6 +29,9 @@ class NostrService {
final Map<String, StreamController<Map<String, dynamic>>> final Map<String, StreamController<Map<String, dynamic>>>
_messageControllers = {}; _messageControllers = {};
/// Retry timers for relays that are attempting to reconnect.
final Map<String, Timer> _retryTimers = {};
/// Creates a [NostrService] instance. /// Creates a [NostrService] instance.
NostrService(); NostrService();
@ -67,6 +70,9 @@ class NostrService {
orElse: () => throw NostrException('Relay not found: $relayUrl'), orElse: () => throw NostrException('Relay not found: $relayUrl'),
); );
relay.isEnabled = enabled; relay.isEnabled = enabled;
if (enabled) {
relay.retryCount = 0; // Reset retry count when enabling
}
// If disabling, also disconnect // If disabling, also disconnect
if (!enabled && relay.isConnected) { if (!enabled && relay.isConnected) {
@ -156,6 +162,7 @@ class NostrService {
// No errors occurred, connection is established // No errors occurred, connection is established
connectionConfirmed = true; connectionConfirmed = true;
relay.isConnected = true; relay.isConnected = true;
relay.retryCount = 0; // Reset retry count on successful connection
Logger.info('Connection confirmed for relay: $relayUrl (no errors after 500ms)'); Logger.info('Connection confirmed for relay: $relayUrl (no errors after 500ms)');
} }
}); });
@ -167,6 +174,7 @@ class NostrService {
if (!connectionConfirmed) { if (!connectionConfirmed) {
connectionConfirmed = true; connectionConfirmed = true;
relay.isConnected = true; relay.isConnected = true;
relay.retryCount = 0; // Reset retry count on successful connection
Logger.info('Connection confirmed for relay: $relayUrl (first message received)'); Logger.info('Connection confirmed for relay: $relayUrl (first message received)');
connectionTimer?.cancel(); connectionTimer?.cancel();
} }
@ -207,9 +215,7 @@ class NostrService {
connectionTimer?.cancel(); connectionTimer?.cancel();
Logger.error('WebSocket error for relay: $relayUrl', error); Logger.error('WebSocket error for relay: $relayUrl', error);
relay.isConnected = false; relay.isConnected = false;
// Automatically disable relay when connection error occurs _handleConnectionFailure(relayUrl, relay, controller, 'connection error');
relay.isEnabled = false;
Logger.warning('Relay $relayUrl disabled due to connection error');
controller.addError(NostrException('Relay error: $error')); controller.addError(NostrException('Relay error: $error'));
}, },
onDone: () { onDone: () {
@ -217,9 +223,7 @@ class NostrService {
connectionTimer?.cancel(); connectionTimer?.cancel();
Logger.warning('WebSocket stream closed for relay: $relayUrl'); Logger.warning('WebSocket stream closed for relay: $relayUrl');
relay.isConnected = false; relay.isConnected = false;
// Automatically disable relay when connection closes _handleConnectionFailure(relayUrl, relay, controller, 'stream closure');
relay.isEnabled = false;
Logger.warning('Relay $relayUrl disabled due to stream closure');
controller.close(); controller.close();
}, },
); );
@ -230,6 +234,101 @@ class NostrService {
} }
} }
/// Handles connection failure with retry logic.
///
/// [relayUrl] - The URL of the relay that failed.
/// [relay] - The relay object.
/// [controller] - The stream controller for this relay.
/// [reason] - Reason for the failure (for logging).
void _handleConnectionFailure(
String relayUrl,
NostrRelay relay,
StreamController<Map<String, dynamic>> controller,
String reason,
) {
// Cancel any existing retry timer
_retryTimers[relayUrl]?.cancel();
_retryTimers.remove(relayUrl);
// Only retry if relay is enabled
if (!relay.isEnabled) {
Logger.info('Relay $relayUrl is disabled, skipping retry');
return;
}
relay.retryCount++;
Logger.warning('Relay $relayUrl failed ($reason), retry attempt ${relay.retryCount}/${NostrRelay.maxRetryAttempts}');
if (relay.retryCount >= NostrRelay.maxRetryAttempts) {
// Max retries reached - disable the relay
relay.isEnabled = false;
relay.retryCount = 0; // Reset for future attempts
Logger.warning('Relay $relayUrl disabled after ${NostrRelay.maxRetryAttempts} failed attempts');
} else {
// Schedule retry after delay (5 seconds)
_scheduleRetry(relayUrl, relay, controller, reason);
}
}
/// Schedules a retry attempt for a failed relay connection.
///
/// [relayUrl] - The URL of the relay to retry.
/// [relay] - The relay object.
/// [controller] - The stream controller for this relay.
/// [reason] - Reason for the retry (for logging).
void _scheduleRetry(
String relayUrl,
NostrRelay relay,
StreamController<Map<String, dynamic>> controller,
String reason,
) {
Logger.info('Scheduling retry for relay $relayUrl in 5 seconds (attempt ${relay.retryCount}/${NostrRelay.maxRetryAttempts})');
_retryTimers[relayUrl] = Timer(const Duration(seconds: 5), () {
_retryTimers.remove(relayUrl);
if (relay.isEnabled && !relay.isConnected) {
Logger.info('Retrying connection to relay: $relayUrl');
try {
// Clean up old connection
disconnectRelay(relayUrl);
// Attempt to reconnect
connectRelay(relayUrl).then((stream) {
// Reset retry count on successful connection
relay.retryCount = 0;
Logger.info('Relay $relayUrl reconnected successfully');
// Cancel the stream subscription as we're just testing the connection
stream.listen(null).cancel();
}).catchError((e) {
Logger.warning('Retry failed for relay $relayUrl: $e');
// Handle retry failure - check if we should retry again or disable
if (relay.isEnabled) {
if (relay.retryCount >= NostrRelay.maxRetryAttempts) {
relay.isEnabled = false;
relay.retryCount = 0;
Logger.warning('Relay $relayUrl disabled after ${NostrRelay.maxRetryAttempts} failed attempts');
} else {
// Schedule another retry (retryCount already incremented in _handleConnectionFailure)
_scheduleRetry(relayUrl, relay, controller, 'retry failure');
}
}
});
} catch (e) {
Logger.error('Error during retry for relay $relayUrl', e);
// Handle synchronous exception
if (relay.isEnabled) {
if (relay.retryCount >= NostrRelay.maxRetryAttempts) {
relay.isEnabled = false;
relay.retryCount = 0;
Logger.warning('Relay $relayUrl disabled after ${NostrRelay.maxRetryAttempts} failed attempts');
} else {
// Schedule another retry (retryCount already incremented in _handleConnectionFailure)
_scheduleRetry(relayUrl, relay, controller, 'synchronous retry failure');
}
}
}
}
});
}
/// Disconnects from a relay. /// Disconnects from a relay.
/// ///
/// [relayUrl] - The URL of the relay to disconnect from. /// [relayUrl] - The URL of the relay to disconnect from.

@ -56,6 +56,7 @@ class RelayManagementController extends ChangeNotifier {
url: relay.url, url: relay.url,
isConnected: relay.isConnected, isConnected: relay.isConnected,
isEnabled: relay.isEnabled, isEnabled: relay.isEnabled,
retryCount: relay.retryCount,
)).toList(); )).toList();
notifyListeners(); notifyListeners();
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.