301 lines
12 KiB
Markdown
301 lines
12 KiB
Markdown
# 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`).
|