12 KiB
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) viaISyncTransferRepository.InsertToSrvAsync(). - Ce que lit SyncProcessor : les opérations en attente dans
MobReceptionviaGetPendingOperationsAsync(). - 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 parAddOmikaDataServiceConnection()) : base Service, tableTOSRV.IWebServiceHfSqlConnection(enregistrée parAddOmikaData()) : base WebService/Business, tablesMobReceptionetMobLog.
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
CancellationTokenn'est pas annulé ; - à chaque cycle :
SyncProcessor.ProcessAsync()puis purge légère deMobLog; - 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, tableTOSRV) ;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 deAddOmikaDataServiceConnection()(connexion Service pour TOSRV) en plus deAddOmikaData()(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) :- Worker appelle SyncProcessor et s'arrête sur annulation ;
- absence de travail ne crée pas de boucle CPU ;
- exception de cycle ne provoque pas de boucle agressive ;
- intervalle de polling configurable respecté ;
- 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).