@startuml title Native Indexer — Boucles background (offload, DHT refresh, GC streams) participant "Indexer A (enregistré)" as IndexerA participant "Indexer B (enregistré)" as IndexerB participant "Native Indexer" as Native participant "DHT Kademlia" as DHT participant "Node A (responsible peer)" as NodeA note over Native: runOffloadLoop — toutes les 30s loop Toutes les 30s Native -> Native: len(responsiblePeers) > 0 ? note over Native: responsiblePeers = peers pour lesquels\nle native a fait selfDelegate (aucun indexer dispo) alt Des responsible peers existent (ex: Node A) Native -> Native: reachableLiveIndexers() note over Native: Filtre liveIndexers par TTL\nping PeerIsAlive pour chaque candidat alt Indexers A et B maintenant joignables Native -> Native: responsiblePeers = {} (libère Node A et autres) note over Native: Node A se reconnectera\nau prochain ConnectToNatives else Toujours aucun indexer note over Native: Node A reste sous la responsabilité du native end end end note over Native: refreshIndexersFromDHT — toutes les 30s loop Toutes les 30s Native -> Native: Collecter tous les knownPeerIDs\n= {PeerID_A, PeerID_B, ...} loop Pour chaque PeerID connu Native -> Native: liveIndexers[PeerID] encore frais ? alt Entrée manquante ou expirée Native -> DHT: SearchValue(ctx 5s, "/indexer/"+PeerID) DHT --> Native: channel de bytes loop Pour chaque résultat DHT Native -> Native: Unmarshal → liveIndexerEntry Native -> Native: Garder le meilleur (ExpiresAt le plus récent, valide) end Native -> Native: liveIndexers[PeerID] = best entry note over Native: "native: refreshed indexer from DHT" end end end note over Native: LongLivedStreamRecordedService GC — toutes les 30s loop Toutes les 30s Native -> Native: gc() — lock StreamRecords[Heartbeat] loop Pour chaque StreamRecord (Indexer A, B, ...) Native -> Native: now > rec.Expiry ?\nOU timeSince(LastSeen) > 2×TTL restant ? alt Pair périmé (ex: Indexer B disparu) Native -> Native: Supprimer Indexer B de TOUS les maps de protocoles note over Native: Stream heartbeat fermé\nliveIndexers[PeerID_B] expirera naturellement end end end note over IndexerA: Indexer A continue à heartbeater normalement\net reste dans StreamRecords + liveIndexers @enduml