Temps de réponse très dégradés depuis le déploiement de la P24

Bonjour,

Nous avons observé une dégradation importante des temps de réponses lors de l’affichage de listes depuis le déploiement de la P24 (en mars/avril dernier) qui est devenue beaucoup plus prononcée (avec impact métier important) depuis que nous sommes contraints de travailler à distance par VPN (en environnement Intranet / réseau interne, la dégradation est moins sensible).

En creusant le sujet, j’ai pu déterminer que chaque row de liste embarque l’ensemble des métadonnées de chaque champ de l’objet listé. L’effet est désastreux sur un objet en particulier qui embarque 240 champs (je tend le bâton pour le faire battre, je sais; il s’agit d’un objet modélisé pour nos juristes et il est comme les codes et réglementation avec lesquels ils travaillent…on ajoute mais on enlève jamais ou rarement).

Je précise que l’affichage en liste a été optimisé (fonctionnellement) en configurant uniquement quelques champs visibles en liste / le reste en formulaire seulement. Hors, les métadonnées servies au front lors du rendu de la liste embarquent aussi les champs configurés en formulaire seulement.

De ce fait, la volumétrie du json servi est énorme (~100ko en moyenne par record(metadata+data) pour un total de 2Mo par page de 20 items).

Est-il envisageable de filtrer les métadonnées aux champs visibles dans la liste ou (compte tenu du fait que la plupart des métadonnées sont les mêmes pour chaque ligne) de factoriser l’arbre des métadonnées?

C’est normalement le cas, l’optimisation avait été faite il y a longtemps sur les listes.
Les attributs remontés (meta + data) sont uniquement ceux de la liste sauf pour 2 exceptions :

  • présence de contrainte inter-champs sur l’objet : même invisible, la UI peut en avoir besoin pour calculer qq chose sur ceux visibles
  • ou bascule en liste éditable : on affiche les attributs modifiables mais il faut aussi les champs cachés

Car là il faut potentiellement tous les champs pour valider les hooks front.

Je vais vérifier qu’il n’y a pas de régression à ce niveau.

Je ne vois pas de régression, ne sont remontés que les champs nécessaires et suffisants à la liste.
Il va falloir faire le tri dans vos 250 champs.

Simplicité envoie les champs :

  • non forbidden
  • Row ID ou Foreign key
  • Visible en liste ou partout
  • En recherche (pour pouvoir afficher les badges de filtre)
  • Liés à une contrainte
  • ou liés à une place map (je ne vois pas trop pourquoi)

Combien de champs sont visibles en liste ou recherchable dans votre objet ?

Bonsoir François,
merci beaucoup pour ton analyse.
Je pense qu’il y a des marges d’amélioration dans les champs activés en recherche (ils le sont quasiment tous). Par contre, de nombreux champs (form only) sont soumis à des contraintes et là ça risque de coincer. Je vais faire le ménage au maximum.

C’est vrai qu’avant on ne remontait pas les champs en recherche, c’est devenu nécessaire pour permettre d’afficher les filtres (sous forme de badge) sur des champs qui ne sont justement pas en liste. On doit pouvoir optimiser ça sur les champs dont les filtres sont non vides uniquement, les autres ne servent à rien sur la liste (à vérifier tout de même).

Pour les contraintes, ça doit pouvoir s’optimiser de notre côté si on peut mieux cerner le contexte (contrainte back uniquement, ou formulaire = context update). Comment sont paramétrées vos contraintes ?

Bonjour François,
sur l’objet concerné, la plupart des contraintes sont front (pour que l’utilisateur puisse voir l’effet sans attendre un enregistrement) :

  • 12 contraintes de visibilité de zones d’attributs (54 champs concernés)
  • 78 contraintes de réinitialisation de valeur, visibilité ou obligatoire (44 champs concernés)
    Bruno

Bonjour Bruno,

Ok je vais regarder les pistes d’amélioration pour limiter les méta-data remontées sur chaque ligne.

Pour les contraintes front, il va falloir trouver un moyen de savoir si elles s’appliquent à la liste ou pas.
Ont elles une condition de type [CONTEXT:UPDATE] pour limiter les impacts au seul formulaire ?
Car si la condition est “true” elles s’appliquent partout et il ne sera pas possible de filtrer sans aller chercher les types d’impact (visibilité du champ ou valeur forcée).

D’un point de vue conceptuel, un tel objet typé à champs “à tiroir” peut-il se rapprocher d’une modélisation par héritage limitant les attributs de chaque héritier “typé” sans avoir besoin de passer par des contraintes sur un gros objet chapeau ? L’objet chapeau n’aurait alors que les attributs communs en liste + un getTargetObjet pour rediriger vers des héritiers typés avec leurs champs supplémentaires.

Oui le pattern par héritage (autrement appellé le pattter “legume > choux ou carotte”) est peut être la bonne approche.

Une autre approche c’est de mettre les règles de visibilité conditionnelle des attributs dans du code serveur ìnitUpdate/Create/Delete/List` plutôt que sous forme de contraintes.

Sinon sur un autre cas j’avais un objet à plus de 400 attributs que j’ai implémente avec une logique de toggle “fort” en fonction du “theme” actif (par toggle “fort” je veux dire que dans les initXXX je mets tous les attributs en forbidden sauf ceux du thème actif ou de la liste quand j’affiche la liste, à noter qu’en update un changement de theme déclenche un save), avec cette stratégie j’avais au max 20-30 attributs actifs sur le formulaire (et une dizaine en liste/recherche) et du coup des temps de réponse nickel malgré la monstruosité de l’objet (les attributs forbidden sont exclus des metadonnées/données)

Merci beaucoup pour vos retours et recommandations.

J’ai déjà commencé à faire le tri sur les options utiles de recherche (souvent, l’option de recherche est active par défaut dans l’objet détenteur mais la recherche n’est pas forcément requise dans l’objet qui importe ces propriétés via une foreign key).

Je vais en effet aussi regarder du côté du “[CONTEXT:UPDATE]” (toutes mes contraintes sont “true” ou “false” si désactivées globalement à la marge).

La piste du cloisonnement par héritage est intéressante mais certains aspects ne sont pas encore bien couverts (héritage des options d’association par exemple) donc je n’utilise cette possibilité que si je n’ai pas pu faire autrement / en fonction du rapport coût/bénéfice.

Je vais voir dans quelles mesures ces options améliorent la situation.

Merci encore
Bruno

Après analyse de notre côté, il apparaît qu’on peut bien limiter les meta de chaque ligne car on récupère déjà les champs “visible ou présent en recherche ou en contrainte ou dans un placemap” dans les meta de l’objet lui-même dans le service search. Chaque ligne peut donc ne recevoir que les fields visibles uniquement (les autres seront repris de ceux de l’objet pour pouvoir appliquer les contraintes), mais il faut bien envoyer les data (visibles ou en contrainte de champs cachés) de chaque lignes.

C’est donc une optim sans impact sur votre code ou votre paramétrage (même si mette [CONTEXT:UPDATE] permettrait de limiter le scope d’application, ou retirer des recherches fulltext est une autre forme d’optimisation à la main du designer).

PS : pour désactiver un contrainte, il faut lui mettre un ordre négatif (en général inverser son signe pour pas perdre l’ordre paramétré), car ça l’exclura de l’objet lors du chargement en mémoire. Ca évite de l’interpréter pour ne rien faire en runtime.

C’est noté et pris en compte (ménage dans les options de recherche et les contraintes).
cela m’a permis de retrouver d’anciennes contraintes sans impact suite à la suppression des champs initialement impactés (un oubli).
Pour l’instant, je n’ai réussi à gagner “que” ~15% sur la volumétrie (1,65Mo->1,43Mo pour 20 lignes sur le compte de test utilisé).
Merci beaucoup.

Question bête, est-ce que votre reverse proxy implémente de la compression gzip ?

Sinon ça peut se configurer niveau Tomcat, je ne pense pas que nos images Docker aient un param pour l’activer à la demande car en général ça se fait un niveau du reverse proxy mais ça peut s’envisager

C’est une bonne question…
L’entête de la requête contient bien “Accept-Encoding: gzip, deflate, br” mais l’entête de la réponse ne contient apparemment aucun “Content-Encoding: xxx” (ni gzip ni rien d’autre).

Je viens de push un pipeline avec

labels:
  - "traefik.http.middlewares.test-compress.compress=true"

Je teste et je te dis.

Si c’est en place tu as effectivement Content-ecoding: gzip dans les headers de la réponse:

NB: Le ratio gzip vs non compressé c’est typiquement entre 5 et 10

Test KO (enfin ça fonctionne toujours mais l’entête “Content-Encoding: xxx” n’est toujours pas dans la réponse.

Voici précisément les headers de la réponse au POST de refresh de la liste concernée:

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 24 Apr 2020 15:43:32 GMT
Content-Type: application/json;charset=UTF-8
Content-Length: 1501792
Connection: keep-alive
Etag: "1587743011:dtagent101892004201755148z0B:dtagent101892004201755148z0B"
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Last-Modified: Fri, 24 Apr 2020 15:43:29 GMT
X-Oneagent-Js-Injection: true
X-Simplicite-Sessionid: F89E4DEF96157503CB86A8ACABB24AC5

Sachant que dans ma stack Docker, j’ai configuré un traefik mais pas de nginx (je ne sais pas d’où il sort)

    networks:
      - _NETWORK_
      - traefik-net
    deploy:
      replicas: 1
      labels:
       - "traefik.port=8443"
       - "traefik.frontend.rule=Host:${BCSI_APP}"
       - "traefik.docker.network=traefik-net"
       - "traefik.frontend.redirect.entryPoint=https"
       - "traefik.backend.loadbalancer.method=drr"
       - "traefik.http.middlewares.test-compress.compress=true"

Ce serait impactant d’activer la compression par défaut au niveau du tomcat? (ou idéalement, via un paramétrage)

Je pense qu’on peut faire évoluer l’image Docker pour pouvoir activer/désactiver la compression niveau Tomcat. Je regarde ça de plus près et je te dis

OK, merci beaucoup.

Ce qui est curieux, c’est que les headers de la réponse à /health ou /ui/logs contiennent bien “Content-Encoding: gzip” mais pas les headers de la réponse au POST de chargement de la liste…

Il n’y aurait qd même pas une règle au dessus qui empêche la compression du json ? (cf. différente de Content-Type)

L’optimisation au niveau des metadata de ligne sera livrée au prochain build. à voir si ça améliore les choses pour votre objet. Normalement, la définition des champs en contrainte ou en recherche n’est plus remontée qu’au niveau de l’objet, et au niveau de chaque ligne :

  • on n’a que la définition des attributs visibles : car peut avoir été modifiée par le back (hook de postSelect ou contrainte back…)
  • et les valeurs visibles ou utilisées par des contraintes (mais pas en recherche)

C’est un peu compliqué de traiter chaque cas, mais je ne vois pas d’autres axes d’amélioration généralisables à ce stade.

Nous avons poussé de nouvelles images Docker incluant la possibilité de faire gérer la compression par Tomcat via un -e GZIP=true.

En lançant des containers standalones ça fait ce qu’il faut mais je ne sais pas trop si ça fera bon ménage avec vos diverses couches de reverse proxies… à tester.