Comment abordez-vous normalement les problèmes de dépendance transitive qui se produisent au moment de l'exécution dans les grands projets logiciels?
Au cours des trois dernières semaines, j'ai essayé de démarrer un composant d'un gros morceau de logiciel dans un autre composant du logiciel, mais il meurt par intermittence en raison de problèmes de dépendance transitive qui ne sont connus qu'au moment de l'exécution.
Par problèmes de dépendance transitive, je veux dire que certaines dépendances des dépendances d'un projet donné entrent en collision avec d'autres dépendances au moment de l'exécution, provoquant une instabilité ou une défaillance instantanée.
Il existe des centaines, des centaines de dépendances en cours d'utilisation et environ 50 sous-projets associés à l'outil qui sont travaillés de manière isolée par d'autres équipes, où tous les modules ont des dépendances profondément imbriquées entre elles. Personne ne sait à quoi servent tous les sous-projets, étant donné l'ampleur et la complexité du projet.
Dans ce cas, tenteriez-vous de générer une représentation visuelle du DAG pour chacune des dépendances du composant affecté et tenteriez-vous de déterminer où des collisions peuvent se produire au moment de l'exécution? Je n'ai aucun contrôle sur la façon dont les dépendances sont gérées dans d'autres sous-projets et je ne peux pas modifier le code Java écrit par d'autres développeurs
Les solutions que j'ai trouvées ne fonctionnent que pendant une heure ou deux, puis elles cessent de fonctionner en raison de changements dans les composants en amont. Un exemple de composant en amont est un artefact dont dépend le projet sur lequel je travaille et qui est construit à un stade antérieur dans le pipeline CI.
À la demande des autres , je vais inclure des informations sur la technologie utilisée, au risque de clore la question pour avoir fourni trop d'informations, ou pour le corps trop long:
- Maven est utilisé pour la gestion des dépendances; et
- Le ressort est utilisé comme conteneur DI;
- La plupart des problèmes de dépendance impliquent des contextes de bean qui se chevauchent en raison du chargement des contextes d'autres modules au moment de l'exécution
- Le produit fonctionne correctement et il existe une multitude de tests unitaires et de tests d'intégration pour échapper à l'exactitude fonctionnelle du programme
En général, je recherche une approche indépendante du langage pour identifier les moyens de résoudre les conflits de dépendance sans énumérer à travers toutes les combinaisons possibles des dépendances d'un projet donné.
Je ne peux pas ré-architecturer le projet, ajouter des portes de qualité supplémentaires, pousser pour des changements de paradigme à travers l'entreprise ou changer de langue comme résolution.
Réponses:
Il peut être possible de résoudre votre problème en utilisant des chargeurs de classe personnalisés pour donner à chaque module de l'application son propre environnement d'exécution.
Fondamentalement, vous définiriez un environnement de base qui se compose d'un petit noyau de l'application, avec toutes les dépendances universellement acceptées et toutes les interfaces nécessaires pour que les modules communiquent entre eux. Ensuite, chaque module que vous chargez obtient son propre chargeur de classe qui peut accéder (1) aux classes de l'environnement d'exécution, (2) aux classes du cœur de l'application et (3) aux classes de ce module et à ses dépendances directes. Toutes les communications inter-modules passent par des interfaces définies dans le cœur de sorte qu'aucun module ne dépend directement d'un autre.
Cette technique est utilisée dans les serveurs d'applications pour permettre aux applications d'avoir des dépendances qui autrement se heurteraient, de sorte que vous pouvez voir une implémentation fonctionnelle de la technique, par exemple, dans Tomcat (et, si vous êtes chanceux, vous pourrez peut-être utiliser Tomcat's mise en œuvre avec peu de changement).
la source
Je n'ai jamais travaillé avec Maven ou Spring, mais pour vous donner une réponse générique à une question générique: quand il s'agit de problèmes de dépendance, votre système doit être conçu pour les détecter le plus tôt possible et quand ces problèmes sont détectés , ils doivent être signalés immédiatement, y compris leur cause possible.
Idéalement, ce point dans le temps n'est pas au "temps d'exécution de la production". Le meilleur moment est le «temps de construction», mais cela semble impossible dans votre cas. La deuxième meilleure option est donc les tests d'exécution. Vous nous avez dit qu'il existe des «tests d'intégration», mais comme ils ne détectent pas les problèmes de dépendance, ils semblent incomplets ou ne testent pas les collisions de dépendance en général. C'est là que vous devriez commencer à essayer de résoudre le problème.
De plus, pour rendre les tests plus efficaces, vos composants doivent "échouer rapidement" lorsqu'un problème de dépendance survient. Cela signifie que dès qu'une dépendance est résolue et qu'un composant est chargé, les collisions potentielles de dépendance doivent être vérifiées et signalées immédiatement. Si le système fonctionne pour la première fois pendant une heure avant qu'une collision ne soit détectée, il devient très difficile d'identifier la cause du problème. Je ne sais pas si Maven / Spring vous propose déjà une stratégie intégrée de "échec rapide", mais si ce n'est pas le cas, essayez de penser dans cette direction.
Pour atténuer les problèmes de production, votre système doit être conçu pour ne pas mourir complètement lorsqu'une collision de dépendance se produit avec un sous-composant moins important impliqué. L'échec ne doit pas être masqué, bien sûr, mais le système devrait idéalement rejeter le chargement de ce composant, consigner et / ou signaler le problème et rester dans un état stable. Lorsque le composant est chargé en premier, le système devient instable et vous ne détectez le problème qu'après, vous devrez probablement arrêter et redémarrer complètement le système, ce que je suppose que vous devriez éviter.
Par exemple, si votre système est une boutique Web et que votre sous-module pour "abonnement à la newsletter" ne peut pas être chargé pendant quelques heures, c'est quelque chose qui pourrait être plus facilement toléré comme si la boutique entière ne fonctionnait plus.
Enfin, cela pourrait ne pas être une option dans votre cas, mais si les composants peuvent être exécutés de manière plus isolée les uns par rapport aux autres, et si cela peut éviter les collisions de dépendance, cela peut être une approche qui mérite réflexion.
la source
Considérez cela comme si je réfléchissais tout haut ici. Selon les exigences / contraintes de temps, je pourrais considérer ceci:
1) créer une liste d'interfaces et de leurs implémentations (en utilisant la réflexion, si disponible)
2) créez une sorte de wrapper autour de votre DI. Lorsqu'une DI est invitée à fournir une implémentation concrète, voyez s'il y a déjà une règle DI en place; si la règle existe - renvoyez simplement ce que votre DI fournit; sinon, s'il y a une seule implémentation d'une interface et que vous pouvez créer une instance - log, et renvoyer une instance; sinon connectez-vous et échouez.
Cela dépend de la façon dont vous voulez vous impliquer, je suppose, mais finalement vous voulez juste avoir une liste complète de ce qui nécessite quoi - cette solution finira par générer cette liste.
Cependant, cela représente sans doute beaucoup de travail et n'est pas fiable - et si vous avez une condition étrange nécessitant une certaine dépendance une fois dans une lune bleue?
Que voulez-vous dire lorsque vous dites «changements dans les composants en amont»?
la source
Si je vous suis correctement, le problème est que le code que vous devez utiliser a des dépendances d'exécution sur les modules exprimés en Spring XML, mais maven n'en est pas informé. Ainsi, lorsque vous les utilisez, les choses dont ils ont besoin (les dépendances transitives) ne sont pas sur votre chemin de classe, et ils ne font pas ce qu'ils devraient.
Je suppose que lorsque vous demandez à vos collègues «comment puis-je faire fonctionner cela?», Ils disent «évidemment, vous avez besoin de cette liste de 35 modules et versions , comment pourriez-vous ne pas le savoir?
Vraisemblablement lorsque le test d'intégration est terminé, les modules requis sont déclarés comme dépendances de test. La solution systématique consiste donc à mettre en place des inspections des pom de test d'intégration pour s'assurer qu'ils n'ont rien d'autre que des outils de test tiers déclarés comme dépendances de test. Cela pourrait même être automatisé par le plugin mavenforcer .
Apparemment, vous ne pouvez pas faire cela, donc votre meilleure option se trouve probablement ici .
la source