Blog navigation

Derniers articles

Optimisation des Imports en Batch Multithread dans Sage X3 pour Gagner en Performance

 

Optimisation des Imports en Batch Multithread dans Sage X3 pour Gagner en Performance

Dans les installations de Sage X3, les lenteurs d’import en standard deviennent de plus en plus visibles, surtout dans des environnements où plusieurs verticaux cohabitent. Ce phénomène est exacerbé par le fait que Sage, en tant qu'éditeur, ne contrôle pas toujours la qualité et l'intégrité des développements fournis pour chaque vertical, ce qui peut causer des conflits et des ralentissements. L'absence de vérification entre les différents développements intégrés dans le catalogue conduit souvent à des problématiques de performance.

Pour résoudre ce problème, il est possible de paralléliser les imports via un serveur batch, permettant ainsi d'exécuter plusieurs tâches simultanément et de contourner la nature monotâche des importations standard. Voici une explication de la solution et le code permettant cette optimisation.

Scénario classique

Prenons un exemple typique : l'importation d'une seule pièce prend environ 2 secondes. Dans une situation monotâche, importer 100 pièces prendrait donc 200 secondes. Cependant, en parallélisant l'import via 4 tâches distinctes, nous pouvons diviser ce temps par quatre, le ramenant à 50 secondes.

La seule contrainte à prendre en compte est le délai de scrutation du serveur batch, fixé par défaut à 30 secondes. Cela peut induire un léger décalage dans l'exécution mais reste largement compensé par le gain global apporté par la parallélisation.

Exemple de code pour la parallélisation d'import dans Sage X3

Voici le code permettant de paralléliser les imports en utilisant des tâches batch multithread dans Sage X3 :


Funprog   YTRX_IMP_X3_BATCH(YTHDTRX, YFILELIST, YAOETPL)
  Value Integer     YTHDTRX
  Value Char        YFILELIST()()
  Value Char        YAOETPL()

  Local File ="X3.ABATRQT"      [F:YABR]
  Local File ="X3.ABATRQTL"     [F:YABL]
  Local Integer YX3BATCHOK    : Raz YX3BATCHOK
  Local Integer YX3BATCHSLEEP : Raz YX3BATCHSLEEP
  Local Integer YTHXTRXID
  Local Integer YNUMREQ(YTHDTRX)
  Local Integer YIBAL

  Trbegin [F:YABR], [F:YABL]
  For YTHXTRXID=0 To (YTHDTRX-1)
    # Query header
    YNUMREQ(YTHXTRXID) = uniqid([F:YABR]) # max(ABATRQT.NUMREQ)+1
    [F:YABR]NUMREQ=YNUMREQ(YTHXTRXID)
    [F:YABR]DAT=date$
    [F:YABR]DFIN=date$
    [F:YABR]DOSSIER=nomap
    [F:YABR]FLAG=1
    [F:YABR]HEURE=format$("D:hhmm", date$)
    [F:YABR]MESSAGE=1
    [F:YABR]MONO=1
    [F:YABR]TACHE="IMPORT"
    [F:YABR]USER=[V]GUSER
    [F:YABR]CREUSR=[V]GUSER
    [F:YABR]UPDUSR=[V]GUSER
    [F:YABR]CREDATTIM=datetime$
    [F:YABR]UPDDATTIM=datetime$
    [F:YABR]LAN=[V]GLANGUE
    # Query Parameters
    For YIABL=1 To 7
      [F:YABL]NUMREQ=YNUMREQ(YTHXTRXID)
      [F:YABL]NUM=YIABL
      [F:YABL]CREUSR=[V]GUSER
      [F:YABL]UPDUSR=[V]GUSER
      [F:YABL]CREDATTIM=datetime$
      [F:YABL]UPDDATTIM=datetime$
      If YIABL = 1
        [F:YABL]PARAM="MODIMP"
        [F:YABL]VALEUR=YAOETPL
      Elsif YIABL = 2
        [F:YABL]PARAM="NOMIMP"
        [F:YABL]VALEUR=left$(filpath("tmp",[L]YFILELIST(YTHXTRXID),"csv"),30)   # ABATRQTL.VALEUR = 30 Alpha max
      Elsif YIABL = 3
        [F:YABL]PARAM="NOMIMP"
        [F:YABL]VALEUR=mid$(filpath("tmp",[L]YFILELIST(YTHXTRXID],"csv"),31,30) # ABATRQTL.VALEUR = 30 Alpha max
      Elsif YIABL = 4
        [F:YABL]PARAM="NOMIMP"
        [F:YABL]VALEUR=right$(filpath("tmp",[L]YFILELIST(YTHXTRXID],"csv"),61)  # ABATRQTL.VALEUR = 30 Alpha max
      Elsif YIABL = 5
        [F:YABL]PARAM="TYPEXP"
        [F:YABL]VALEUR="2"
      Elsif YIABL = 6
        [F:YABL]PARAM="VOLFIL"
        [F:YABL]VALEUR=left$("[TMP]/"+[L]YFILELIST(YTHXTRXID]+".csv",30)        # ABATRQTL.VALEUR = 30 Alpha max
      Elsif YIABL = 7
        [F:YABL]PARAM="VOLFIL"
        [F:YABL]VALEUR=right$("[TMP]/"+[L]YFILELIST(YTHXTRXID]+".csv",31)       # ABATRQTL.VALEUR = 30 Alpha max
      Endif
      Write [F:YABL]
    Next YIABL
    Write [F:YABR]
  Next YTHXTRXID
  If !fstat : Commit : Else : Rollback : Endif

  # We loop on the queries until they finished to been executed
  # Otherwise VAT will not be available
  If find(YAOETPL,"YTRXSIHVAT","YTRXSIHCST")
    Repeat
      Read [F:YABR]NUMREQ = YNUMREQ(YX3BATCHOK)
      # 1 = Standby (max 30s)
      # 2 = In progress
      # 3 = Finished
      # 7 = Error
      If [F:YABR]FLAG>2 : YX3BATCHOK += 1
      Else : Sleep 1 : YX3BATCHSLEEP += 1 : Endif
    Until YX3BATCHOK = [L]YTHDTRX
  Endif
End YX3BATCHSLEEP

Explication de la solution

Le code ci-dessus illustre comment configurer des requêtes batch pour gérer plusieurs fichiers d'import en parallèle. Chaque fichier est traité dans une tâche distincte, identifiée par un NUMREQ unique. Les paramètres associés à chaque requête sont spécifiés dans la table ABATRQTL.

Le script boucle ensuite jusqu'à ce que toutes les tâches aient terminé leur exécution (avec des statuts possibles de "En attente", "En cours", "Terminé", ou "Erreur"). Cette boucle assure que les données nécessaires, comme la TVA, sont bien disponibles avant la validation de l'import.

Conclusion

Grâce à cette technique de parallélisation des imports dans Sage X3, il est possible de diviser par quatre (voire plus) le temps d'exécution global des imports, rendant ainsi le processus beaucoup plus efficace dans des environnements où plusieurs verticals cohabitent. Le principal avantage est une réduction considérable des temps de traitement, tout en maintenant une architecture standard sur le serveur batch.

 
Publié dans: Sage X3, 4GL