[Instance plantée] ladomdev.demo.simplicite.io / boucle infinie dans les hooks

Bonjour,

L’instance Simplicité® ne répond plus.
Via Firefox j’ai une erreur 500 et via chrome le designer ne se connecter plus et les logs semblent bloqués.

Est-ce que tu peux voir ce qu’il se passe ?

Merci.

PS. Cette instance est provisoire et nous sert dans l’attente de la réstauration de l’instance ladomdevv4.orangeab.simplicite.io

Dans les logs de cette instance il y aun stackoverflow (une boucle infinie) dont l’origine se stitue visiblement dans votre code specifique (dans le hook postLoad de LadomFeuilleDeTemps)

06-Jan-2021 11:59:11.309 SEVERE [http-nio-19233-exec-3] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [JSONUI] in context with path [] threw exception [Servlet execution threw an exception] with root cause
        java.lang.StackOverflowError
                at java.base/java.lang.reflect.InvocationTargetException.<init>(InvocationTargetException.java:74)
                at jdk.internal.reflect.GeneratedMethodAccessor21.invoke(Unknown Source)
                at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                at java.base/java.lang.reflect.Method.invoke(Method.java:564)
                at java.base/java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1216)
                at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2381)
                at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2215)
                at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1707)
                at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2520)
                at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2414)
                at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2215)
                at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1707)
                at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2520)
                at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2414)
                at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2215)
                at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1707)
                at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:517)
                at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:475)
                at java.base/java.util.ArrayList.readObject(ArrayList.java:899)

(...)
                at com.simplicite.util.Tool.streamToObject(Tool.java:4955)
                at com.simplicite.util.Tool.bytesToObject(Tool.java:4942)
                at com.simplicite.util.engine.MemoryCache.cloneDefinition(MemoryCache.java:138)
                at com.simplicite.util.engine.ObjectLoader.getClone(ObjectLoader.java:121)
                at com.simplicite.util.engine.ObjectLoader.load(ObjectLoader.java:87)
                at com.simplicite.util.ObjectDirect.init(ObjectDirect.java:41)
                at com.simplicite.util.ObjectDB.init(ObjectDB.java:238)
                at com.simplicite.util.ObjectDB.load(ObjectDB.java:216)
                at com.simplicite.util.engine.CoreCache.instantiateObject(CoreCache.java:3605)
                at com.simplicite.util.engine.CoreCache.getObject(CoreCache.java:3564)
                at com.simplicite.util.engine.GrantManager.getObject(GrantManager.java:96)
                at com.simplicite.util.GrantDirect.getObject(GrantDirect.java:578)
                at com.simplicite.util.Grant.getObject(Grant.java:1886)
                at com.simplicite.util.Grant.getTmpObject(Grant.java:1998)
                at com.simplicite.objects.LadomFeuilleDeTemps.LadStagiaire.addDataFormation(LadStagiaire.java:87)
                at com.simplicite.objects.LadomFeuilleDeTemps.LadStagiaire.postLoad(LadStagiaire.java:41)
(...)

Sans voir votre code je pense que vous faites appel à des objets dans votre postLoad ce qu’il faut é
éviter absolument (car dans le postLoad on est encore dans le load de l’objet, y demander de loader un objet aboutit donc forcément potentiellement à une boucle infinie).

Je pense donc que ce que vous faites dans ce postLoad n’est pas au bon endroit ou n’est pas écrit comme il faudrait.

PS: je l’ai rédémarrée, n’exécutez pas cette partie de votre code ou d’autres du même genre, modifiez le en mettant en commentaire tout appel à des objets dans des postLoad

Pouvez vous redémarrer nous avons accéder à la page ?

C’est fait sur l’instance ladomdev (je précise car il y a aussi sur ce serveur une instance ladom sur laquelle je n’ai rien fait ni maintenant ni avant, celle-ci sert elle à quelqu’un ?)

Oui c’est bien sur ladomdev l’autre instance ne sert à rien. Par contre j’ai toujours la page de chargement et il ne se passe rien. Nous ne pouvons pas modifier le code pour désactiver le code.
Pouvez voir ce qu’il se passe ?

J’ai redémarré et mis en commentaires le contenu de votre LadStagiaire.postLoad qui est visiblement ce qui induit le stack overflow. Je n’ai pas eu de pb pour le faire juste après le démarrage…

il ne s’agit pas de l’instance https://ladomdev.orangeab.simplicite.io/ mais Simplicité® qui a un problème.

Ah oui ok je me suis mélangé dans mes onglets. Je vais regarder la ladomdev.demo.simplicite.io

Merci de ne rien faire pendant que je regarde

ok.
à priori ce qu’il faut commenter c’est l’appelle at com.simplicite.objects.LadomFeuilleDeTemps.LadStagiaire.addDataFormation(LadStagiaire.java:87)
at com.simplicite.objects.LadomFeuilleDeTemps.LadStagiaire.postLoad(LadStagiaire.java:41)

Oui cf. en bas de l’extrait de logs de ma réponse initiale.

Je vais le faire via l’interface I/O pour ne pas passer par la UI (qui fait tout de suite un load de l’objet en question, à priori à cause du menu). Je vous tiens au courant.

Le fond du pb c’est de ne jamais faire un load dans un load => je ne sais pas ce que vous avez voulu faire mais c’est clairement pas dans un postLoad qu’il faut le faire (peut être une confusion sur ce à quoi sert un postLoad vs un postSelect ?) Cf. Simplicité® documentation/01-core/businessobject-code-hooks => on a ajouté un warning sur ce point

Ok nous attendons.
En fait il y a 2 besoins.

  • Le premier est de mettre à jour des données lors de la première connexion. Cette partie fonctionne.
  • Le second est de récupérer des valeurs sur des objets métiers liés en 1…N pour les afficher. Il y a une erreur dans le code mais dans le postLoad cela avait un sens même si c’est effectivement au moment de la sélection de l’objet que nous allons afficher que nous voulons ces valeurs. A voir si nous mettons cela dans un postSelect finallement ?!

Bonjour,

Le postLoad sert à initialiser/surcharger le comportement d’une instance d’objet suite au chargement de sa définition (exemple : mettre une search-spec ou masquer des champs pour certains groupes).
On n’y fait jamais de mises à jour sur les données métier. Votre besoin doit surement pouvoir se placer ailleurs.

Ensuite les boucles infinies peuvent se produire si dans un hook vous rappelez un verbe qui repassera par ce hook sur la même instance. Exemple classique : un postSave qui refait un save sur l’objet this, dans ce cas il faut juste setter les valeurs à mettre à jour au preSave.

Dans des cas plus complexes de dépendance de mises à jour en cascade, il faut travailler sur une instance dédiée (ex getTmpObject) et tester le nom de l’instance (ex isTmpInstance) qui doit faire la mise à jour pour ne pas boucler sur “this”.

Pouvez vous préciser le besoin, de quoi dépend cette mise à jour et ce qu’elle met à jour ?

Si vous le faites dans le postLoad, il faudra limiter les instances qui font ce calcul (ex isMainInstance), je doute que tous les usages (main/ref/panel/cron/export XML…) qui instancient cet objet ont besoin de lancer ce calcul, qui finira par être synchronisé pour éviter les concurrence d’accès. Bref ce n’est un design-pattern très fiable de mon point de vue, même si dans un cas particulier il fonctionne.

La première connexion au sens de la session se trouve dans les GrantHooks (ou PlatformHooks en V5) dans le postLoadGrant par exemple pour faire un mise à jour en masse = attention l’appel est synchrone, l’utilisateur doit attendre pour avoir sa page d’accueil.

S’il s’agit de mettre à jour des données relatives à un record (1 stagiaire) à faire à chaque consultation: il faut plutôt utiliser le pre ou postSelect en base (avant affichage du formulaire). Idem il faut que l’update soit rapide pour ne pas ralentir l’affichage.

Si le traitement est couteux en temps UI, il faudra penser à faire cet update lors de la mise à jour du stagiaire au postSave + postDelete (1 calcul pour N consultations au lieu de N calculs sur des données qui n’ont pas changé à chaque consultation).

Ou alors invoquer une Action asynchrone ou l’appeler par cron périodiquement si ces données n’ont pas besoin d’être à jour dès la mise à jour d’une donnée du calcul.

J’ai mis en commentaire le code du postLoad incriminé via un export + modif manuel + import de votre module LadomFeuilleDeTemps

A priori plus de pb:

Pour mémoire voici ce que j’ai fait:

  1. démarrage de l’instance sans accès à la UI

  2. export: curl -u designer --form service=moduleexport --form module=LadomFeuilleDeTemps https://ladomdev.demo.simplicite.io/io > LadomFeuilleDeTemps.xml

  3. modif manuelle du fichier XML

  4. import du fichier modifié: curl -u designer --form service=moduleimport --form version=0.1 --form file=@LadomFeuilleDeTemps.xml --form module=LadomFeuilleDeTemps https://ladomdev.demo.simplicite.io/io

  5. Clear cache global: curl -u designer:simplicite --form service=clearcache https://ladomdev.demo.simplicite.io/io

NB: J’aurais pu faire ça aussi via Git

Nous allons surement mettre ce premier code dans un postSelect même si le mise à jour des données logiquement faible et le second dans des postUpdate.
Merci.