Come si affrontano i conflitti di dipendenza transitivi che sono noti solo in fase di esecuzione? [chiuso]


9

Come si affrontano normalmente i problemi di dipendenza transitiva che si verificano in fase di esecuzione in progetti software di grandi dimensioni?

Nelle ultime tre settimane, ho cercato di avviare un componente di un grosso software all'interno di un altro componente del software, ma esso si interrompe in modo intermittente a causa di problemi di dipendenza transitiva che sono noti solo in fase di esecuzione.

Per problemi di dipendenza transitiva, intendo che alcune dipendenze delle dipendenze di un determinato progetto si scontrano con altre dipendenze in fase di esecuzione, causando instabilità o fallimento immediato.

Ci sono centinaia, su centinaia di dipendenze in uso e ci sono circa 50 sottoprogetti associati allo strumento su cui si lavora isolatamente da altri team, in cui tutti i moduli hanno dipendenze profondamente annidate tra loro. Nessuno sa a cosa servano tutti i sottoprogetti, date le dimensioni e la complessità del progetto.

In questa situazione, proveresti a generare una rappresentazione visiva del DAG per ciascuna delle dipendenze del componente interessato e tenteresti di determinare dove possono verificarsi collisioni in fase di esecuzione? Non ho alcun controllo su come vengono gestite le dipendenze in altri sottoprogetti e non posso modificare alcun codice Java scritto da altri sviluppatori

Le soluzioni che ho escogitato funzionano solo per un'ora o due e poi smettono di funzionare a causa di cambiamenti nei componenti a monte. Un esempio di un componente a monte è un artefatto da cui dipende il progetto su cui sto lavorando, che è stato costruito in una fase precedente della pipeline CI.


Su richiesta di altri , includerò informazioni su quale tecnologia viene utilizzata, a rischio di avere la domanda chiusa per fornire troppe informazioni o che il corpo si allunghi troppo a lungo:

  • Maven viene utilizzato per la gestione delle dipendenze; e
  • La primavera è usata come contenitore DI;
  • La maggior parte dei problemi di dipendenza riguardano contesti di bean sovrapposti a seguito del caricamento di contesti di altri moduli in fase di esecuzione
  • Il prodotto funziona correttamente e sono disponibili gruppi di test unitari e test di integrazione per eludere la correttezza funzionale del programma

In generale, sto cercando un approccio indipendente dal linguaggio per identificare i modi per risolvere i conflitti di dipendenza senza enumerare tutte le possibili combinazioni delle dipendenze di un determinato progetto.

Non posso riprogettare il progetto, aggiungere ulteriori porte di qualità, spingere per cambiamenti di paradigma all'interno dell'azienda o cambiare lingua come risoluzione.


Stai parlando di qualcosa come il contenitore DI non correttamente impostato per sapere che per IFoo utilizzare IFooImpl?
Andy,

No, è più simile al pacchetto (b) nel progetto (b) è una dipendenza transitiva del pacchetto (a) nel progetto (A), ma (B) viene introdotto solo in fase di esecuzione. Tutto il software funziona da quello che posso dire, ma ho solo bisogno di una serie appropriata di dipendenze per consentire che tutte le dipendenze transitive vengano risolte correttamente. Esistono serie di dipendenze che consentiranno l'avvio corretto del prodotto, ma tali configurazioni sono molto fragili. Come sviluppatore junior. Non ho alcun controllo su questo.
Tyler,

2
Dai, "problema di dipendenza" è un termine molto generico, e chiunque legga questo probabilmente immagina qualcosa di diverso. Per il softare del mondo reale questo dipende dalla tecnologia, quindi per favore fai un esempio reale per una specifica tecnologia che hai in mente. I problemi si presentano perché mancavano componenti mancanti? O perché viene fornita una versione errata? Che tipo di meccanismo di risoluzione delle dipendenze è già in atto? Si prega di precisare.
Doc Brown,

1
Nel mondo .Net risolvo questo problema semplicemente installando i pacchetti nel mio progetto di applicazione principale. Mentre nessun codice applicativo utilizza effettivamente le librerie dei plugin direttamente, il compilatore .Net garantisce che la DLL sia nel posto giusto durante la compilazione e quindi la distribuzione.
Andy,

2
"Non ho alcun controllo su come vengono gestite le dipendenze, e non riesco a modificare il codice" - ciò che effettivamente può cambiare? Se non puoi cambiare nulla, la domanda sembra inutile.
Doc Brown,

Risposte:


6

Potrebbe essere possibile risolvere il problema utilizzando classloader personalizzati per assegnare a ciascun modulo dell'applicazione il proprio ambiente di esecuzione.

Fondamentalmente, definiresti un ambiente di base che consiste in un piccolo kernel dell'applicazione, insieme alle dipendenze universalmente concordate e a tutte le interfacce necessarie ai moduli per comunicare tra loro. Quindi, ogni modulo caricato ottiene il proprio classloader che può accedere a (1) classi dall'ambiente di runtime, (2) classi dal core dell'applicazione e (3) classi da quel modulo e dalle sue dipendenze dirette. Tutte le comunicazioni tra i moduli passano attraverso le interfacce definite nel core in modo che nessun modulo dipenda direttamente da un altro.

Questa tecnica viene utilizzata nei server applicazioni per consentire alle applicazioni di avere dipendenze che altrimenti si scontrerebbero, quindi puoi vedere un'implementazione funzionante della tecnica, ad esempio, in Tomcat (e, se sei fortunato, potresti essere in grado di utilizzare Tomcat implementazione con poche modifiche).


1
Non sono autorizzato a riprogettare il progetto per risolvere il problema delle dipendenze. Il passaggio a un'architettura simile a un microkernel sarebbe comunque piuttosto interessante.
Tyler,

2
@Tyler: tuttavia penso che questa sia una buona risposta generica alla parte generica della domanda.
Doc Brown,

1
Sembra un po 'come osgi.
SpaceTrucker,

4

Non ho mai lavorato con Maven o Spring, ma per darti una risposta generica a una domanda generica: quando si tratta di problemi di dipendenza, il tuo sistema dovrebbe essere progettato per rilevarli il più presto possibile e quando tali problemi vengono rilevati , dovrebbero essere segnalati immediatamente, compresa la loro possibile causa.

Idealmente, questo momento non è in "fase di esecuzione della produzione". Il momento migliore è il "tempo di costruzione", ma questo sembra essere impossibile nel tuo caso. Quindi la seconda opzione migliore sono i test del tempo di esecuzione. Ci hai detto che esistono "test di integrazione", ma poiché non rilevano i problemi di dipendenza, sembrano essere incompleti o non verificano le collisioni di dipendenza in generale. Ecco dove dovresti iniziare a provare a risolvere il problema.

Inoltre, per rendere i test più efficaci, i tuoi componenti devono "fallire rapidamente" quando si presenta un problema di dipendenza. Ciò significa che, non appena viene risolta una dipendenza e viene caricato un componente, le potenziali collisioni di dipendenza devono essere verificate e segnalate immediatamente. Se il sistema funziona per un'ora prima che venga rilevata una collisione, diventa molto difficile individuare la causa del problema. Non so se Maven / Spring ti fornisca già una strategia "fail fast" integrata, ma se non è così, prova a pensare in quella direzione.

Per mitigare i problemi di produzione, il sistema dovrebbe essere progettato in modo da non morire completamente quando si verifica una collisione di dipendenza con un sottocomponente meno importante coinvolto. L'errore non dovrebbe essere mascherato, ovviamente, ma il sistema dovrebbe idealmente rifiutare il caricamento di quel componente, registrare e / o segnalare il problema e rimanere in uno stato stabile. Quando il componente viene caricato per primo, il sistema diventa instabile e rilevi il problema solo in seguito, quindi dovrai probabilmente arrestare e riavviare completamente il sistema, il che immagino sia qualcosa che vorresti evitare.

Ad esempio, se il tuo sistema è un negozio Web e il sottomodulo per "iscrizione alla newsletter" non può essere caricato per alcune ore, è qualcosa che potrebbe essere più facilmente tollerato come se l'intero negozio non funzionasse più.

Infine, questa potrebbe non essere un'opzione nel tuo caso, ma se i componenti possono essere fatti funzionare in un migliore isolamento l'uno contro l'altro, e se ciò può evitare collisioni di dipendenza, questo potrebbe essere un approccio che vale la pena considerare.


2

Considera questo io solo a pensare ad alta voce qui. A seconda dei requisiti / vincoli temporali, potrei considerare questo:

1) creare un elenco di interfacce e relative implementazioni (usando reflection, se disponibile)

2) crea una sorta di wrapper attorno al tuo DI. Quando viene chiesto a un DI di fornire un'implementazione concreta, vedere se esiste già una regola DI; se la regola esiste - restituisci semplicemente ciò che il tuo DI fornisce; in caso contrario, se esiste un'unica implementazione di un'interfaccia e si è in grado di creare un'istanza: accedere e restituire un'istanza; altrimenti accedere e fallire.

Dipende da quanto sei coinvolto, immagino, ma alla fine vuoi solo avere un elenco completo di ciò che richiede cosa - questa soluzione alla fine genererà quell'elenco.

Tuttavia, probabilmente questo è un sacco di lavoro e non è affidabile - cosa succede se hai una strana condizione che richiede una certa dipendenza una volta su una luna blu?

Cosa intendi quando dici "cambiamenti nei componenti a monte"?


Siamo spiacenti, non volevo fornire troppe informazioni di base: quando descrivo l'ambiente operativo, le domande vengono generalmente ridotte al voto e fiammate a morte. Aggiornerò la domanda originale con una sezione delle definizioni.
Tyler,

2

Se ti seguo correttamente, il problema è che il codice che devi utilizzare ha dipendenze di runtime dai moduli espressi in Spring XML, ma non viene detto a Maven. Quindi quando li usi, le cose di cui hanno bisogno (le dipendenze transitive) non sono sul tuo percorso di classe e non fanno ciò che dovrebbero.

Immagino che quando chiedi ai tuoi colleghi "come faccio a farlo funzionare?", Dicono "ovviamente hai bisogno di questo elenco di 35 moduli e versioni , come potresti non saperlo?"

Presumibilmente quando viene eseguito il test di integrazione, i moduli richiesti vengono dichiarati come dipendenze del test. Quindi la soluzione sistematica è quella di impostare ispezioni dei pom di test di integrazione per assicurarsi che non abbiano altro che strumenti di test di terze parti dichiarati come dipendenze di test. Questo potrebbe anche essere automatizzato dal plug-in Maven Enforcer .

Apparentemente non puoi farlo, quindi la tua migliore opzione è probabilmente trovata qui .


È esattamente quello che sta succedendo, da quello che posso dire. Finora sto manipolando 30 dipendenze e modificando gli ambiti per eliminare le collisioni, ma qui è necessaria la revisione tra pari.
Tyler,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.