fix(realtime): les mises à jour live SURVIVENT aux redéploiements du hub (reconnexion + resync SSE)
Cause (diagnostic) : docker restart du hub à chaque déploiement détruit tous les clients SSE en mémoire. Les flux courriels/ cloche utilisaient un EventSource brut SANS onerror/reconnexion → le 502 pendant le restart les met CLOSED définitivement (plus de reconnexion native) → morts jusqu'au rechargement manuel. Le dispatch se reconnectait mais ne resynchronisait jamais. Fix (client) : - useSSE : nouvelle option onReconnect() appelée à chaque RE-connexion (pas au 1er open) → resync après coupure. - useNotifications (cloche) : reconnexion auto si le flux meurt (CLOSED → relance 3 s). Côté co-édité déployé via bundle (non commité ici) : useConversations (inbox) = reconnexion + resync fetchList + repli poll 25 s sur les 3 connexions SSE ; PlanificationPage = onReconnect → reloadOccupancy+reloadPool ; server.js = event 'ready' + retry:3000. Hub 'ready' vérifié live. Reste (durabilité, différé) : persister _watchSnap pour rejouer les deltas Legacy manqués pendant la coupure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
daeb86008b
commit
187115cb56
|
|
@ -109,6 +109,9 @@ function onCallIncoming (d) {
|
|||
function initNotifications () {
|
||||
loadPrefs(); loadMyQueues()
|
||||
if (es) return
|
||||
connectNotifSSE()
|
||||
}
|
||||
function connectNotifSSE () {
|
||||
try {
|
||||
es = new EventSource(`${HUB_URL}/sse?topics=outbox,conversations`)
|
||||
es.addEventListener('rating', e => { try { onRating(JSON.parse(e.data)) } catch {} })
|
||||
|
|
@ -116,6 +119,9 @@ function initNotifications () {
|
|||
es.addEventListener('conv-message', e => { try { onConvMessage(JSON.parse(e.data)) } catch {} })
|
||||
es.addEventListener('conv-email', e => { try { onConvEmail(JSON.parse(e.data)) } catch {} })
|
||||
es.addEventListener('call-incoming', e => { try { onCallIncoming(JSON.parse(e.data)) } catch {} })
|
||||
// Survit aux redéploiements du hub : si le flux meurt DÉFINITIVEMENT (502 pendant le restart → EventSource CLOSED,
|
||||
// plus de reconnexion native), on relance après 3 s (sinon la cloche reste muette jusqu'au prochain rechargement).
|
||||
es.onerror = () => { if (es && es.readyState === EventSource.CLOSED) { es = null; setTimeout(connectNotifSSE, 3000) } }
|
||||
} catch (e) { /* SSE indisponible */ }
|
||||
}
|
||||
function markAllRead () { unread.value = 0 }
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export function useSSE (opts = {}) {
|
|||
let es = null
|
||||
let reconnectTimer = null
|
||||
let reconnectDelay = 1000 // start at 1s, exponential backoff
|
||||
let opened = false // pour distinguer 1re connexion vs RECONNEXION (→ resync)
|
||||
|
||||
function connect (topics) {
|
||||
disconnect()
|
||||
|
|
@ -30,6 +31,10 @@ export function useSSE (opts = {}) {
|
|||
es.onopen = () => {
|
||||
connected.value = true
|
||||
reconnectDelay = 1000 // reset backoff on successful connect
|
||||
// RESYNC : à toute RE-connexion (après une coupure — ex. redéploiement du hub), on rattrape ce qui a changé pendant
|
||||
// l'absence en refetchant l'état (le hub ne rejoue pas les deltas manqués). Pas au tout 1er open (le consommateur charge au mount).
|
||||
if (opened && opts.onReconnect) { try { opts.onReconnect() } catch {} }
|
||||
opened = true
|
||||
}
|
||||
|
||||
es.addEventListener('message', (e) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user