feat: v6.4.0 Realtime production — merge 3-voix (au-delà du LWW) + broadcast non bloquant
FlowDeck CI / lint (push) Successful in 1m25s
FlowDeck CI / test (push) Successful in 10m20s
FlowDeck CI / docker (push) Successful in 1m21s

- app/services/realtime_merge.py : merge à 3 voix diff3-lite, regions disjointes conservees, conflit par champ + drapeau
- protocole base (client embarque la base de sa saisie) ; sans base -> LWW historique (retro-compat)
- ack renvoie le bloc fusionne + conflict ; adoption cote client + toast ; broadcast du resultat fusionne
- broadcast non bloquant : file sortante + tache writer par connexion, coalescence des curseurs
- clients trop lents deconnectes (4413), budget ops anti-flood (400/10s)
- fix fuite room 4404 + room_state() sur page inexistante
- GET /api/realtime/stats (observabilite)
- 26 tests test_realtime_v64.py ; suite 725 verte ; ruff + eslint OK ; version 6.4.0
This commit is contained in:
2026-09-23 23:55:39 -04:00
parent f2e5684e4e
commit f2f2f3209e
11 changed files with 1017 additions and 85 deletions
+37 -2
View File
@@ -814,6 +814,41 @@ Détails livrés :
- [x] `tests/test_public_api_v2.py` — **24 tests** (auth scopes, pagination, filtres, RFC 7807, idempotency, webhooks deliveries, CRUD multi-domaines)
- [x] Vérif `ruff check app tests` + `pytest -n auto` → **668 verte**
## v6.4.0 — Realtime editing (production) ✅ (2026-09-22)
> **Objectif** : passer le realtime v5.13.0 en « production » — résolution de
> conflits au-delà du last-write-wins + édition à grande échelle (broadcast non
> bloquant, plusieurs rooms/pages, observabilité). **COMPLETED**.
#### Conflits au-delà du LWW
- [x] **Merge à 3 voix (diff3-lite)** — nouveau service `app/services/realtime_merge.py` : `merge_text_3way()` (merge de caractères) + `merge_block_3way()` (merge champ-par-champ), fonctions pures et testées sans WebSocket ni base
- [x] **Régions disjointes conservées** — deux utilisateurs tapant à des endroits différents du *même* bloc voient leurs deux saisies survivre (au lieu d'écraser l'une par l'autre) ; ordre d'arrivée indifférent
- [x] **Chevauchement réel → LWW par champ + drapeau** — un conflit n'est plus « tout le bloc perdu » mais limité au champ concerné ; `conflict: true` renvoyé dans l'`ack` et le broadcast
- [x] **Protocole `base`** — le client embarque dans chaque `update` la version du bloc dont dérive sa saisie ; le serveur fait `merge_block_3way(base, current, incoming)` ; sans `base` → LWW historique (rétro-compat ancien client)
- [x] **Adoption côté client** — l'`ack` renvoie le bloc fusionné ; le client met à jour sa `base`, adopte le résultat (hors bloc en cours d'édition) et affiche un toast en cas de conflit
- [x] **Broadcast du résultat fusionné** — le serveur diffuse toujours le bloc final fusionné (jamais la proposition brute) pour convergence garantie de tous les clients
#### Échelle & robustesse
- [x] **Broadcast non bloquant** — chaque connexion a une file sortante (`asyncio.Queue`) + une tâche `_writer` dédiée ; `_broadcast()` fait `put_nowait` et n'attend plus le socket → un client lent ne fige plus la room
- [x] **Coalescence des curseurs** — le writer réduit les messages `sel` empilés à la position la plus récente (seule la dernière compte), tout en préservant l'ordre des messages importants
- [x] **Déconnexion des clients trop lents** — file pleine (`MAX_OUT_QUEUE=512`) → fermeture 4413 + compteur `slow_disconnects` (évite qu'une room entière stagne sur un pair mortel)
- [x] **Anti-flood** — budget d'opérations par connexion (`OP_WINDOW_MAX=400` / 10 s) ; au-delà, réponse `ack stale` sans application
- [x] **Fix fuite de rooms** — une connexion 4404 n'enregistre plus de `Room` orpheline en mémoire ; `room_state()` sur page inexistante retourne un dict vide au lieu de planter sur `None`
- [x] **Observabilité** — `GET /api/realtime/stats` (authentifié) : rooms, connexions, ops, merges, conflits, déconnexions lentes + détail par page
Détails livrés :
- `app/services/realtime_merge.py` — merge 3-voix (nouveau, ~170 lignes)
- `app/services/realtime_server.py` — `RTConn` (file + writer + budget), `_apply()` avec merge, `_evict_slow()`, `stats()`, fix fuites
- `app/routers/realtime.py` — endpoint `GET /api/realtime/stats`
- `app/templates/_page_editor_realtime.html` — envoi de `base`, adoption du bloc fusionné, toast de conflit
- **26 tests** `tests/test_realtime_v64.py` (merge purs, protocole WS, convergence 2 clients, rétro-compat LWW, fuite 4404, coalescence, stats, anti-flood) ; **14 tests** `tests/test_realtime.py` préservés
- `ruff check app tests` OK · `eslint static/js` 0 problème
- **Version** — 6.4.0
---
## v6.0.0 — Pro (futur)
- [x] **PWA** — Progressive Web App, offline support ✅ (livré) — [📄 Conception détaillée](/docs/V6_PWA_Progressive_Web_App.md)
@@ -821,7 +856,7 @@ Détails livrés :
- [x] **Web Clipper** — extension navigateur ✅ (livré v6.2.0/6.2.1) — [📄 Conception détaillée](/docs/V6_Web_Clipper.md)
- [x] **API publique complète** — REST API documentée (OpenAPI) ✅ (livré v6.3.0) — [📄 API Guide v2](/docs/API_GUIDE_V6.md) · [📄 OpenAPI](/docs/openapi-v2.json)
- [ ] **SSO/SAML** — enterprise authentication — [📄 Conception détaillée](/docs/V6_SSO_SAML_Enterprise_Auth.md)
- [ ] **Realtime editing (production)** — voir **v5.13.0** (curseurs + présence déjà avancés ici) ; reste en v6 : conflits avancés, édition large échelle
- [x] **Realtime editing (production)** ✅ livré **v6.4.0** (merge 3-voix au-delà du LWW, broadcast non bloquant) ; voir **v5.13.0** pour le socle (curseurs + présence)
- [ ] **Synced blocks (production)** — voir **v5.14.0** (bloc de base) ; reste en v6 : syncing côté databases/vues
---
@@ -868,4 +903,4 @@ Quality DB views, Agent IA Palette → Realtime + E
DB avancée, redo, drag&drop, bookmark, avancée
Calendrier, AI duplicate) lightbox…) (Pt.2) v6.1 ✅ v6.2 ✅ v6.3 ✅
*Dernière mise à jour: 2026-09-21 — **v6.3.0 API publique complète v2 COMPLETED** (~100 endpoints `/api/v2`, scopes hiérarchiques `read<write<admin`, RFC 7807, idempotence, audit, webhooks CRUD, OpenAPI 402 chemins, 24 tests, suite 668 verte) + **v6.2.1 Web Clipper COMPLETED**. Reste: SSO/SAML, realtime production, synced blocks prod, webhooks HMAC/retry.*
*Dernière mise à jour: 2026-09-22 — **v6.4.0 Realtime editing (production) COMPLETED** (merge 3-voix au-delà du LWW via `realtime_merge.py`, broadcast non bloquant avec file sortante + writer, coalescence des curseurs, fix fuite de rooms 4404, anti-flood, `GET /api/realtime/stats`, 26 tests dédiés) + **v6.3.0 API publique complète v2 COMPLETED** (~100 endpoints `/api/v2`, scopes hiérarchiques `read<write<admin`, RFC 7807, idempotence, audit, webhooks CRUD, OpenAPI 402 chemins). Reste: SSO/SAML, synced blocks prod (databases/vues).*