141 lines
5.8 KiB
Go
141 lines
5.8 KiB
Go
# NATS dans oc-scheduler
|
|
|
|
## Vue d'ensemble
|
|
|
|
`oc-scheduler` utilise NATS comme bus d'événements pour deux objectifs :
|
|
|
|
1. **Recevoir les planners** (disponibilité des ressources) publiés par `oc-discovery`.
|
|
2. **Réagir aux modifications de workflows** pour diffuser un planner actualisé et signaler les streams WebSocket actifs.
|
|
|
|
Tout le code NATS se trouve dans `infrastructure/nats.go`.
|
|
|
|
---
|
|
|
|
## Canaux écoutés
|
|
|
|
### `PROPALGATION_EVENT` — réception des planners
|
|
|
|
**Condition d'acceptation :** `resp.FromApp == "oc-discovery"` et `prop.Action == PB_PLANNER`.
|
|
|
|
**Ce qui se passe :**
|
|
- Le payload est désérialisé en `planner.Planner`.
|
|
- Le champ `peer_id` est extrait pour identifier le pair.
|
|
- Le planner est stocké dans `PlannerCache[peerID]` via `storePlanner()`.
|
|
- Si c'est la **première apparition** de ce `peerID` dans le cache, une goroutine de TTL est lancée (voir §TTL ci-dessous).
|
|
- Tous les abonnés en attente d'un changement sur ce `peerID` sont notifiés.
|
|
|
|
### `CREATE_RESOURCE` — modification d'un workflow
|
|
|
|
**Condition d'acceptation :** `resp.Datatype == WORKFLOW`.
|
|
|
|
**Ce qui se passe :**
|
|
1. Le payload est désérialisé en `workflow.Workflow`.
|
|
2. `broadcastPlanner(wf)` est appelé : pour chaque pair (storage + compute) du workflow dont le planner **n'est pas encore en cache**, un événement `PB_PLANNER` est émis sur NATS afin de demander un planner frais à `oc-discovery`.
|
|
3. `notifyWorkflowWatchers(wf.GetID())` est appelé : tous les streams WebSocket qui observent ce workflow sont signalés pour **rafraîchir leur liste de pairs surveillés**.
|
|
|
|
---
|
|
|
|
## Canaux émis
|
|
|
|
### `PROPALGATION_EVENT` — deux actions possibles
|
|
|
|
| Action | Déclencheur | Effet attendu |
|
|
|---|---|---|
|
|
| `PB_PLANNER` | Workflow modifié, pair inconnu du cache | `oc-discovery` renvoie le planner du pair |
|
|
| `PB_CLOSE_PLANNER` | TTL expiré **ou** déconnexion WebSocket | Les consommateurs (oc-discovery, autres schedulers) libèrent leur état pour ce pair |
|
|
|
|
---
|
|
|
|
## Cache des planners (`PlannerCache`)
|
|
|
|
```
|
|
PlannerCache : map[string]*planner.Planner // clé = peerID
|
|
plannerAddedAt : map[string]time.Time // horodatage de première insertion
|
|
```
|
|
|
|
- Protégé par `plannerMu` (RWMutex).
|
|
- Alimenté uniquement via `storePlanner()` (appelé par le listener NATS).
|
|
- Supprimé via `EmitNATS(peerID, PB_CLOSE_PLANNER)`, qui efface l'entrée **et** notifie les abonnés.
|
|
|
|
### TTL de 24 heures
|
|
|
|
À la **première** insertion d'un `peerID`, une goroutine est lancée :
|
|
|
|
```
|
|
sleep(24h)
|
|
→ si l'entrée existe encore : EmitNATS(peerID, PB_CLOSE_PLANNER)
|
|
```
|
|
|
|
Cela évite que des planners obsolètes stagnent indéfiniment. L'entrée est supprimée et les streams actifs reçoivent une notification « plus de planner » pour ce pair.
|
|
|
|
---
|
|
|
|
## Pub/sub interne
|
|
|
|
Un registre d'abonnements en mémoire permet à d'autres composants (notamment le controller WebSocket) de réagir aux événements sans coupler directement le code NATS et les goroutines HTTP.
|
|
|
|
Deux registres distincts :
|
|
|
|
| Registre | Clé | Signification |
|
|
|---|---|---|
|
|
| `plannerSubs` | `peerID` | « le planner de ce pair a changé » |
|
|
| `workflowSubs` | `workflowID` | « ce workflow a été modifié » |
|
|
|
|
### API
|
|
|
|
```go
|
|
// S'abonner aux changements de planners pour plusieurs pairs
|
|
ch, cancel := SubscribePlannerUpdates(peerIDs []string)
|
|
|
|
// S'abonner aux modifications d'un workflow
|
|
ch, cancel := SubscribeWorkflowUpdates(wfID string)
|
|
```
|
|
|
|
Chaque canal est bufférisé (`capacity 1`) : si un signal est déjà en attente, les suivants sont ignorés sans bloquer.
|
|
|
|
---
|
|
|
|
## Intégration avec le stream WebSocket (`GET /oc/:id/check`)
|
|
|
|
Le handler `CheckStream` dans `controllers/workflow_sheduler.go` exploite ces mécanismes :
|
|
|
|
1. **Ouverture** : résolution des `peerIDs` du workflow, abonnement à `SubscribePlannerUpdates` et `SubscribeWorkflowUpdates`.
|
|
2. **Boucle de streaming** :
|
|
- `plannerCh` reçoit un signal → re-calcul du `CheckResult` et envoi au client.
|
|
- `wfCh` reçoit un signal (workflow modifié) → recalcul des `peerIDs`, désabonnement + ré-abonnement aux nouveaux pairs, re-calcul et envoi.
|
|
3. **Fermeture** (déconnexion client) :
|
|
- Désabonnement des deux registres.
|
|
- `EmitNATS(peerID, PB_CLOSE_PLANNER)` pour **chaque pair surveillé** : le cache est purgé et `oc-discovery` est informé que le scheduler n'a plus besoin du planner.
|
|
|
|
---
|
|
|
|
## Flux de données résumé
|
|
|
|
```
|
|
oc-discovery ──PROPALGATION_EVENT(PB_PLANNER)──► ListenNATS
|
|
│
|
|
storePlanner()
|
|
PlannerCache[peerID] = planner
|
|
notifyPlannerWatchers(peerID)
|
|
│
|
|
SubscribePlannerUpdates
|
|
│
|
|
CheckStream (WS) ──► client
|
|
|
|
Workflow modifié ──CREATE_RESOURCE(WORKFLOW)──► ListenNATS
|
|
│
|
|
broadcastPlanner(wf)
|
|
PROPALGATION_EVENT(PB_PLANNER) → oc-discovery
|
|
notifyWorkflowWatchers(wfID)
|
|
│
|
|
SubscribeWorkflowUpdates
|
|
│
|
|
CheckStream refresh peerIDs ──► client
|
|
|
|
TTL 24h / déconnexion WS ──► EmitNATS(PB_CLOSE_PLANNER)
|
|
│
|
|
delete PlannerCache[peerID]
|
|
notifyPlannerWatchers(peerID)
|
|
PROPALGATION_EVENT(PB_CLOSE_PLANNER) → NATS bus
|
|
```
|