Skip to content

Commit 2f496e8

Browse files
committed
fix(nostr): temporarily disable relays after repeated connection failures
Why: Relays that consistently fail were causing endless reconnection loops What: - Add failureCycles tracking to disable relays after 3 complete retry cycles - Re-enable disabled relays after 5 minute timeout - Remove unreliable relays (relay.nostr.band, relay.current.fyi) from defaults
1 parent b5254fa commit 2f496e8

1 file changed

Lines changed: 50 additions & 12 deletions

File tree

src/services/nostr-sync.js

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,7 @@ export const NOSTR_KINDS = {
9090
export const DEFAULT_RELAYS = [
9191
'wss://relay.damus.io',
9292
'wss://nos.lol',
93-
'wss://relay.nostr.band',
9493
'wss://nostr-pub.wellorder.net',
95-
'wss://relay.current.fyi',
9694
]
9795

9896
// Connection states
@@ -109,8 +107,10 @@ const RETRY_CONFIG = {
109107
baseDelay: 1000, // 1 second base delay
110108
maxDelay: 30000, // 30 second max delay
111109
backoffFactor: 2, // Double delay each retry
112-
maxRetries: 6, // Max 6 retries before giving up
110+
maxRetries: 5, // Max 5 retries before giving up on this connection cycle
113111
jitterFactor: 0.1, // 10% jitter to prevent thundering herd
112+
maxFailureCycles: 3, // After 3 complete failure cycles, disable relay temporarily
113+
disabledRelayTimeout: 300000, // Re-enable disabled relays after 5 minutes
114114
}
115115

116116
// ========================================================================
@@ -831,6 +831,7 @@ export class NostrSyncService {
831831
// State
832832
this.connections = new Map() // relay URL -> connection info
833833
this.subscriptions = new Map() // subscription ID -> subscription info
834+
this.disabledRelays = new Map() // relay URL -> timestamp when disabled
834835
this.eventQueue = [] // Queued events for when connections are restored
835836
this.isInitialized = false
836837
this.nostrKeypair = null
@@ -1553,28 +1554,50 @@ export class NostrSyncService {
15531554
// Private methods
15541555

15551556
async _connectToRelay(relayUrl) {
1556-
if (this.connections.has(relayUrl)) {
1557-
const conn = this.connections.get(relayUrl)
1558-
if (conn.state === CONNECTION_STATES.CONNECTED ||
1559-
conn.state === CONNECTION_STATES.CONNECTING) {
1557+
// Check if relay is temporarily disabled
1558+
if (this.disabledRelays && this.disabledRelays.has(relayUrl)) {
1559+
const disabledAt = this.disabledRelays.get(relayUrl)
1560+
if (Date.now() - disabledAt < RETRY_CONFIG.disabledRelayTimeout) {
1561+
this._log(`Relay ${relayUrl} is temporarily disabled, skipping`)
1562+
return
1563+
}
1564+
// Re-enable relay after timeout
1565+
this.disabledRelays.delete(relayUrl)
1566+
this._log(`Re-enabling relay ${relayUrl} after timeout`)
1567+
}
1568+
1569+
const existingConn = this.connections.get(relayUrl)
1570+
if (existingConn) {
1571+
if (existingConn.state === CONNECTION_STATES.CONNECTED ||
1572+
existingConn.state === CONNECTION_STATES.CONNECTING) {
15601573
return // Already connected or connecting
15611574
}
15621575
}
15631576

15641577
this._log(`Connecting to relay: ${relayUrl}`)
15651578

1566-
const connection = {
1579+
// Preserve retryCount and failureCycles if reconnecting, otherwise create new connection
1580+
const connection = existingConn ? {
1581+
...existingConn,
1582+
ws: null,
1583+
state: CONNECTION_STATES.CONNECTING,
1584+
connectedAt: null,
1585+
lastError: null
1586+
// retryCount and failureCycles preserved from existingConn
1587+
} : {
15671588
url: relayUrl,
15681589
ws: null,
15691590
state: CONNECTION_STATES.CONNECTING,
15701591
retryCount: 0,
1592+
failureCycles: 0,
15711593
retryTimeout: null,
15721594
connectedAt: null,
15731595
lastError: null
15741596
}
15751597

15761598
this.connections.set(relayUrl, connection)
1577-
this._notifyConnectionChange(relayUrl, CONNECTION_STATES.DISCONNECTED, CONNECTION_STATES.CONNECTING)
1599+
const oldState = existingConn ? existingConn.state : CONNECTION_STATES.DISCONNECTED
1600+
this._notifyConnectionChange(relayUrl, oldState, CONNECTION_STATES.CONNECTING)
15781601

15791602
try {
15801603
const ws = new WebSocket(relayUrl)
@@ -1623,6 +1646,7 @@ export class NostrSyncService {
16231646
connection.state = CONNECTION_STATES.CONNECTED
16241647
connection.connectedAt = Date.now()
16251648
connection.retryCount = 0 // Reset retry count on successful connection
1649+
connection.failureCycles = 0 // Reset failure cycles on successful connection
16261650
connection.lastError = null
16271651

16281652
this._log(`Connected to relay: ${relayUrl}`)
@@ -1745,8 +1769,22 @@ export class NostrSyncService {
17451769
connection.retryCount++
17461770

17471771
if (connection.retryCount > RETRY_CONFIG.maxRetries) {
1748-
this._log(`Max retries exceeded for ${relayUrl}, giving up`)
1749-
return
1772+
// Completed a full retry cycle without success
1773+
connection.failureCycles = (connection.failureCycles || 0) + 1
1774+
connection.retryCount = 0 // Reset for next cycle
1775+
1776+
if (connection.failureCycles >= RETRY_CONFIG.maxFailureCycles) {
1777+
this._log(`Relay ${relayUrl} failed ${connection.failureCycles} cycles, temporarily disabling`)
1778+
// Initialize disabledRelays map if needed
1779+
if (!this.disabledRelays) {
1780+
this.disabledRelays = new Map()
1781+
}
1782+
this.disabledRelays.set(relayUrl, Date.now())
1783+
connection.state = CONNECTION_STATES.DISCONNECTED
1784+
return
1785+
}
1786+
1787+
this._log(`Max retries exceeded for ${relayUrl}, starting cycle ${connection.failureCycles + 1}`)
17501788
}
17511789

17521790
// Calculate delay with exponential backoff and jitter
@@ -1758,7 +1796,7 @@ export class NostrSyncService {
17581796
const jitter = baseDelay * RETRY_CONFIG.jitterFactor * (Math.random() - 0.5)
17591797
const delay = Math.max(baseDelay + jitter, 100) // Minimum 100ms delay
17601798

1761-
this._log(`Scheduling reconnect to ${relayUrl} in ${Math.round(delay)}ms (attempt ${connection.retryCount})`)
1799+
this._log(`Scheduling reconnect to ${relayUrl} in ${Math.round(delay)}ms (attempt ${connection.retryCount}, cycle ${(connection.failureCycles || 0) + 1})`)
17621800

17631801
connection.retryTimeout = setTimeout(async () => {
17641802
connection.state = CONNECTION_STATES.RECONNECTING

0 commit comments

Comments
 (0)