¿Cómo aborda normalmente los problemas de dependencia transitiva que se producen en tiempo de ejecución en grandes proyectos de software?
Durante las últimas tres semanas, he estado tratando de iniciar un componente de una gran pieza de software dentro de otro componente del software, pero muere de forma intermitente debido a problemas de dependencia transitiva que solo se conocen en tiempo de ejecución.
Por problemas de dependencia transitiva, me refiero a que ciertas dependencias de las dependencias de un proyecto determinado chocan con otras dependencias en tiempo de ejecución, causando inestabilidad o falla instantánea.
Hay cientos, cientos de dependencias en uso, y hay aproximadamente 50 subproyectos asociados con la herramienta en los que otros equipos trabajan de forma aislada, donde todos los módulos tienen dependencias profundamente anidadas entre sí. Nadie sabe para qué se utilizan todos los subproyectos, dada la escala y la complejidad del proyecto.
En esta situación, ¿intentaría generar una representación visual del DAG para cada una de las dependencias del componente afectado e intentaría determinar dónde pueden ocurrir colisiones en tiempo de ejecución? No tengo control sobre cómo se gestionan las dependencias en otros subproyectos, y no puedo cambiar ningún código Java que haya sido escrito por otros desarrolladores
Las soluciones que se me ocurrieron solo funcionan durante una hora o dos, y luego dejan de funcionar debido a los cambios en los componentes ascendentes. Un ejemplo de un componente ascendente es un artefacto en el que el proyecto en el que estoy trabajando depende del que se construye en una etapa anterior en la tubería de CI.
A petición de otros , voy a incluir información sobre qué tecnología se está utilizando, a riesgo de que se cierre la pregunta por proporcionar demasiada información o que el cuerpo se alargue demasiado:
- Maven se usa para la gestión de dependencias; y
- Spring se utiliza como contenedor DI;
- La mayoría de los problemas de dependencia implican contextos de bean superpuestos como resultado de los contextos de otros módulos que se cargan en tiempo de ejecución
- El producto funciona correctamente, y hay una mezcla heterogénea de pruebas unitarias y pruebas de integración para eludir la corrección funcional del programa.
En general, estoy buscando un enfoque independiente del lenguaje para identificar formas de resolver conflictos de dependencia sin enumerar todas las combinaciones posibles de las dependencias de un proyecto determinado.
No puedo rediseñar el proyecto, agregar puertas de calidad adicionales, impulsar cambios de paradigma en toda la empresa o cambiar idiomas como una resolución.
Respuestas:
Es posible resolver su problema usando cargadores de clases personalizados para dar a cada módulo en la aplicación su propio entorno de ejecución.
Básicamente, definiría un entorno central que consta de un pequeño núcleo de la aplicación, junto con las dependencias acordadas universalmente y todas las interfaces necesarias para que los módulos se comuniquen entre sí. Luego, cada módulo que cargue tiene su propio cargador de clases que puede acceder a (1) clases desde el entorno de tiempo de ejecución, (2) clases desde el núcleo de la aplicación y (3) clases desde ese módulo y sus dependencias directas. Toda la comunicación entre módulos pasa por interfaces definidas en el núcleo, de modo que ningún módulo depende directamente de otro.
Esta técnica se usa en los servidores de aplicaciones para permitir que las aplicaciones tengan dependencias que de otra manera entrarían en conflicto, por lo que puede ver una implementación funcional de la técnica, por ejemplo, en Tomcat (y, si tiene suerte, puede usar Tomcat's implementación con poco cambio).
fuente
Nunca he trabajado con Maven o Spring, pero para darle una respuesta genérica a una pregunta genérica: cuando se trata de problemas de dependencia, su sistema debe estar diseñado para detectarlos lo antes posible y cuando se detecten esos problemas. , deben señalizarse de inmediato, incluida su posible causa.
Idealmente, este punto en el tiempo no está en el "tiempo de ejecución de producción". El mejor momento es el "tiempo de construcción", pero eso parece ser imposible en su caso. Entonces, la segunda mejor opción son las pruebas de tiempo de ejecución. Nos dijo que hay "pruebas de integración", pero como no detectan los problemas de dependencia, parecen estar incompletas o no prueban las colisiones de dependencia en general. Ahí es donde debes comenzar a tratar de resolver el problema.
Además, para que las pruebas sean más efectivas, sus componentes deben "fallar rápidamente" cuando surge un problema de dependencia. Eso significa que, tan pronto como se resuelva una dependencia y se cargue un componente, las posibles colisiones de dependencia deben verificarse y señalizarse de inmediato. Si el sistema se ejecuta por primera vez durante una hora antes de que se detecte una colisión, se hace muy difícil detectar la causa del problema. No sé si Maven / Spring ya le proporciona una estrategia integrada de "falla rápida", pero si este no es el caso, trate de pensar en esa dirección.
Para mitigar los problemas en la producción, su sistema debe estar diseñado para no morir por completo cuando se produce una colisión de dependencia con un subcomponente menos importante involucrado. La falla no debe enmascararse, por supuesto, pero el sistema idealmente debe rechazar la carga de ese componente, registrar y / o señalar el problema y permanecer en un estado estable. Cuando el componente se carga primero, el sistema se vuelve inestable y detecta el problema solo después, entonces probablemente tendrá que apagar y reiniciar el sistema por completo, lo que supongo que es algo que es mejor evitar.
Por ejemplo, si su sistema es una tienda web, y su submódulo para "suscripción al boletín" no se puede cargar durante algunas horas, eso es algo que podría tolerarse más fácilmente como si toda la tienda ya no funciona.
Finalmente, esta podría no ser una opción en su caso, pero si los componentes pueden ejecutarse en un mejor aislamiento entre sí, y si eso puede evitar colisiones de dependencia, puede ser un enfoque en el que valga la pena pensar.
fuente
Considera esto solo pensando en voz alta aquí. Dependiendo de los requisitos / limitaciones de tiempo, podría considerar esto:
1) crear una lista de interfaces y sus implementaciones (usando la reflexión, si está disponible)
2) crea una especie de envoltorio alrededor de tu DI. Cuando se le pide a un DI que proporcione una implementación concreta, vea si ya existe una regla de DI; si existe una regla, simplemente devuelva lo que proporciona su DI; de lo contrario, si hay una implementación única de una interfaz y puede crear una instancia: registre y devuelva una instancia; de lo contrario, inicie sesión y falle.
Depende de cuán involucrado quiera involucrarse, supongo, pero eventualmente solo desea tener una lista completa de lo que requiere qué: esta solución eventualmente generará esa lista.
Sin embargo, podría decirse que esto es mucho trabajo y no es confiable: ¿qué pasa si tiene una condición extraña que requiere cierta dependencia una vez en una luna azul?
¿Qué quieres decir cuando dices "cambios en los componentes aguas arriba"?
fuente
Si te sigo correctamente, el problema es que el código que necesitas usar tiene dependencias de tiempo de ejecución en los módulos expresados en Spring XML, pero a maven no se le informa sobre ellos. Entonces, cuando los usa, las cosas que necesitan (las dependencias transitivas) no están en su classpath, y no hacen lo que deberían.
Supongo que cuando le preguntas a tus colegas '¿cómo hago para que esto funcione?', Ellos dicen 'obviamente necesitas esta lista de 35 módulos y versiones , ¿cómo puedes no saber eso?'
Presumiblemente, cuando se realizan las pruebas de integración, los módulos necesarios se declaran como dependencias de prueba. Por lo tanto, la solución sistemática es configurar inspecciones de pom de prueba de integración para garantizar que no tengan más que herramientas de prueba de terceros declaradas como dependencias de prueba. Esto incluso podría ser automatizado por el complemento maven enforcer .
Aparentemente no puede hacer eso, por lo que su mejor opción probablemente se encuentre aquí .
fuente