Da ormai qualche anno mi occupo di seguire il controllo del codice sorgente e i piani di release su database. Si tratta di un argomento molto spesso sottovalutato, ma di fondamentale importanza.
Tra le varie possibilità (tra cui anche quella a mio avviso da non seguire, ovvero non mettere il database sotto controllo del codice sorgente e versionamento ) possiamo elencare le più famose: - progetti di tipo SQL Server con Visual Studio ed integrazione con source control quali TFS, SVN, ecc.
- Integrazione diretta di TFS, SVN ed altri source control tool via filesystem
- Utilizzo di Management Studio in integrazione con il source control tool (sempre TFS, SVN, e quant'altro)
- Backup del database - Varie ed eventuali..
In questo post andremo ad approfondire l'integrazione tra SQL Server Management Studio, il prodotto di terze parti che attualmente utilizzo e l'integrazione con TFServices:
N.B. Per abilitare il Source Control di red-gate è necessario cambiare il file di config come indicato in questo post.
Non mi soffermerò sulle pratiche da seguire con i prodotti di terze parti, piuttosto vedremo meglio come funziona l'integrazione verso il source control server.
Grazie ai tool sopra elencati è possibile trattare il database esattamente come il codice e quindi possiamo fare un "database per ogni team". Più tecnicamente andremo a fare una branch per ogni team.
Utilizzando SQL Source control, dal menu contestuale, andiamo a collegare il database al TFS via https.
Dopo un redirect alla pagina di inserimento credenziali sui TFServices, verrà proposto il tree dei nostri progetti. Noi utilizzeremo in questo esempio il mio SampleProject e creeremo una cartella CoreDB per il codice sorgente del database. Instaurando il link abbiamo selezionato anche il tipo di gestione "Dedicated database" che applicheremo proprio durante lo sviluppo. Con questo comportamento ogni client che si connette con SSMS utilizzerà la sua versione del database, per fare in modo che il database centrale non venga toccato fino al checkin dei dati o delle strutture cambiate sulla propria branch di sviluppo.
Come potete notare l'icona sul nostro client è cambiata e con essa anche la relazione tra gli oggetti database ed il source control server:
Ogni eventuale cambiamento farà comparire una serie di simboli che darà allo sviluppatore l'idea di cosa è cambiato, per ora abbiamo solo fatto link, ma se proviamo a creare una tabella come la seguente, avremo un diverso look and feel:
CREATE TABLE dbo.Foo
(
idFoo int IDENTITY(1, 1) NOT NULL
, FooData varchar(30) NOT NULL
, CONSTRAINT
PK_dboFoo PRIMARY KEY
CLUSTERED
(
idFoo
ASC
)
);
Quel pallino è un'aggiunta o modifica per il nostro source control plugin. In caso di rimozione, avremmo notato un pallino vuoto all'interno.
Aprendo il database da un'altra postazione le nostre modifiche non saranno visibili. Esattamente come per il codice, solo un Get Lates Version (disponibile anche con i tool di terze parti che stiamo utilizzando) porterà le modifiche committate (non quelle pending) alla nuova workstation. In questo modo, ogni dev è libero di fare le proprie modifiche senza rischiare di intaccare il lavoro altrui. Estendendo, ogni team potrà effettuare il proprio lavoro senza disturbare gli altri.
Già questo comportamento aiuta molto ad evitare conflitti, almeno nel database di sviluppo che poi verrà utilizzato per la promozione del codice nei vari ambienti.
Rimane tuttavia di estrema importanza una figura che comprenda le modifiche che ogni branch porta con sè per determinare, in caso di conflitto, quale sia la decisione da prendere. Una sorta di committer per ogni team per il rilascio delle modifiche e la gestione appunto dei conflitti.
In base al programma determinato per i deploy è inoltre importante capire quando effettuare operazioni di
merge. Nella realtà che sto seguendo, ogni
branch, una volta portata nella
main line, deve essere "mergiata" fin da subito verso le altre linee di sviluppo, il tutto per ridurre al minimo le possibilità di conflitto o di rottura. Ma su questo argomento ci sono vari tipi di approccio, e questo post non vuole essere una documentazione a riguardo
Spostiamoci ora sul TFS. Allo stato attuale non troveremo nulla, in quanto la versione dedicata del database non è ancora stata confermata e inviata al source control server. Un semplice operazione di checkin consentirà di aggiungere il database changeset (insieme di file modificati su db) al source control server.
Ma cerchiamo di fare qualcosa di più, andiamo a creare un backlog item e un paio di task figli per la gestione della tabella di esempio:
Possiamo notare che la nostra iterazione (Sprint 1) ha un solo Backlog Item (
add dbo.Foo Table) il quale a sua volta ha due figli, uno per la creazione della tabella ed uno per il popolamento, guardacaso assegnati al sottoscritto
.
Andiamo ora ad associare il nostro cambiamento al task che ha come id 421, utilizzando Sql Server Management Studio ed il plugin:
Ecco la nostra tabella, notate il commento "Table dbo.Foo created #R421":
#R = resolve
#A = associate
seguiti dall'id del task.
Premendo il pulsante Commit il task 421 verrà chiuso come Done. Infatti nel TFS avremo questo nel task 421:
Aprendo il task, vedremo il changeset legato alla sua chiusura:
Vedere il codice relativo è semplice.. spostiamoci nella parte relativa sul TFS e vediamo cosa contiene il changeset 283:
Si tratta dello script di creazione della nostra tabella all'interno di un file .sql. Fino all'atto del checkin la cartella Tables era vuota.
Se volessimo mettere sotto controllo del codice sorgente anche i dati, è sufficiente legare anche i dati che il tool di terze parti chiama "static":
Con la view successiva è possibile selezionare quali tabelle legare al source control. Selezioniamo appunto l'unica tabella creata. Questa operazione serve per il prossimo esempio.
Anche i dati infatti verranno inseriti con formato .sql sul source control server.
Ipotizziamo ora di lanciare uno script che modifica il tipo del campo FooData e che successivamente inserisce i dati all'interno della tabella:
ALTER TABLE dbo.Foo ALTER COLUMN FooData varchar(100) NULL;
GO
INSERT INTO dbo.Foo ( FooData )
VALUES
( 'ONE' ),
( 'TWO' ),
( 'THREE'
),
( 'FOUR'
),
( 'FIVE'
);
GO
Noteremo una nuova entry nei cambiamenti in sospeso (Commit changes), poichè la tabella dbo.Foo è ora sotto controllo del codice sorgente anche per i suoi dati:
Oltre alla alter, notiamo anche un DATA LINK changeset, dedicato appunto alle insert.
Con questo checkin abbiamo risolto il task 422 e su TFS possiamo vedere le modifiche alla struttura con l'operazione di compare:
e i dati invece sono salvati all'interno della cartella dedicata:
Personalmente non ho mai usato in profondo Visual Studio nelle sue versioni "database" precedenti, perchè l'ho sempre trovato un pochino macchinoso. Vi è da dire però che dal 2012 il tipo di progetto SQL Server è migliorato tantissimo e prima o poi proverò ad approfondire, per evitare comunque prodotti di terze parti e quindi sfruttando la possiblità di avere tutto sul nostro amato editor
.
Se però si è abituati ad usare il Management Studio, che è il tool per eccellenza per fare database server management (integrato ormai nella shell di Visual Studio da tempo), abbiamo visto quanto alcuni plugin siano veramente interessanti per sfruttare appunto sia il source control, sia il continuous integration, sia il deploy.
Con questi semplici esempi abbiamo visto come direttamente da Sql Server Management Studio è possibile gestire la propria branch di sviluppo. Ovviamente con una sola tabella non è così importante, ma immaginate di avere tanti oggetti, tabelle, viste, stored procedure. Quello che non si vede poi ma che è di estrema importanza, è un progressivo che il tool di red-gate aggiunge nei metadati del nostro database per tenere un progressivo di versione e determinare appunto il flusso di ogni deploy, con tanto di alert in caso di mismatch di revisioni.
Con questo plugin, nessun dba o dbdev dovrà separarsi da Management Studio ed il track delle modifiche sarà molto più semplice e comodo, poichè sempre integrato nella shell.
Personalmente ritengo che sia necessario tenere sotto controllo del codice sorgente anche i nostri database. Il mio consiglio è di non dare mai per scontato nulla nemmeno lato db. Durante lo sviluppo, ne trarrete praticamente solo vantaggi, seppure si tratti comunque di un aumento della mole di lavoro da svolgere. Ogni contesto ha le sue necessità ed in realtà enterprise o comunque multi applcazione questo modo di procedere consente non solo il controllo ma anche la possibilità di automatizzare release e migliorarne i piani.
Perchè dico tutto questo? Come può aiutarci un software di controllo del codice sorgente?
Perchè anche il database è codice e quindi, siccome molto spesso va di pari passo con le implementazioni sui vari servizi/applicazioni, deve essere correttamente salvaguardato, versionato e opportunamente splittato, per dare sempre più supporto a chi vuole seguire implementazioni a "team". E tutto ciò va al di là dei database refactoring pattern, seguendo i quali i nostri database sono potenzialmente modificabili "all'indietro" e meno soggetti a regressioni.
Pensiamo al modo di procedere AGILE. Immaginiamo di avere tre team, che lavorano sulla medesima base dati, ma su logiche applicative che hanno sì a che fare tra loro, ma non necessariamente in tutto e per tutto. Si può avere in prima analisi l'illusione che non sarà mai un problema, ma poi sempre il contrario .
Questi team, stanno partecipando a tre iterazioni/sprint differenti, ognuno dei quali ha il suo particolare focus. Ogni team ha le sue due/tre settimane per terminare l'iterazione e dare una review di quanto fatto. Il tutto con necessaria qualità.
Ora immaginate quello che potrebbe succedere se una particolare implementazione toccasse un oggetto che almeno due dei tre team devono cambiare.
Se le modifiche sono incrementali, nessun problema. Al contrario, se esse coinvolgono le stesse informazioni si possono verificare appunto, conflitti, cambi che sovrascrivono modifiche precedenti, regressioni molto gravi ed a volte irreversibili. Irreversibili soprattutto se si parla di dati, iquali non sempre possono essere ricostruiti, dopo una release che cambia la loro struttura/qualità (solo un restore in quel caso può ripristinare i dati ad una particolare situazione, ma si parla di "disaster recovery", un disaster non dovuto a hardware failure, ma, diciamo, logico).
Ecco, quanto abbiamo visto aiuta tantissimo l'organizzazione del team, del codice e dei rilasci, il che riduce, e non di poco, la superficie in cui si può incappare in seri problemi di gestione della vita dell'applicazione.
RISORSE:
Stay Tuned!