Premiere commit - Omika
This commit is contained in:
@@ -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`).
|
||||
Reference in New Issue
Block a user