# 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`).