@startuml 25_failure_node_gc title F7 — Crash nœud → GC indexeur + AfterDelete participant "Node\\n(crashé)" as N participant "Indexer A" as IA participant "Indexer B" as IB participant "Native A" as NA note over N, NA: État nominal : N heartbeatait vers IA et IB == Crash Node == N ->x IA: stream reset (heartbeat coupé) N ->x IB: stream reset (heartbeat coupé) == GC côté Indexer A == note over IA: HandleHeartbeat : stream reset détecté\\nStreamRecords[ProtocolHB][N].LastSeen figé loop ticker GC (30s) — StartGC(30*time.Second) IA -> IA: gc()\\nnow.After(Expiry) où Expiry = lastHBTime + 2min\\n→ si 2min sans heartbeat → éviction IA -> IA: delete(StreamRecords[ProtocolHB][N])\\nAfterDelete(N, name, did) appelé hors lock note over IA: N retiré du registre vivant.\\nFillRate recalculé (n-1 / maxNodes). end == Impact sur le scoring / fill rate == note over IA: FillRate diminue\\nProchain subscribe vers NA inclura FillRate mis à jour IA -> NA: /opencloud/native/subscribe/1.0\\nIndexerRegistration{FillRate: 0.3} /' était 0.5 '/ NA -> NA: liveIndexerEntry[IA].FillRate = 0.3\\nPriorité de routage recalculée : w(0.3) = 0.21 == Impact sur la Phase 2 (indexerLivenessVote) == note over IA: Si un autre nœud demande consensus,\\nN n'est plus dans StreamRecords.\\nN absent de la réponse Alive[]. note over IB: Même GC effectué côté IB.\\nN retiré de StreamRecords[ProtocolHB]. == Reconnexion éventuelle du nœud == N -> N: redémarrage N -> IA: SendHeartbeat /opencloud/heartbeat/1.0\\nHeartbeat{Score: X, IndexersBinded: 2} IA -> IA: HandleHeartbeat → nouveau UptimeTracker(FirstSeen=now)\\nStreamRecords[ProtocolHB][N] recréé note over IA: N de retour avec FirstSeen frais.\\ndynamicMinScore élevé tant que age < 24h. @enduml