Files
Omika/docs/LOT_SYNC_WORKER.md
T
2026-10-23 18:46:50 +02:00

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