2014-07-09 16 views
8

Un po 'complesso da descrivere, ma farò del mio meglio. Fondamentalmente stiamo usando il flusso di lavoro Git, nel senso che abbiamo i seguenti rami:Come gestire più script db alter provenienti da diversi rami di funzionalità Git?

  • produzione, che è il ramo dal vivo. Tutto è in esecuzione nella produzione nell'ambiente web live.
  • integrazione, in cui tutte le nuove funzionalità sono integrate. Questo ramo viene unito alla produzione ogni settimana.
  • uno o più rami di funzionalità in cui sviluppatori o team di sviluppo sviluppano nuove funzionalità. Al termine, gli sviluppatori uniscono il loro ramo di funzionalità all'integrazione.

Quindi, niente di veramente complesso qui. Tuttavia, poiché la nostra applicazione è un'applicazione Web in esecuzione su un database MySQL, le nuove funzionalità spesso richiedono modifiche allo schema del database. Per automatizzare questo, stiamo usando dbdeploy, che ci permette di creare script alternativi, dato un numero. Per esempio. 00001.sql, 00002.sql, ecc. Dopo l'unione con il ramo di integrazione, dbdeploy verificherà quali script alterati hanno un numero maggiore rispetto all'ultimo più recente su quel specifico database e li eseguirà.

Ora assumere quanto segue. - l'integrazione ha alter script fino a 00200.sql. Tutti questi sono eseguiti sul database di integrazione. - lo sviluppatore John ha una caratteristica di funzione X, che è stata creata quando l'integrazione aveva ancora 00199.sql come il più alto alter script.

John crea 00200.sql a causa di alcune modifiche richieste dello schema db.

Ora, a un certo punto John unirà le sue modifiche al ramo di integrazione. John otterrà un conflitto di fusione e vedrà che il suo 00200.sql esiste già in integrazione. Ciò significa che ha bisogno di aprire il file in conflitto, estrarre il suo contenuto, resettare il file su "mio" (lo stato originale come in integrazione) e inserire il suo contenuto in un nuovo file.

Ora, dal momento che stiamo lavorando con dieci sviluppatori, otteniamo questa situazione ogni giorno. E mentre capiamo le ragioni di questo, a volte è molto complicato. John rinomina il suo script, esegue un'unione di commit per l'integrazione, spinge le modifiche all'upstream solo per vedere che qualcun altro ha già creato uno 00201.sql, richiedendo a John di eseguire nuovamente i processi.

Sicuramente ci devono essere più team che utilizzano il flusso di lavoro Git e utilizzano uno strumento di gestione delle modifiche del database per l'automazione delle modifiche allo schema del database?

Così, in breve, le mie domande sono:

  • Come automatizzare le modifiche allo schema di database, quando si lavora su diverse funzionalità rami, che operano su diverse istanze dello stesso db?
  • Come evitare conflitti di unione per tutto il tempo, pur avendo ancora la possibilità di avere un ordine fisso negli script di modifica eseguiti? Per esempio. 00199.sql deve essere eseguito prima di 00200.sql, perché 00200.sql potrebbe dipendere da qualcosa fatto in 00199.sql.

Eventuali altri suggerimenti sono i benvenuti.

+0

Una discussione utile e complicata, di sicuro. Gli sviluppatori di software là fuori, prendi nota. Tracciare i cambiamenti dello schema come l'OP ha sottolineato dipende in realtà dal tipo di copertura che ci si aspetta di ottenere dallo sforzo in primo luogo. Vuoi una build quotidiana? Versioning? Punti di recupero? Codifica difensiva? Le soluzioni varieranno di conseguenza. –

risposta

0

Ho usato due approcci diversi per superare il problema in passato.

Il primo è utilizzare un n ORM che può gestire gli aggiornamenti dello schema.

L'altro approccio è creare uno script, che crea in modo incrementale lo schema del database. In questo modo se uno sviluppatore ha bisogno di una riga aggiuntiva in una tabella, deve aggiungere l'istruzione sql appropriata dopo che la tabella è stata creata. Allo stesso modo, se ha bisogno di una nuova tabella, dovrebbe aggiungere l'istruzione sql per quello. Quindi la fusione diventa una questione di assicurarsi che le cose accadano nell'ordine corretto. Questo è fondamentalmente ciò che fa il processo di aggiornamento del database in un ORM. Tale script deve essere codificato in modo molto difensivo, e ogni affermazione dovrebbe verificare se esistono i suoi privilegi.

4

I binari utilizzati per fare questo, con esattamente i problemi che descrivi. Hanno cambiato il seguente schema: i file (binari loro migrazioni chiama) sono etichettati con un timestamp UTC di quando è stato creato il file, ad esempio

20140723069701_add_foo_to_bar 

(La seconda parte del nome non significa contribuire alla ordinazione).

Rails registra i timestamp di tutte le migrazioni che sono state eseguite. Quando gli chiedi di eseguire migrazioni in sospeso seleziona tutti i file di migrazione il cui timestamp non è presente nell'elenco delle migrazioni già eseguite e le esegue in ordine numerico.

Non si otterranno più conflitti di unione a meno che due persone ne creino uno esattamente nello stesso momento.

I file vengono ancora eseguiti nell'ordine in cui sono stati scritti, ma possibilmente interlacciati con il lavoro di qualcun altro. In teoria puoi ancora avere problemi - per esempio lo sviluppatore decide di rinominare un tavolo che ho deciso di aggiungere anche una colonna. Questo è molto meno comune di 2 sviluppatori che apportano modifiche al db e si avrebbero problemi anche se non si considerano le modifiche dello schema presumibilmente ho appena scritto codice che interroga una tabella non più esistente - a un certo punto gli sviluppatori che lavorano su cose correlate avranno parlare tra loro!

2

Alcuni suggerimenti:

1 - hanno uno sguardo a Liquibase, ogni versione ottiene un file che fa riferimento alle modifiche che devono accadere, quindi i file di cambiamento possono essere nominati utilizzando una stringa di significato, piuttosto che per numero.

2 - avere una posizione centrale per ottenere il prossimo numero disponibile, quindi le persone utilizzano l'ultimo numero.

Ho usato Liquibase in passato, con successo, e non avevamo il problema che descrivevi.

+0

Ma hai utilizzato il flusso di lavoro GIT con i branch di funzionalità per ogni sviluppatore? Perché, specialmente con l'opzione 2, suppongo che tu abbia ancora problemi con l'ordine di esecuzione garantito su tutti i rami. Per esempio. John prenderebbe il numero 3, perché ho preso il numero 2. Ma John unirà le sue modifiche al ramo dell'integrazione prima di me. In questo modo le modifiche di John verranno eseguite prima delle mie sul ramo di integrazione. Successivamente, quando si uniscono alla produzione, le mie modifiche verranno eseguite prima di Johns. –

+0

Non ho mai usato l'opzione 2, è la cosa migliore che potrei inventare per uno strumento di gestione dello schema che utilizza nomi di script numerati. Ho usato i rami di funzionalità con Liquibase, entrambi con una versione raggruppata e con distribuzione continua e ha funzionato bene. Le tracce di Liquibase che sono state modificate sono state applicate e applicano quelle mancanti, ma supporta anche le dipendenze tra i gruppi di modifiche consentendo ordini forzati dove necessario. –

1

Come Frederick Cheung suggested, utilizzare timestamp anziché un numero di serie. L'applicazione delle modifiche dello schema per ordine di data/ora dovrebbe funzionare, poiché le modifiche dello schema possono dipendere solo dalle modifiche di una data precedente.

Inoltre, includere il nome dello sviluppatore nel nome dello script alternativo. Ciò impedirà il conflitto di fusione al 100%.

L'hook di unione dovrebbe cercare gli script di modifica appena aggiunti (presenti nel ramo unito ma non nel ramo upstream) ed eseguirli per ordine di data/ora.

0

Per dbvc commandline tool, utilizzo git log per determinare l'ordine degli script di aggiornamento.

git log -c --no-merges --pretty="format:" --name-status -p dev/db/updates/ | \ 
    grep '^A' | awk '{print $2}' | tac 

In questo caso il modo in cui l'ordine dei tuoi commit determinerà la sequenza in cui vengono eseguiti gli aggiornamenti. Qual è probabilmente quello che vuoi.

  • Se si esegue git merge b, gli aggiornamenti dal maestro verrà eseguito prima e che da B.
  • Se si esegue git rebase b, l'aggiornamento da B verrà eseguito prima e che da maestro.