package workflow_builder import ( "encoding/json" "fmt" "strconv" "sync" "cloud.o-forge.io/core/oc-lib/logs" "cloud.o-forge.io/core/oc-lib/tools" ) // considersCache stocke les canaux en attente d'un PB_CONSIDERS, // indexés par "executionsId:dataType". Un même message NATS réveille // tous les waiters enregistrés sous la même clé (broadcast). type considersCache struct { mu sync.Mutex pending map[string][]chan struct{} } var globalConsidersCache = &considersCache{ pending: make(map[string][]chan struct{}), } // considersKey construit la clé du cache à partir de l'ID d'exécution, // du type de données et du peer compute (SourcePeerID). // peerID permet de différencier plusieurs waiters COMPUTE_RESOURCE du même // executionsId (1 local + N distants en parallèle). func considersKey(executionsId string, dataType tools.DataType, peerID string) string { key := executionsId + ":" + strconv.Itoa(dataType.EnumIndex()) if peerID != "" { key += ":" + peerID } return key } // register inscrit un nouveau canal d'attente pour la clé donnée. // Retourne le canal à lire et une fonction de désinscription à appeler en defer. func (c *considersCache) register(key string) (<-chan struct{}, func()) { ch := make(chan struct{}, 1) c.mu.Lock() c.pending[key] = append(c.pending[key], ch) c.mu.Unlock() unregister := func() { c.mu.Lock() defer c.mu.Unlock() list := c.pending[key] for i, existing := range list { if existing == ch { c.pending[key] = append(list[:i], list[i+1:]...) break } } if len(c.pending[key]) == 0 { delete(c.pending, key) } } return ch, unregister } // confirm réveille tous les waiters enregistrés sous la clé donnée // et les supprime du cache. func (c *considersCache) confirm(key string) { c.mu.Lock() list := c.pending[key] delete(c.pending, key) c.mu.Unlock() for _, ch := range list { select { case ch <- struct{}{}: default: } } } // StartConsidersListener démarre un abonné NATS global via ListenNats (oclib) // qui reçoit les messages CONSIDERS_EVENT et réveille les goroutines en attente // via globalConsidersCache. Doit être appelé une seule fois au démarrage. func StartConsidersListener() { log := logs.GetLogger() log.Info().Msg("Considers NATS listener starting on " + tools.CONSIDERS_EVENT.GenerateKey()) go tools.NewNATSCaller().ListenNats(map[tools.NATSMethod]func(tools.NATSResponse){ tools.CONSIDERS_EVENT: func(resp tools.NATSResponse) { fmt.Println("CONSIDERS") var body struct { ExecutionsID string `json:"executions_id"` PeerID string `json:"peer_id,omitempty"` } if err := json.Unmarshal(resp.Payload, &body); err != nil { log.Error().Msg("CONSIDERS_EVENT: cannot unmarshal payload: " + err.Error()) return } key := considersKey(body.ExecutionsID, resp.Datatype, body.PeerID) log.Info().Msg(fmt.Sprintf("CONSIDERS_EVENT dispatched for key=%s", key)) globalConsidersCache.confirm(key) }, }) }