Premiere commit - Omika

This commit is contained in:
2026-10-23 18:46:50 +02:00
commit 8ea76c1b24
2155 changed files with 984301 additions and 0 deletions
+300
View File
@@ -0,0 +1,300 @@
# LOT SERVEUR — Worker de Synchronisation (Omika.Worker)
## 1. Décision de conception
Le projet **`src/Omika.Worker/`** est le **nom officiel** du service de
synchronisation serveur dans cette solution. Il est **réutilisé tel quel** :
aucun nouveau projet `Omika.Sync.Worker` n'est créé, aucun renommage, aucun
déplacement, aucune duplication de `SyncProcessor`, `SyncWorker` ou des
handlers.
Le projet existant satisfait déjà le rôle attendu d'un .NET Worker Service
séparé de `Omika.Api`.
## 2. Rôle
`Omika.Worker` est un **BackgroundService** qui traite les opérations de
synchronisation mobile reçues par l'infrastructure serveur. Il est **séparé**
de l'API : l'API reçoit les opérations et les écrit dans la file de transfert
(`TOSRV`) ; le Worker les transfère vers la file de réception (`MobReception`),
les applique via les handlers métier, et journalise le résultat dans `MobLog`.
## 3. Architecture
```
API (SyncOperationService.ReceiveAsync)
→ InsertToSrvAsync() → écrit dans TOSRV
Worker (SyncWorker → SyncProcessor.ProcessAsync)
→ TransferToReceptionAsync() → TOSRV → MobReception
→ GetPendingOperationsAsync() → lit MobReception
→ Handler (Intervention / Default) → applique l'opération
→ MarkSuccessAsync / MarkRejectedAsync / IncrementRetryAsync → MobLog
```
### Chaîne TOSRV → MobReception
Le transfert **TOSRV → MobReception** est **déjà géré** par
`SyncTransferRepository.TransferToReceptionAsync()`, appelé par
`SyncProcessor.ProcessAsync()` (première étape de chaque cycle). **Aucun
chaînon manquant** : la chaîne est complète.
- **Qui écrit dans TOSRV** : `SyncOperationService.ReceiveAsync()` (côté API)
via `ISyncTransferRepository.InsertToSrvAsync()`.
- **Ce que lit SyncProcessor** : les opérations en attente dans `MobReception`
via `GetPendingOperationsAsync()`.
- **MobReception n'est jamais vide** : il est alimenté par le transfert
TOSRV → MobReception à chaque cycle.
## 4. Program.cs / DI
`Program.cs` enregistre :
| Service | Lifetime | Connexion |
|---|---|---|
| `AddOmikaData()` | — | Business (ODBC métier) + WebService |
| `AddOmikaDataServiceConnection()` | — | **Service** (OLEDB, TOSRV) |
| `SyncOptions` (section `Sync`) | Options | — |
| `DefaultSyncOperationHandler` | Scoped | — |
| `IMobileEntityIdentityResolver` | Singleton | — |
| `InterventionSyncOperationHandler` | Scoped | Business |
| `SyncOperationHandlerRegistry` | Scoped | — |
| `SyncProcessor` | Scoped | — |
| `SyncWorker` (HostedService) | Singleton | — |
### Topologie multi-connexion
Le `ISyncTransferRepository` est désormais **multi-connexion** : il reçoit deux
connexions explicites via le DI.
- `IServiceHfSqlConnection` (enregistrée par `AddOmikaDataServiceConnection()`) :
base Service, table `TOSRV`.
- `IWebServiceHfSqlConnection` (enregistrée par `AddOmikaData()`) : base
WebService/Business, tables `MobReception` et `MobLog`.
Le Worker reçoit via DI le repository multi-connexion. Il ne recrée **aucune**
logique TOSRV/MobReception : il délègue entièrement à `SyncProcessor` →
`ISyncTransferRepository`.
## 5. Connexion Service / WebService
La connexion **Service** est configurée dans la section `ServiceConnection`
(`ServiceConnectionOptions`), via le provider OLEDB `PCSOFT.HFSQL`. Elle est
enregistrée comme `IHfsqlConnection` nommée `"Service"` et exposée via le
wrapper `IServiceHfSqlConnection`. Elle héberge la table `TOSRV`.
La connexion **WebService/Business** est la connexion métier ODBC par défaut
(`HfsqlOptions`), exposée via le wrapper `IWebServiceHfSqlConnection`. Elle
héberge les tables `MobReception` et `MobLog` ainsi que les données métier.
`ISyncTransferRepository` est branché sur ces **deux** connexions explicites.
## 6. Connexion Business
La connexion **Business** (base métier Batik/Omika) est configurée dans la
section `Hfsql` (`HfsqlOptions`), via le driver ODBC HFSQL. Les services métier
(`IInterventionService`, etc.) et les repositories adaptatifs Batik/Omika
restent sur cette connexion.
## 7. Rôle TOSRV
`TOSRV` est la file de transfert **mobile → serveur**. L'API y écrit les
opérations reçues via `InsertToSrvAsync()`. Le Worker les transfère ensuite
vers `MobReception`.
## 8. Rôle MobReception
`MobReception` est la file de réception **serveur**. `SyncProcessor` y lit les
opérations en attente de traitement via `GetPendingOperationsAsync()`.
## 9. Rôle MobLog
`MobLog` est le journal terminal de synchronisation. `SyncProcessor` y écrit
les résultats terminaux (`SUCCESS` / `REJECTED`) et les tentatives
(`IncrementRetryAsync`). Le Worker purge périodiquement les journaux terminaux
au-delà de la rétention configurée (`LogRetentionDays`).
## 10. SyncProcessor
`SyncProcessor` est la logique de traitement testable, réutilisée telle quelle.
`SyncWorker` (le BackgroundService) délègue entièrement à `SyncProcessor` :
il ne contient **aucun SQL métier**. Le BackgroundService gère uniquement la
boucle, le `CancellationToken`, l'intervalle de polling et la gestion des
erreurs de cycle.
## 11. SyncWorker
`SyncWorker` est le `BackgroundService` qui orchestre les cycles :
- boucle tant que le `CancellationToken` n'est pas annulé ;
- à chaque cycle : `SyncProcessor.ProcessAsync()` puis purge légère de `MobLog` ;
- délai entre deux cycles : `SyncOptions.RetryDelaySeconds` (configurable) ;
- arrêt propre sur annulation.
## 12. Polling
L'intervalle de polling est **configurable** via la section `Sync`
(`SyncOptions.RetryDelaySeconds`, défaut 120 s). Aucun refactoring nécessaire :
le Worker utilise déjà cette option comme intervalle de cycle.
## 13. CancellationToken
Le `CancellationToken` est passé à `SyncProcessor.ProcessAsync()` et à la purge.
Une annulation pendant le délai lève `OperationCanceledException`, capturée et
transformée en arrêt propre (`break`).
## 14. Gestion des erreurs
Une exception de cycle est capturée par `SyncWorker` (log d'erreur) et le cycle
suivant reprend normalement : **pas de boucle agressive**, le délai est
respecté entre les cycles.
## 15. Retries / max 30
`SyncOptions.MaxRetryCount` (défaut **30**) borne le nombre de tentatives.
`SyncProcessor` incrémente `RetryCount` à chaque échec transitoire
(`TRANSIENT_FAILURE`). Au-delà du max, l'opération est **REJECTED** (terminal).
Un échec définitif (`PERMANENT_FAILURE`) est rejeté immédiatement sans
consommer les 30 retries. **Aucun retry infini** n'est réintroduit.
## 16. OperationId
`OperationId` est conservé tout au long du cycle et sert à l'idempotence : un
doublon d'`OperationId` (déjà `SUCCESS` ou `REJECTED`) n'est pas réappliqué par
le handler.
## 17. RequestedEntityGuid / CanonicalEntityGuid
Le handler Intervention retourne `RequestedEntityGuid` (GUID mobile) et
`CanonicalEntityGuid` (GUID canonique stable) lorsqu'ils diffèrent. Le log
terminal reconstitué conserve le `CanonicalEntityGuid` d'origine pour
l'idempotence par `OperationId`. La réconciliation CREATE est gérée par le
handler (vérification d'existence avant recréation).
## 18. Handlers
Handlers actuellement enregistrés :
- **Intervention** (`InterventionSyncOperationHandler`) : CREATE, UPDATE,
ARCHIVE.
- **DELETE** : non supporté → `PERMANENT_FAILURE` (archivage logique uniquement).
- **Autres EntityTypes sans handler** : `DefaultSyncOperationHandler` →
`PERMANENT_FAILURE` / `NO_HANDLER` (jamais de faux SUCCESS).
Aucun nouveau handler n'est créé dans ce lot.
## 19. Concurrence / claim
**VERSION ACTUELLE : UN SEUL `Omika.Worker` actif par file Sync.** Le
repository/processor ne possède pas de mécanisme fiable de claim. Aucun verrou
HFSQL improvisé n'est créé. La concurrence multi-instance est documentée comme
limite restante (futur lot de déploiement).
## 20. Nombre de Workers autorisé actuellement
**Un seul** `Omika.Worker` actif par file Sync (pas de claim distribué).
## 21. Fonctionnement console
`Omika.Worker` est un exécutable .NET Worker Service : il fonctionne en
**console** via `dotnet run` / l'exécutable produit. Le `Host.CreateApplicationBuilder`
fournit le support console standard.
## 22. Support Windows Service
Le Host .NET standard (`Host.CreateApplicationBuilder`) est compatible avec
l'hébergement Windows Service via `UseWindowsService()`. Ce support n'est pas
encore activé dans `Program.cs` : il est documenté comme **futur lot de
déploiement** (aucune restructuration nécessaire pour l'ajouter).
## 23. Configuration / secrets
Le Worker dispose des sections nécessaires dans `appsettings.json` :
- `Hfsql` (Business / WebService) ;
- `ServiceConnection` (Service, table `TOSRV`) ;
- `Sync` (options : MaxRetryCount, RetryDelaySeconds, LogRetentionDays) ;
- `Omika` (DatabaseVersion) ;
- `Logging`.
Aucun secret n'est versionné : les valeurs de connexion sont des exemples non
sensibles, à surcharger par la configuration d'installation (variables
d'environnement / secrets). Aucune nouvelle convention de secrets n'est créée.
## 24. Fichiers réellement modifiés
- `src/Omika.Worker/Program.cs` : ajout de `AddOmikaDataServiceConnection()`
(connexion Service pour TOSRV) en plus de `AddOmikaData()` (WebService pour
MobReception/MobLog).
- `src/Omika.Worker/appsettings.json` : **créé** (configuration Worker).
- `tests/Omika.Tests/Mobile/SyncWorkerTests.cs` : **créé** (tests du
BackgroundService).
## 25. Tests ajoutés / modifiés
- **Créé** : `tests/Omika.Tests/Mobile/SyncWorkerTests.cs` (5 tests) :
1. Worker appelle SyncProcessor et s'arrête sur annulation ;
2. absence de travail ne crée pas de boucle CPU ;
3. exception de cycle ne provoque pas de boucle agressive ;
4. intervalle de polling configurable respecté ;
5. arrêt propre sur annulation.
Aucun test existant n'est dupliqué (`SyncProcessorTests`,
`InterventionSyncOperationHandlerTests` déjà présents).
## 26. Tests Worker
`SyncWorkerTests` : **5/5** (fakes/mocks, aucune base HFSQL réelle requise).
## 27. Tests Mobile
Référence : **506/506** (à confirmer après validation).
## 28. Suite unitaire
Référence : **1683/1683** hors HFSQL environnemental (à confirmer après
validation).
## 29. Build solution
Référence : **VERT** (à confirmer après validation).
## 30. Build Worker
`Omika.Worker` : **VERT**.
## 31. Build Android
Référence : **VERT** (à confirmer après validation).
## 32. Test HFSQL réel
**Non effectué** dans ce lot : les tests unitaires du Worker utilisent des
fakes/mocks. Les tests HFSQL réels restent environnementaux (nécessitent un
serveur HFSQL de test). Une absence de serveur HFSQL de test ne constitue pas
un échec unitaire.
## 33. Documentation
- `docs/LOT_SYNC_WORKER.md` (ce document).
## 34. Limites restantes
- **Concurrence** : un seul Worker actif par file Sync (pas de claim distribué).
- **Windows Service** : support non activé (futur lot de déploiement).
- **Test HFSQL réel** : non effectué (environnemental).
- **Handlers** : seul Intervention est branché ; les autres EntityTypes passent
par le fallback `NO_HANDLER`.
## 35. Confirmation aucun schéma HFSQL modifié
**Aucun schéma HFSQL n'est modifié** : aucune création/suppression de table,
aucune modification de colonne. Le Worker lit/écrit dans les tables techniques
existantes (TOSRV, MobReception, MobLog) via le repository existant.
## 36. Confirmation aucun protocole historique réintroduit
**Aucun protocole historique n'est réintroduit** : ni `CB_Ajout_TOSRV`, ni le
protocole WINDEV historique. Le transfert TOSRV → MobReception est assuré par
le repository moderne (`SyncTransferRepository.TransferToReceptionAsync`).