ugrás a tartalomhoz

Adatbázisok verziókövetése Liquibase és Liquiface segítségével

Examor · 2013. Szep. 2. (H), 17.32
Adatbázisok verziókövetése Liquibase és Liquiface segítségével

Manapság minden felelős fejlesztő használ verziókövető-rendszereket. Minden kódunk minden változását nyomonkövetjük, felülvizsgáljuk, analizáljuk. Elterjedt gyakorlat, aminek hasznosságát senki nem vonja kétségbe. Miért is annyira nagyszerű dolog ez? Sok egyéb mellett például azért, mert pár másodperc alatt képesek vagyunk visszaállni alkalmazásunk egy korábbi verziójára, legyen az két nappal, vagy akár két évvel korábbi állapot. Ez így nagyon jól hangzik, de tényleg képesek vagyunk rá?

Vegyük a következő élethelyzetet: régóta fejlesztett szoftverünk egy régebbi verziójában felbukkan egy hiba. A feladat egy új kollégához kerül, aki eddig csak a szoftver friss verzióin dolgozott. A verziókövetőből checkout-olja a megfelelő kódot, elindítja az alkalmazást, majd némi fejvakarás közepette szétnéz az irodában: „Srácok, van valakinek ehhez a verzióhoz passzoló adatbázisa?”

Amire ezzel a kis példával rá szeretnék mutatni, az az, hogy az alkalmazás maga több a puszta kódnál, esszenciális része az adatbázis is. Ennek ellenére azt a szigorú fegyelmet, amivel a kódunkat karban tartjuk, sokszor valahogy nem érezzük kötelezően érvényesnek az adatbázisunkra. Pedig az adatbázis verziókövetésének sok előnye van:

  • egy-egy commit-ban a kód változása mellett rögtön látszik, hogy hogyan változott az adatbázisséma;
  • ha minden változás megvan, akkor a referencia-adatbázis bármikor felépíthető;
  • ugyanígy a kód bármelyik verziójához tartozó adatbázis bármikor felépíthető;
  • ha minden változtatást megfelelően standardizált módszerrel tárolunk, akkor az adatbázis frissítése automatizálható.

Ha elfogadjuk, hogy ez nekünk jó, akkor felmerül a kérdés, hogy mégis hogyan is tegyük ezt meg. Ahogy mondani szokás, a lehetőségek tárháza végtelen: az interneten keresgélve sok módszert, leírást, eszközt találhatunk ennek a megvalósítására. Én most csak egy eszközt mutatnék be, ami nálunk jól bevált.

Liquibase

A Liquibase egy ingyenes, nyílt forráskódú, adatbázisfüggetlen változáskövető eszköz, ami jól használható az elterjedt verziókövető eszközökkel. Lényege, hogy a szükséges adatbázismódosításokat nem közvetlenül az adatbázison hajtjuk végre, hanem minden változást struktúrált módon, egy ún. changelog fájlban tartunk nyilván. Ezt a changelog fájlt aztán a Liquibase parancssoros alkalmazás segítségével vagy kódból, a Liquibase Java API-val tudjuk érvényesíteni. Ennek használata a következő előnyökkel jár:

  • a changelog fájl feltölthető verziókövetőbe, ezáltal megvalósíthatjuk adatbázisunk verziókövetését;
  • a Liquibase számon tartja, hogy mely változások kerültek korábban már érvényesítésre adatbázisunkon, és csak a szükségeseket futtatja.

Hogyan is néz ki ez a gyakorlatban?

A megszerkesztett changelog fájlunk (ennek felépítéséről részletesen később lesz szó) futtatása egy Oracle adatbázison például a következőképpen néz ki:

liquibase                                 \
--classpath=lib/ojdbc14.jar               \
--driver=oracle.jdbc.OracleDriver         \
--url=jdbc:oracle:thin:@localhost:1521:XE \
--username=user                           \
--password=secret                         \
--changeLogFile=changelogMaster.xml       \
update

A fenti példában a --classpath-t kivéve minden paraméter kötelező. A --driver paraméter adja meg az adatbázishoz tartozó driver nevét, amit használni akarunk. A classpath az az útvonal, ahol a szükséges fájlokat tároljuk (mint pl. az adatbázis driver-e). A --changeLogFile paraméter adja meg a futtatandó changelog fájl nevét. Az --url paraméter a JDBC adatbáziskapcsolat-sztringje, ami (a --username és a --password paraméterekkel kiegészülve) az adatbázis eléréséhez szükséges minden információt tartalmaz.

Mint látható, futtatáskor elég sok adatot kötelezően meg kell adnunk. Hogy mégse kelljen ezt minden alkalommal begépelnünk, használhatunk egy properties fájlt, amiben ezeket a szükséges információkat felsoroljuk.

# liquibase.properties

classpath:     lib/ojdbc14.jar
driver:        oracle.jdbc.OracleDriver
url:           jdbc:oracle:thin:@localhost:1521:XE
username:      user
password:      secret
changeLogFile: changelogMaster.xml

Ha liquibase.properties-nek nevezzük el, és a Liquibase alkalmazással egy mappába tesszük, akkor futtatáskor automatikusan ezeket az adatokat fogja használni. Más esetben a következő szintakszissal érhető el:

liquibase --defaultsFile=<útvonal>/<fájlnév>.properties update

Ha mindent megfelelően adtunk meg, akkor a Liquibase az első sikeres futtatás legelején létrehoz az adatbázisunkban két táblát DATABASECHANGELOG és DATABASECHANGELOGLOCK néven. Ez az egyetlen alku, amit a Liquibase használatához meg kell kötnünk: erre a két táblára szüksége van mindenképpen. Cserébe az adatbázis tudja magáról, hogy melyik változás lett már futtatva rajta és pontosan mikor.

A changelog fájl felépítése

Eredetileg a changelog fájlok csak XML típusúak lehettek. Ma már a Liquibase támogatja emellett a YAML, JSON és a (speciális kommentekkel ellátott) SQL típusokat is. Jelen cikkben az XML típust használva fogom bemutatni egy changelog fájl felépítését.

A changelog fájl tartalmazza a változásokat (changes/refactorings), amiket végre szeretnénk hajtani. Minden változtatáshoz, amit megtehetünk tartozik egy Liquibase change. Ezeket a change-eket changeSet-ekbe kell szerveznünk. Minden changeSet kötelezően rendelkezik egy id és egy author paraméterrel, valamint tartalmaz egy vagy több change-et. change-ek csak changeSet-en belül létezhetnek.

Egy példán keresztül talán szemléletesebb, hogy is működik ez. Tegyük fel, hogy üres adatbázisunkban létre szeretnénk hozni egy user táblát, id, login és created mezőkkel. Ebben az esetben a changelog fájlunk a következőképpen fog kinézni:

<?xml version="1.0" encoding="utf-8" standalone="no" ?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"
>
    <changeSet author="botond" id="create_user_table">
        <createTable tableName="user">
            <column name="id" type="integer"/>
            <column name="login" type="VARCHAR(255)"/>
            <column name="created" type="TIMESTAMP"/>
        </createTable>
    </changeSet>
</databaseChangeLog>

Ez a példa csak egy a sok támogatott change közül: az alapvető műveletektől, mint tábla/oszlop létrehozása, törlése, az elsődleges és külső kulcsok, különböző megszorítások létrehozásán át egészen szekvenciák, indexek és tárolt eljárások létrehozásáig, módosításáig (és tovább) terjed a lista.

Adattartalom kezelése

A Liquibase nem áll meg a sémadefiníciós utasításoknál. Tegyük fel, hogy az előbb létrehozott táblánkba szeretnénk felvenni egy developer nevű felhasználót, hogy tudjuk tesztelni a fejlesztett alkalmazásunkat. Ezt persze megtehetjük bármelyik adatbáziskezelővel, de ilyenkor a többi fejlesztő is arra kényszerül, hogy kézzel hozza létre a felhasználót. Ehelyett használhatjuk az insert utasítást. Ekkor a changelog fájlunk a következőképpen fog kinézni:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"
>
    <changeSet author="botond" id="create_user_table">
        <createTable tableName="user">
            <column name="id" type="INTEGER"/>
            <column name="login" type="VARCHAR(255)"/>
            <column name="created" type="TIMESTAMP"/>
        </createTable>
    </changeSet>
    <changeSet author="botond" id="insert_developer_user">
        <insert tableName="user">
            <column name="id" value="1" />
            <column name="login" value="developer" />
            <column name="created" valueDate="2013-07-30T16:30:42" />
        </insert>
    </changeSet>
</databaseChangeLog>

Az egyedi insert-ek mellett lehetőségünk van nagyobb adatmigrációra is a loadData és a loadUpdateData utasításokkal. Ezek az utasítások – az oszlopok egyeztetése után – képesek CSV fájlokból feltölteni az adatbázist.

Előfeltételek megadása

Az előbb láttunk egy példát arra, hogy tesztfelhasználót hoztunk létre Liquibase használatával. Igen ám, de mi a helyzet, ha ez az igény csak később jelentkezett? Elkészítettük a táblát létrehozó utasítást, feltöltöttük verziókövetőbe, a fejlesztő kollégák futtatták, és tegyük fel, van, aki már létre is hozott magánál developer nevű felhasználót. Nyilván szeretnénk elkerülni, hogy problémát okozzunk egy újabb developer nevű felhasználó létrehozásával. Szerencsére a Liquibase erre is kínál megoldást, a preConditions elem használatával. Minden changeSet elején definiálhatunk előfeltételeket, hogy milyen esetben futtassa a Liquibase az adott changeSet-et. A mi esetünkben például vizsgálhatjuk, hogy a user táblában van-e már developer nevű felhasználó. Ezzel kiegészülve a changelog fájlunk már így néz ki:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"
>
    <changeSet author="botond" id="create_user_table">
        <createTable tableName="user">
            <column name="id" type="INTEGER"/>
            <column name="login" type="VARCHAR(255)"/>
            <column name="created" type="TIMESTAMP"/>
        </createTable>
    </changeSet>
    <changeSet author="botond" id="insert_developer_user">
        <preConditions onFail="MARK_RAN">
            <sqlCheck expectedResult="0">SELECT COUNT(*) FROM user WHERE login = 'developer'</sqlCheck>
        </preConditions>
        <insert tableName="user">
            <column name="id" value="1" />
            <column name="login" value="developer" />
            <column name="created" valueDate="2013-07-30T16:30:42" />
        </insert>
    </changeSet>
</databaseChangeLog>

Jelen esetben az történt, hogy az sqlCheck elem segítségével azt mondtuk, a fenti lekérdezés futtatásakor a várt érték a 0. Ha ezt az értéket kapjuk, a changeSet futtatásra kerül. Az sqlCheck mellett számos másik elemet is használhatunk, az and és or elemekkel pedig összetett kifejezésekké kombinálhatjuk őket.

Vegyünk észre még egy érdekességet: a preConditions elemnek megadhatjuk az onFail paraméter segítségével, hogy mi történjen, ha a feltétel nem teljesül. Jelen esetben azt mondtuk a MARK_RUN értékkel, hogy ha az előfeltétel nem teljesül, akkor hagyja ki a changeSet-et és jelölje meg futtatottként. Mondhattuk volna azt is, hogy a teljes changelog futtatását szakítsa meg (ez az alapértelmezett), de azt is, hogy ezúttal hagyja ki, de következő futtatásnál próbálja újra.

Környezet

Az előbb láttuk, hogyan írhatunk feltételtől függő change-t fejlesztéshez használt felhasználó létrehozására. De mi a helyzet abban az esetben, ha a feltételünk nem fogalmazható meg ilyen egyszerűen SQL utasítás formájában? Például hogyan tudnánk megoldani, hogy a fejlesztéshez használt felhasználónk csak a fejlesztői adatbázisokon jöjjön létre? Az ilyen helyzetek megoldására létezik a changeSet-ek context paramétere. Ennek segítségével minden changeSet-re külön beállíthatjuk, hogy milyen környezetben szeretnénk, ha lefutna. A changelog fájl futtatásakor pedig (akár parancssorosan, akár Java API-val futtatjuk) megadhatjuk, hogy milyen környezetben vagyunk éppen.

Példánknál maradva tehát, ha szeretnénk egy „development” nevű környzetet kialakítani, akkor changelog fájlunk a következőképpen alakul:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"
>
    <changeSet author="botond" id="create_user_table">
        <createTable tableName="user">
            <column name="id" type="INTEGER"/>
            <column name="login" type="VARCHAR(255)"/>
            <column name="created" type="TIMESTAMP"/>
        </createTable>
    </changeSet>
    <changeSet author="botond" id="insert_developer_user" context="development">
        <preConditions onFail="MARK_RAN">
            <sqlCheck expectedResult="0">SELECT COUNT(*) FROM user WHERE login = 'developer'</sqlCheck>
        </preConditions>
        <insert tableName="user">
            <column name="id" value="1" />
            <column name="login" value="developer" />
            <column name="created" valueDate="2013-07-30T16:30:42" />
        </insert>
    </changeSet>
</databaseChangeLog>

A properties fájlunk pedig így néz ki:

# liquibase.properties

classpath:     lib/ojdbc14.jar
driver:        oracle.jdbc.OracleDriver
url:           jdbc:oracle:thin:@localhost:1521:XE
username:      user
password:      secret
changeLogFile: changelogMaster.xml
contexts:      development

Ha több környzetet is kialakítottunk, futtatáskor vesszővel elválasztva többet is felsorolhatunk. Amelyik changeSet-hez nem adtunk meg környzetet, az minden esetben le fog futni, aktuális környezettől függetlenül. Ha futtatásnál nem adtunk meg semmilyen környezetet, akkor minden changeSet lefut, context paraméterüktől függetlenül.

Adatbázisfüggetlenség

Korábban említettem, hogy a Liquibase adatbázisfüggetlen. Ez így nem teljesen igaz, valójában inkább csak annak tűnik, hiszen több mint 15 adatbázist támogat, többek között MySQL-t, Oracle-t, SQLite-ot, PostgreSQL-t stb. Éppen ez az egyik indok, amiért érdemes az XML formátumú changelog fájlt használni: a Liquibase az éppen használt adatbázis függvényében állítja össze a megfelelő konkrét SQL utasításokat. Így ugyanazt a changelog fájlt tudjuk használni még olyan extrém esetekben is, ha pl. az éles rendszer Oracle adatbázison, a fejlesztői rendszerek MySQL adatbázison, az integrációs tesztek pedig Derby-n futnak.

De nem veszítjük el ezt az adatbázisfüggetlenséget azáltal, hogy oszlopok létrehozásakor konkrét típusokat adunk meg? A válasz az, hogy sajnos de. Hiszen pl. Oracle esetén VARCHAR helyett VARCHAR2 használata javasolt, a PostgreSQL használ BOOLEAN típust, míg sok más adatbázis nem és így tovább. Ám erre a problémára is van megoldás.

A Liquibase a property elem használatával támogatja paraméterek dinamikus behelyettesítését. Ez a gyakorlatban azt jelenti, hogy a környezettől és/vagy adatbázistól függően deklarált property-re a changelog fájlban bárhol hivatkozhatunk a ${<property név>} szintaxissal, akár típusok megadásánál is. Tehát ha pl. Oracle-lel és Apache Derby-vel is kompatibilissé szeretnénk tenni korábbi changelog fájlunkat, akkor azt például a következőképpen tehetjük meg:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"
>
    <property name="integer" dbms="oracle" value="NUMBER(38,0)" />
    <property name="integer" dbms="derby" value="BIGINT" />
    <property name="varchar" dbms="oracle" value="VARCHAR2" />
    <property name="varchar" dbms="derby" value="VARCHAR" />
    <changeSet author="botond" id="create_user_table">
        <createTable tableName="user">
            <column name="id" type="${integer}"/>
            <column name="login" type="${varchar}(255)"/>
            <column name="created" type="TIMESTAMP"/>
        </createTable>
    </changeSet>
    <changeSet author="botond" id="insert_developer_user" context="development">
        <preConditions onFail="MARK_RAN">
            <sqlCheck expectedResult="0">SELECT COUNT(*) FROM user WHERE login = 'developer'</sqlCheck>
        </preConditions>
        <insert tableName="user">
            <column name="id" value="1" />
            <column name="login" value="developer" />
            <column name="created" valueDate="2013-07-30T16:30:42" />
        </insert>
    </changeSet>
</databaseChangeLog>

Mi az, amit a Liquibase nem tud?

A fent leírtak még csak a jéghegy csúcsa: írhatnék még arról, hogy milyen hasznos, hogy a legtöbb changeSet adatbázison történő futtatás után is automatikusan visszavonható a rollback paranccsal. Vagy arról, hogy milyen egyszerű már meglévő adatbázist is Liquibase alapúvá tenni a generateChangeLog utasítással. Vagy hogy futtatáskor jelzi, ha valamelyik changeSet tartalma az utolsó futás óta megváltozott. De ezekre most nem térek ki, mert akinek sikerült felkelteni a figyelmét az eddigiekkel, az valószínűleg ezekre a funkciókra már hamar rá fog találni, ezért inkább rátérnék arra, hogy mik a Liquibase hiányosságai, hátrányai.

Hátránynak lehet esetleg tekinteni, hogy meg kell barátkozni a korábban említett két új tábla létezésével. Emellett ugye meg kell ismerkedni a Liquibase saját „nyelvével”. Ezen kívül más hátrány nálunk nem ütötte fel a fejét, ebből is látszik, hogy gyakorlatilag csak előnyökkel jár a használata.

Ami hiányosságot esetleg lehet említeni, az sem igazán komoly probléma: az XML szerkesztése általában lassúnak érződik. Hiába a megfelelő XSD és IDE használata, típusok értékét például így sem lehet automatikusan kiegészíteni, ráadásul gyakran nagyon hasonló utasításokat kell sokszor egymás után írni, és ilyenkor nagyon csábító a copy-paste használata, amivel könnyű apró hibákat bevezetni. Az itt leírtak lényegében mind visszavezethetőek egyetlen hiányosságra: a Liquibase-nek nincs grafikus felülete. Ez az, amit most megpróbálunk pótolni, ugyanis létrehoztunk egy NetBeans plugin-t, amivel kényelmesen, grafikus felület segítségével használhatjuk a leggyakoribb Liquibase funkciókat.

Liquiface

A Liquiface elnevezés úgy érezzük, jól kifejezi, hogy próbálunk „arcot adni” a Liquibase-nek. A projekt alapötlete pár éve született egy a Webstar Csoport-on belüli 24 órás FedEx nap nevet viselő programozási versenyen, később pedig belső önképzésként folytattuk (Takács Viktor, Marics Tamás és én, Adorján Botond). Az általunk megálmodott széles funkcionalitás sok részére azonban még így sem jutott idő. Szerencsére a cégvezetés is hasznosnak látta a projektet, ezért kaptunk a megvalósításra további erőforrásokat, így végül eljutottunk a most bemutatkozó 1.0-ás verzióig.

Mit tud a Liquiface?

Röviden megfogalmazva igyekszik kényelmi funkciókkal kiegészíteni a Liquibase eszköztárát. Ezt például azzal éri el, hogy a közvetlen XML szerkesztés helyett kényelmesen, varázslók által vezetve hozhatjuk létre a leggyakoribb Liquibase change típusokat. Ennek köszönhetően azok is könnyen megismerkedhetnek a Liquibase hasznosságával, akiknek nincs kedvük vagy idejük a szerkezetét megtanulni.


Új tábla létrehozása

Ennyinél azonban nem álltunk meg: hiszen nem csak az a fontos, hogy képesek legyünk elkészíteni egy Liquibase change-t, legalább annyira hasznos, ha rögtön látjuk is, hogy mit csináltunk. Ezért a Liquiface szimulálja nekünk az adatbázis struktúráját, megjelenítve benne minden táblát és kapcsolatot. Mivel adatbázisunk nagyra nőhet, ezért lehetőség van többfajta szűrésre és bizonyos megjelenítések kikapcsolására is.


Adatbázis megjelenítése Liquiface-en

Ha pedig készen vagyunk a szerkesztéssel, változtatásainkat elmenthetjük új fájlba vagy hozzáadhatjuk már létezőhöz. Emellett a NetBeans-ben definiált adatbáziskapcsolatok segítségével lehetőségünk van arra is, hogy rögtön adatbázison futtassuk a változásokat.


Changelog mentése

A most kiemelt funkciók csak a Liquiface által nyújtott könnyebbségek sarokkövei. Akit részletesebben érdekel, pontosan milyen egyéb képességekkel vérteztük fel, az tovább tájékozódhat a Liquiface használati útmutatóban vagy letöltheti a NetBeans portálról, és maga is kipróbálhatja.

Összefoglalás

Az adatbázisok verziókövetésének általános elveitől a Liquibase eszközön keresztül eljutottunk egészen a Liquiface plugin-ig. Én mindenkit arra biztatnék, aki még nem teszi, kezdje el verziókövetni az adatbázisok változásait is. Az is van olyan fontos, mint a kód, ne kezeljük mostohán! A bemutatott eszközök pedig erre egyszerű és gyors módot szolgáltatnak. Remélem, aki még nem találkozott velük, annak sikerült felkeltenem az érdeklődését! Aki pedig kipróbálja a Liquiface-t, attól azt kérném, segítse a munkánkat: mondja el nekünk véleményét, meglátásait, hogy olyan irányba folytathassuk a plugin fejlesztését, ami minden felhasználójának megelégedésére szolgál!

A bélyegképen Tony Sava fényképe látható.

 
Examor arcképe
Examor
Adorján Botond jelenleg a Webstar Csoport Kft. alkalmazásában álló szoftverfejlesztő. Leginkább Java alapú egyedi webes alkalmazásfejlesztésekkel foglalkozik, szakmai írásai pedig leggyakrabban a http://blog.webstar.hu blogon olvashatóak.
1

Nagyon jó hogy kikerült ide

BlaZe · 2013. Szep. 2. (H), 18.37
Nagyon jó hogy kikerült ide egy ilyen cikk, remélem felkelti a fejlesztők figyelmét, aki még esetleg nem használ db verziózást.

Az elmúlt hónapokban néztünk végig pár adatbázis verziókezelőt, és nálunk is a liquibase tűnt a legkézenfekvőbb választásnak. Versenyben volt még a flywaydb, illetve a RoR és Play megoldása is felvetődött. Végül azonban egyik sem lett, hanem írnom kellett egy sajátot, nem kis erőforrásbefektetéssel :) Lényegében a liquibase ötletére épül, tehát igazából ezzel nem volt baj.

És akkor ami még nekem tetszett:
- Van maven/ant pluginja, integrálható CI rendszerekkel. Ez egyrészt egyszerűbbé teszi a dev környezetek karbantartását, másrészt az elrontott change-ek hamar kijönnek.
- Az utolsó mondathoz tartozik egy fontos tulajdonsága: támogatja a rollbackTestinget is, ami annyit tesz, hogy egymás után végrehajtja az upgrade, rollback, upgrade change-eket egymás után. Ez szerintem egy nagyon hasznos feature.

Nálunk ami hátrányként kijött:
- Produktion db-t elérő gépen ott kell legyen a liquibase. Bármilyen blődül is hangzik ez, vannak környezetek, ahol ez nem megy valami egyszerűen. Nálunk ez volt az a pont, amin a liquibase megbukott, de itt megbukott volna minden egyéb tool is.
- A statement separatorokkal itt-ott trükközni kell, pl az oracle tároltjaival kellett kicsit masszírozni
- Van egy kapcsolója, ami 1 marhanagy sql fileba tolja ki az upgrade és a rollback utasításokat. Itt emlékeim szerint a tranzakciókkal volt egy kis gubanc, de most fejből már nem emlékszem pontosan.

És még pár gondolat a rollbackről. Triviális, de nem minden adatbázis módosítás visszavonható. Tehát pl egy delete table-nek nem valid rollbackje a create table, hiszen a táblában valószínűleg olyan adatok voltak, amik kellettek, és/vagy constraintet sért(enének), ha nincsenek meg. És adott esetben production db-t is kellhet rollbackezni (pl az új verzió bevezet valami bugot). Persze játszótér db-kre önmagában a create table is jó (lehet), de éles adatbázisoknál kicsit körültekintőbben kell eljárni a rollback iránnyal, ebben az esetben pedig már kicsit bonyolult megoldások tudnak születni :)

Mindenesetre nekem bejött a liquibase és én is szívesen ajánlom. A db verziózást meg pláne :)
2

Videó

Drawain · 2013. Szep. 3. (K), 00.32
Ha már Liquibase és Liquiface akkor ne felejtsük el megemlíteni Marics Tamás előadását a pécs.web meetupon, amit ugyanebben a témakörben ejtett meg.
3

Nagyon jó cikk lett, mèg ma

virág · 2013. Szep. 3. (K), 07.44
Nagyon jó cikk lett, mèg ma kipròbalom.
4

Kérdések

Hidvégi Gábor · 2013. Szep. 3. (K), 11.04
Vegyük a következő élethelyzetet: régóta fejlesztett szoftverünk egy régebbi verziójában felbukkan egy hiba. A feladat egy új kollégához kerül, aki eddig csak a szoftver friss verzióin dolgozott.
Ez mennyire reális szituáció? Két verzió párhuzamos futtatása kétszer annyi problémát jelent, és az idő múlásával csak egyre rosszabb lesz.

Megoldható-e következő: egy szerveren belül a teszt- és bétaadatbázisnak saját prefixe van, pl. teszt_adatok, beta_adatok. Ha a teszt_adatokon belül végzünk módosításokat, azt át lehet-e vinni a béta megfelelő tábláiba? Megoldható-e a változások másik szerverre való átmásolása?
5

Ez mennyire reális szituáció?

Poetro · 2013. Szep. 3. (K), 12.03
Ez mennyire reális szituáció? Két verzió párhuzamos futtatása kétszer annyi problémát jelent, és az idő múlásával csak egyre rosszabb lesz.

Tegyük fel, hogy van egy szoftverünk, aminek van egy publikus változata. Ez már kint van fél éve. De készül a következő verzió már 7 hónapja. Viszont jó lenne tudni, hogyan működik az alkalmazás éles adatokkal is. Szerintem ez elég reális.
6

Ez mennyire reális szituáció?

Examor · 2013. Szep. 3. (K), 12.58
Ez mennyire reális szituáció? Két verzió párhuzamos futtatása kétszer annyi problémát jelent, és az idő múlásával csak egyre rosszabb lesz.


Olyan alkalmazás esetén, ami több főverziót is megél, szerintem reális igény lehet régebbi verziók esetén is pl. a kritikus hibák javítása.

Megoldható-e következő: egy szerveren belül a teszt- és bétaadatbázisnak saját prefixe van, pl. teszt_adatok, beta_adatok. Ha a teszt_adatokon belül végzünk módosításokat, azt át lehet-e vinni a béta megfelelő tábláiba? Megoldható-e a változások másik szerverre való átmásolása?


Bevallom, itt nem teljesen értem a kérdést. Ha a teszt- és bétaadatbázis saját prefixe alatt azt érted, hogy külön sémában vannak, akkor a válasz az, hogy minden change típus támogatja séma megadását is.
Ha a teszt_adatokon belül hajtasz végre módosítást, azaz liquibase használata nélkül, valamilyen adatbáziskezelő segítségével módosítod az adatokat, akkor azokat a változásokat a másik adatbázison is ugyanúgy kézzel kell végrehajtanod. Viszont ha a módosításokat liquibase segítségével hajtod végre, akkor egyszerűen annyi a dolgod, hogy ugyanazt a changelog xml-t futtatod a másik adatbázison is.
Ugyanígy oldható meg a másolás is.

Tekints a changelog fájlra úgy, mintha minden egyes sql query-t, ami az adatbázis struktúráját, esetleg néhány fontos adattartalmát módosítja, szekvenciálisan eltárolnád néhány metaadattal együtt.
Plain sql-lel szemléltetve: ha írsz egy create table-t és utána két insertet és ezeket elmented egy sql fájlba, akkor azt az sql szkriptet utána bármilyen adatbázison futtathatod, működni fog. De attól, hogy egy adatbázison futtatsz egy harmadik insertet, az még nem fog bekerülni az előbb megírt sql szkriptedbe.
7

Pontosítok

Hidvégi Gábor · 2013. Szep. 3. (K), 14.04
Tegyük fel, hogy az adataim szét vannak osztva több adatbázisba:
teszt_adat1 db
- tábla 1
- tábla 2
teszt_adat2 db
- tábla 1
- tábla 2

Ezeken végzek módosítást:
ALTER TABLE teszt_adat1.tabla1 ...
ALTER TABLE teszt_adat2.tabla1 ...

Megoldható-e, hogy legenerálja a következőt:
ALTER TABLE beta_adat1.tabla1 ...
ALTER TABLE beta_adat2.tabla1 ...?
9

Létezik a liquibasenek egy

Examor · 2013. Szep. 3. (K), 15.52
Létezik a liquibasenek egy diff utasítása, ez képes két adatbázist összehasonlítani, és szükség esetén changesetet generálni. Ebben az esetben arra azért fontos figyelni, hogy ez jelenleg nem teljeskörű, pl. bizonyos constrainteket ill. tárolt eljárásokat nem figyel, nem generál. Valamint alapértelmezetten az adattartalmat sem figyeli.

Ha így generálunk egy changelog fájlt, update esetén van lehetőség arra, hogy valójában ne futtassa a változásokat, csak írja ki a generált sql-t. Hogy ilyenkor a sémanevet is beleveszi-e, annak utána kellene nézni.
10

Köszönöm a válaszokat!

Hidvégi Gábor · 2013. Szep. 3. (K), 16.05
Köszönöm a válaszokat!
11

Ez mennyire reális szituáció?

BlaZe · 2013. Szep. 3. (K), 20.31
Ez mennyire reális szituáció? Két verzió párhuzamos futtatása kétszer annyi problémát jelent, és az idő múlásával csak egyre rosszabb lesz.
Egy valamirevaló alkalmazásnak több verziója is van. Nálunk van legfrissebb fejlesztői, belső release a tesztelőknek, következő nagy release, és éles. Nagyjából. Értelemszerűen ezt az adatbázis is követi.

Megoldható-e következő: egy szerveren belül a teszt- és bétaadatbázisnak saját prefixe van, pl. teszt_adatok, beta_adatok. Ha a teszt_adatokon belül végzünk módosításokat, azt át lehet-e vinni a béta megfelelő tábláiba? Megoldható-e a változások másik szerverre való átmásolása?

Itt ha adatokra gondolsz, akkor ez a tool nem arra való, hogy migráljon. Ha alter table-re, mint lejjebb írtad, akkor az a lényege, hogy az xmlben írd le a változtatásaidat, és akkor akárhány db-re ki tudod tenni ugyanazt. Ha ilyen tool mellett valaki kézzel masszírozgatja a db-t, az nádpálcás verést kap :)
39

Több verzió vs. continous integration

fberci · 2013. Szep. 15. (V), 11.59
Egy valamirevaló alkalmazásnak több verziója is van.

A continous integration, ill. a continous delivery ezen verziók számának csökkentésén dolgozik (ideális esetben 1-re).
8

Még csak gyorsan átfutottam.

janez · 2013. Szep. 3. (K), 14.49
Még csak gyorsan átfutottam. De gratulálok hozzá, szép munka!
12

Migrációk vs adatbázis verziókezelés

orionstar · 2013. Szep. 3. (K), 22.37
Már egy ideje használok db verziókövetéshez migrációkat, ezekhez képest milyen előnyökkel (hátrányokkal?) jár egy Liquibase-hez hasonló adatbázis verziókezelő használata?
13

Ez az eszköz elsősorban nem

Examor · 2013. Szep. 4. (Sze), 08.48
Ez az eszköz elsősorban nem adatmigrációra, hanem az adatszerkezet verziókövetésére van kitalálva. Bár persze képesek vagyunk vele adatokat is kezelni (egyrészt létezik kifejezetten adatbetöltő change típus, valamint sql tagen belül bármilyen queryt írhatunk), ezeket szerintem érdemes a minimumon tartani, többek között mert rollback esetén problémákat vetnek fel.

Ettől függetlenül a sima sql szkriptekkel szemben megvan az az előnye, hogy az adatbázisban nyílvántartja, mik azok a változások, amik már lefutottak. Ez a gyakorlatban azt jelenti, hogy nem kell keresgélni, hogy melyik az új szkript, amit futtatni kell, ill. nem fordulhat elő olyan probléma, hogy ugyanazt a szkriptet véletlenül többször futtatjuk. Ebből kifolyólag pedig könnyedén automatizálható a változások futtatása: pl. a liquibase-maven-plugin használatával elérhető, hogy az alkalmazás futtatásakor automatikusan futtassa a liquibase changelogot is.
14

Séma migráció is van

orionstar · 2013. Szep. 4. (Sze), 12.08
Az általam használt PHP alapú keretrendszerek (Symphony, Laravel, Codeigniter) séma migrációt is támogatnak. Mivel az egyes módosítások külön-külön fájlokban találhatóak, ezért a verziókezelés is megoldott. Minden migrációs fájl tartalmazza a séma változtatást és annak visszavonásának logikáját. A különféle verziók között(általában) a keretrendszerek parancssorán tudok választani. Ugyanazt a migrációt kétszer nem lehet lefuttatni, mivel nyilvántartja egy migrációs táblában, hogy melyik aktív.
Amit nem tudok megoldani az az adatbázis tartalmának tehát pont az általad említett adatmigrációk normális megvalósítása verziókezelten, amin ezek szerint a Liquibase sem segítene?
17

Nem használtam még az általad

Examor · 2013. Szep. 4. (Sze), 17.08
Nem használtam még az általad említett eszközöket, de az alapján, amit róluk írsz, hasonló logika mentén működnek, mint a Liquibase, szóval valószínűleg hasonló nehézségekbe ütköznél Liquibase használata esetén is.
Mint írtam, léteznek benne lehetőségek az adatok manipulálására, de ezek inkább néhány kritikus adat felvitelére, esetleg tesztadatok készítésére alkalmasak, teljes adattartalom verziókövetésére nem használnám.
38

Adat migráció

fberci · 2013. Szep. 15. (V), 11.59
Én Doctrine Migrations-t használok újabban, ott az adat migráció könnyen megoldható, mert a migrációk egyszerű PHP fájlok, amik szabadon kiegészíthetők bármilyen PHP kóddal, ill. SQL utasítással.
15

Milyen előnyökkel jár ahhoz

inf3rno · 2013. Szep. 4. (Sze), 14.07
Milyen előnyökkel jár ahhoz képest a fapados megoldáshoz képest, hogy dumpolom az adatbázist minden commit-nál, a checkout-nál meg eldobom az aktuálisat, és futtatom a dumpot?

Az egyik előny nyilván az adatbázis függetlenség. Ezen kívül kérdem...
16

Reméltem, hogy valaki

Joó Ádám · 2013. Szep. 4. (Sze), 14.24
Reméltem, hogy valaki felteszi helyettem a kérdést :)
18

Az egyik előny nyilván az

Examor · 2013. Szep. 4. (Sze), 18.24
Az egyik előny nyilván az adatbázis függetlenség. Ezen kívül kérdem...

Akár különböző típusú adatbázisokat, akár ugyanolyan típusú, de különböző adattartalmú adatbázisokat értesz adatbázisfüggetlenség alatt, szerintem mindkét esetben elég nagy előnye van a dumppal szemben. Nálunk van olyan projekt pl. ahol a fejlesztők, a tesztrendszerek és az éles rendszer mind különböző oracle adatbázisokat használnak, miközben az integrációs tesztek memóriában felhúzott derby adatbázison futnak. Ez nem olyasmi, amit dump használatával könnyű lenne elérni. :)
Emellett például az is előny lehet, hogy liquibase esetén a verziókövető egyértelműen tudja jelezni a változásokat, míg mondjuk egy bináris oracle dump esetén csak annyit látsz, hogy változott a fájl.
19

Hát én most pgsql-re

inf3rno · 2013. Szep. 4. (Sze), 19.23
Hát én most pgsql-re fejlesztek, az éles környezeten is ez van, és teljesen jól elvagyok dumppal. A dump mondjuk nem bináris, úgyhogy látom, hogy mi változott, bár nem sokat nézegetem... Integrációs teszteknél maguk a tesztek töltik fel adattal az üres sémát, ami így szintén nem szempont nálam. Nem mondom, hogy ez a mennyország, de ugyanolyan jól működik bizonyos keretek között, mint az általad felvázolt rendszer. Ha ORM-ről van szó, akkor valóban az lehet a nyerő, én most jelenleg tárolt eljárásokkal kísérletezem...
20

Dump vs db verzió toolok

BlaZe · 2013. Szep. 4. (Sze), 20.20
- Az adatbázisra ránézve honnan tudod, hogy épp milyen állapotban van, hogy elindulhat-e rajta a rendszer?
- Hogy állsz vissza előző adatbázisverzióra, ha már futott a rendszer az új adatbázison?
- Hogy oldod meg, hogy bizonyos változások csak bizonyos környezeten hajtódjanak végre? Pl tesztuserek
- Hogyan akadályozod meg, hogy 2x ne fusson rá a db-re ugyanaz a változtatás többszöri futtatásra? (tegyük fel, hogy automatikus deploy van)
- Hogyan teszteled, hogy mely változtatások futnának le az adatbázison anélkül, hogy módosítanád azt?

A liquibase, vagy egy hasonló tool ezekre a kérdésekre ad választ a dump tudásán túl. És még biztos van, ami nem jutott most eszembe.
21

- Hogy állsz vissza előző

Hidvégi Gábor · 2013. Szep. 4. (Sze), 20.40
- Hogy állsz vissza előző adatbázisverzióra, ha már futott a rendszer az új adatbázison?
- Hogy oldod meg, hogy bizonyos változások csak bizonyos környezeten hajtódjanak végre? Pl tesztuserek
Pl. külön teszt és éles adatbázissal.
22

Ezt nem értem miben válasz a

BlaZe · 2013. Szep. 4. (Sze), 21.12
Ezt nem értem miben válasz a problémákra.

Ha vissza kell állni egy verziót az éles adatbázissal, akkor azon nem segít, hogy van teszt db.

Ugyanúgy a másodikra se válasz. Arra az lehet esetleg válasz, hogy van egy másik db csomag is, amit deven, teszt környezetben még lefuttatok, élesen meg nem, ki se release-elek. De ha több ilyen környezeted van, akkor már az is elég macerás.
24

Hát úgy kell fejleszteni,

inf3rno · 2013. Szep. 4. (Sze), 21.26
Hát úgy kell fejleszteni, hogy ne kelljen visszaállni az élessel ;-)
Esetleg tudsz konkrét példát mondani arra, hogy egy rendesen tesztelt rendszerrel vissza kellett állni? Nem hiszem, hogy ez olyan sűrűn előfordulna...
26

Nem emlékszem, hogy lett

BlaZe · 2013. Szep. 4. (Sze), 21.49
Nem emlékszem, hogy lett volna ilyen eddigi pályafutásom során. Olyanok voltak, hogy "á, ezt inkább mégis vedd le", de nem erről beszélünk. A mostani projectben viszont ez kimondottan kérése volt az ügyfélnek, és jogos is. És nem is ez az első hely, ahol ezt hallottam felmerülni. Vannak olyan projectek, ahol kell az azonnal előkapható B terv, mert minden perccel durván nő a baj. Lásd Knight Capital :) Mondjuk rajtuk már a downgrade se segített volna, mire észrevették, hogy baj van.

De remélem itt se kell egy tervezetlen downgrade-et megélnünk, mert az minden szempontból horror lenne :)
28

Performanciát általában nem

tgr · 2013. Szep. 4. (Sze), 21.57
Performanciát általában nem tudsz megbízhatóan tesztelni, legfeljebb becsülni. Ha kitolod az új feature-t, és öt perc múlva rohan át a rendszergazda, hogy egekben a load, akkor elég jól jön, ha van egy rollback gomb, és nem kell nekiállni röptében optimalizálni, miközben az éles szerver épp térdel lefele.
29

Ja, én is erre gondoltam,

inf3rno · 2013. Szep. 4. (Sze), 23.43
Ja, én is erre gondoltam, mint lehetséges forgatókönyv... Viszont az ilyen esetek is elkerülhetőek, ha elég rugalmas a rendszer. Pl az új feature letiltható, vagy beállítható, hogy csak a felhasználók egy bizonyos részénél fusson, amíg tesztelitek, hogy mennyire erőforrás igényes. Kérdés, hogy az ilyesmi megéri e a ráfordított energiát...
32

A Facebook jelenleg talán a

Joó Ádám · 2013. Szep. 5. (Cs), 04.35
A Facebook jelenleg talán a leglátványosabb alkalmazója ennek a kiadási stratégiának.
35

Feature switcheket sok nagy

tgr · 2013. Szep. 5. (Cs), 06.45
Feature switcheket sok nagy cég használ, de éppen az adatbázisra nem alkalmazhatóak, mert abból jellemzően csak egy van (lehet sok DB szerver, de általában valamilyen master-slave felállásban, ugyanazzal a sémával). De ha nem így lenne, akkor csak még jobban kellene a migráció rollback kezelés, mert ha teszteled az új feature-t 1% forgalmon, és nem jön be, akkor azt az 1%-ot valahogy vissza kell állítani a szoftver előző állapotára, szóval a feature switchektől éppenhogy gyakoribbá válik a rollback, mert megengedheted magadnak, hogy ne teszteléskor kapd el a nem végzetes hibákat, hanem (félig) élesben.
23

Külön teszt és külön éles

inf3rno · 2013. Szep. 4. (Sze), 21.24
Külön teszt és külön éles adatbázis van, a teszt adatbázisnál a schema-t minden checkout legyalulja, aztán ráteszi az akkori állapotnak megfelelőt. Az integrációs tesztek a setup-nál legyalulják az adatokat, és felteszik, amik éppen nekik kellenek. Éles környezetet nem használnék soha tesztelésre. Ha minden okés, akkor az éles szerverre felteszem az adatbázis módosításait kézzel, és push-olom az új kódot. Csapatban be lehet iktatni még egy dev szervert a merge conflict-ok, stb... megoldására, aztán ott is futtatni az integrációs teszteket, és csak utána feltenni élesre, ha valaki átnézte, hogy minden okés. Elsimerem, hogy vannak előnyei annak, ha mindezt automatizálom, de jelenleg nem dolgozom csapatban, és nem érzem úgy, hogy megérné a befektetett időt a megtanulása. Ha csapatban dolgoznék, akkor meg valószínű, hogy egy nálam hozzáértőbb emberre bíznám ennek az egésznek a kiépítését.
25

a teszt adatbázisnál a

BlaZe · 2013. Szep. 4. (Sze), 21.38
a teszt adatbázisnál a schema-t minden checkout legyalulja, aztán ráteszi az akkori állapotnak megfelelőt. Az integrációs tesztek a setup-nál legyalulják az adatokat, és felteszik, amik éppen nekik kellenek.
Erre mondjuk a manuális tesztelők lehet morognának :) Meg ha integrációs környezezetről van szó, akkor a többi csapat is.

Éles környezetet nem használnék soha tesztelésre.
Isten ments :)

jelenleg nem dolgozom csapatban, és nem érzem úgy, hogy megérné a befektetett időt a megtanulása
Kb fél napom ment rá az alapok átnézésére, amikor találkoztam vele. Onnantól gyakorlatilag tudtam használni. Én az egyedi fejlesztésekre is inkább ezt használnám, nem csak csapatban. Szerintem megéri azt a pár órát az ismerkedés, csomó kézimunkát levesz az emberről.
30

No majd belenézek.

inf3rno · 2013. Szep. 4. (Sze), 23.44
No majd belenézek.
40

Leírtad a legnagyobb problémát

fberci · 2013. Szep. 15. (V), 12.00
Ha minden okés, akkor az éles szerverre felteszem az adatbázis módosításait kézzel, és push-olom az új kódot.

Ez a lépés egy rizikó faktor, mert 1) nincs verziókezelve a kód 2) manuálisan kell végrehajtani a folyamatot (hiba jöhet elő, nem lehet pontosan a korábban már tesztelt folyamatot futtatni). Persze a legtöbb probléma csapat esetén jön elő.
27

Mekkora rendszerre

tgr · 2013. Szep. 4. (Sze), 21.51
Mekkora rendszerre fejlesztesz? Háromszáz rekordnál ez is jól működik, háromszázmilliónál nem biztos, hogy elfogadható megoldás, hogy gond esetén leáll az oldal egy napra, amíg töltöd vissza az előző dumpot.

Plusz ha egynél több fejlesztő dolgozik az oldalon, akkor a végső commitot nem feltétlenül az tolja, aki a feature-t fejlesztette. Pl. az egyik fejlesztő megcsinálja az A feature-t, amihez át kell neveznie pár oszlopot, a másik megcsinálja a B featuret, amihez módosít egy mezőtípust, aztán a release manager feladata lesz az A és B brancheket, meg még másik harminchármat összemergelni és tesztelni a végeredményt, amihez nyilván egyik fejlesztő DB dumpja sem megfelelő, és ha neki kézzel kell reprodukálnia az összes sémamódosítást, akkor jövő ilyenkorra se lesz új release.

Plusz ha van egy gép, amin az automatizált tesztek futnak, arra nagyon nem szeretnél bizonytalan eredetű és tartalmú adatbázisdumpokat feltölteni, mert utána már fogalmad sem lenne, hogy mit is tesztelsz tulajdonképpen.
31

Ja, hát kis rendszereket

inf3rno · 2013. Szep. 4. (Sze), 23.46
Ja, hát kis rendszereket csinálok jelenleg, itt működőképes. Nagyban már kevéssé. Mennyire jellemző ez, hogy az adatbázis struktúrában turkálnak egyszerre többen egy csapatban? Azt hittem, hogy azt előre meg szokás tervezni...
33

Mint közös külső erőforrás,

Joó Ádám · 2013. Szep. 5. (Cs), 04.38
Mint közös külső erőforrás, logikus volna, hogy egy emberhez tartozzon az adatbázis, mint komponens…
34

Szerintem ritka, hogy

tgr · 2013. Szep. 5. (Cs), 06.40
Szerintem ritka, hogy egyszerre csak egy olyan feature legyen előkészületben, ami sémamódisítást igényel, úgyhogy egyáltalán nem ritka, hogy 3-4 ember is párhuzamosan adatbázist túr mondjuk egy tízfős csapatban. (Kis-közepes cégeknél ráadásul tipikusan nincs continuous deployment, mert ahhoz azért komolyabb architektúra és szervezet kell, hanem pár napos release ciklusok vannak, tehát még össze is torlódnak a feature-ök.) Hogy van-e valami közös tervezési fázis az adatbázismódosítások előtt, az ettől független kérdés; az én tapasztalatomban nem, de ha lenne is, akkor is különböző emberek fogják kivitelezni, és biztosan nem tervezik meg előre a konkrét SQL utasítások szintjéig.
36

Ha összeakadnak a

inf3rno · 2013. Szep. 5. (Cs), 14.06
Ha összeakadnak a módosítások, akkor van merge conflict az ilyen típusú rendszerekben is, vagy totál szétesik az egész?
37

Szétesik (illetve a későbbi

tgr · 2013. Szep. 5. (Cs), 17.07
Szétesik (illetve a későbbi felülüti a korábbit). De ez a gyakorlatban nem annyira probléma, mert ha két ember ugyanazokon a DB oszlopokon dolgozik, akkor a kódnak is ugyanazon a részén (vagy legalábbis erősen összefüggő részeken) dolgoznak, akkor meg nyilván tudnak egymásról (vagy ha mégsem, akkot nem a DB verziókövető rendszer szintjén van a probléma).
41

Több adatbázismotor

fberci · 2013. Szep. 15. (V), 12.01
pl. az éles rendszer Oracle adatbázison, a fejlesztői rendszerek MySQL adatbázison, az integrációs tesztek pedig Derby-n futnak.

Az logikus, hogy az integrációs tesztek mondjuk egy in-memory adatbázison futnak, de az hogy a fejlesztői rendszer és az éles adatbázis különböző legyen, számomra elég fura. Sajnos messze vagyunk attól, hogy az adatbázismotorok egymással csereszabatosak legyenek. Így viszont egyes bugok lehet, hogy csak prodban jönnek ki. Ha feltesszük, hogy a QA is Oracle-t használ, akkor jobb a helyzet, de ekkor a fejlesztő még mindig nem tudja debuggolni a hibát. De még ha nincs is bug, az adatbázisok teljesítménye alap esetben is különböző, a fejlesztőnek így nincs semmi esélye a teljesítmény tesztelésre.
42

Simán lehet memory db-en

inf3rno · 2013. Szep. 15. (V), 12.43
Simán lehet memory db-en lefejleszteni a projectet, mert jóval gyorsabban mennek rajta az integrációs tesztek, mint bármelyik relációs adatbázison. Utána ha átmegy dev szerverre a kód, akkor már lehet az élesnek megfelelő adatbázisra is tesztelni.
43

És a production Oracle-höz

Joó Ádám · 2013. Szep. 15. (V), 13.09
És a production Oracle-höz járnak dev licencek is?
44

Tudtommal fejlesztésre

H.Z. · 2013. Szep. 15. (V), 18.29
Tudtommal fejlesztésre ingyenes, csak nem jár hozzá support, ergo kitörölheted vele, mert support híján patch-eket sem kapsz, a letölthető szerverek meg olyan bugosak, hogy örülhetsz, ha egyáltalán elindulnak. ;)

Persze nem kizárt, hogy ez is megváltozott az elmúlt években.
46

Dev licenc nem jár, de

Examor · 2013. Szep. 16. (H), 09.01
Dev licenc nem jár, de használható az Oracle XE kiadás. Ingyenesen letölthető, fejlesztésre szabadon használható. Persze teljesítményben nem hozza a production szintjét, és vannak egyéb megkötések is, de nálunk eddig bevált, ha Oracle DB-re kellett fejleszteni.
47

Lusta vagyok utánanézni, de

H.Z. · 2013. Szep. 16. (H), 10.58
Lusta vagyok utánanézni, de nem úgy van, hogy az Oracle XE bármire használható, míg a "nagy" RDBMS csak fejlesztésre?
(előbbi lehet, hogy csak non-profit környezetben)
45

A felhozott példa szerencsére

Examor · 2013. Szep. 16. (H), 08.56
A felhozott példa szerencsére nem életből kiragadott, csak próbáltam szemléltetni annak az előnyét, hogy ugyanaz a liquibase changelog használható különböző adatbázisokon. Szóval ezt senki ne tekintse követendő példának! :)
48

Mennyire kompatibilis ez a

inf3rno · 2014. Feb. 4. (K), 12.20
Mennyire kompatibilis ez a liquibase gittel?

Olyan dolgokra gondolok, hogy pl. több ágat összemergelni, és merge conflictot feloldani a changelog-ban, vagy checkout-nál automatikusan átalakítani az adatbázis szerkezetét (és esetleg adatait) a branch-nek megfelelő formára, stb... Az egy dolog, hogy lehet committálni a változásokat, de ha nem változik minden automatikusan a kóddal együtt, akkor elég nehézkes lesz a használata...
49

Olyan dolgokra gondolok, hogy

BlaZe · 2014. Feb. 4. (K), 19.54
Olyan dolgokra gondolok, hogy pl. több ágat összemergelni, és merge conflictot feloldani a changelog-ban
Ezt nem teljesen értem. Text file, össze lehet merge-ölni egyszerűen, mint egy forrást. Mire gondolsz itt pontosan?

vagy checkout-nál automatikusan átalakítani az adatbázis szerkezetét (és esetleg adatait) a branch-nek megfelelő formára, stb... Az egy dolog, hogy lehet committálni a változásokat, de ha nem változik minden automatikusan a kóddal együtt, akkor elég nehézkes lesz a használata...
Ezt miért szeretnéd? És hol, ki által kicheckoutolva? Írhatsz scripteket, amivel eldobod a db-t és újra felhúzod, ha branchet váltasz. Deves db-nél ez működhet. De azt el kell felejteni, hogy a DB - mint érzékeny adatot tároló "valami" - alkalmas arra, hogy csak úgy egy toollal rángassuk jobbra-balra különböző verziók között, mert az adatvesztéssel fog járni. A liquibase ezt támogatná, de ez a gyakorlatban kivitelezhetetlen. Nálunk úgy néz ki a process, hogy ha a prod deploy elszáll, vagy futás közben derül ki, hogy valami baj van, akkor a rollbacknek automatikusan kell működjön (az érintett táblákról teljes másolat készítésével), utána "manual data reconciliation" következik, a devet bevonva. Szerencsére eddig ilyen nem történt, és remélem soha nem is fog :)
50

Ezt miért szeretnéd? És hol,

inf3rno · 2014. Feb. 5. (Sze), 13.17
Ezt miért szeretnéd? És hol, ki által kicheckoutolva?

Ha nem lett volna egyértelmű, akkor dev adatbázisról van szó, amin az integrációs teszteket futtatod, és nem élesről... Elvileg egy post-checkout hook-kal megoldható, de ha az nem menne, akkor az integrációs tesztek futtatása előtt a changelog-ról készített hash ellenőrzésével is ki lehet deríteni, hogy tényleg a jelenlegi kódhoz tartozó adatbázis szerkezetről van e szó.

Ezt nem teljesen értem. Text file, össze lehet merge-ölni egyszerűen, mint egy forrást. Mire gondolsz itt pontosan?

Pontosan arra, hogy össze lehet e mergelni egyszerűen.
51

Ha nem lett volna egyértelmű,

BlaZe · 2014. Feb. 5. (Sze), 21.06
Ha nem lett volna egyértelmű, akkor dev adatbázisról van szó, amin az integrációs teszteket futtatod, és nem élesről... Elvileg egy post-checkout hook-kal megoldható, de ha az nem menne, akkor az integrációs tesztek futtatása előtt a changelog-ról készített hash ellenőrzésével is ki lehet deríteni, hogy tényleg a jelenlegi kódhoz tartozó adatbázis szerkezetről van e szó.
Ennél jobban jársz, ha maga a teszt gyártja le magának az adatbázist, majd miután végzett, letörli (lefuttatja a rollbackeket, ha sikeres volt, meg ha úgy állítod be). Tud a liquibase dropAll-t, de a package-eket, stored procedure-öket nem takarítja el maga után.
52

Imho baromi lassú, ha a teszt

inf3rno · 2014. Feb. 5. (Sze), 22.16
Imho baromi lassú, ha a teszt hozza létre minden egyes alkalommal az összes táblát meg a stored procedure-öket... A db schema-t pont elég egyszer létrehozni, a teszteknek meg elég, ha az adatokat megfelelően manipulálják...
53

Miért lassú? Vagy mi lassú?

BlaZe · 2014. Feb. 6. (Cs), 21.34
Miért lassú? Vagy mi lassú? 10-20 másodperc alatt már azért egész komoly adatbázis felmegy egy mai átlag gépre is (nyilván nem baromi sok adattal).

Hogy biztos legyen, hogy egyről beszélünk, leírnám én mi alatt mit értek :)

Unit testing: Olyan teszt, ahol célzottan a kód egy részét teszteljük (class, method stb), körülötte mindent kimockolva. DB-t is, így ez nem érdekes most. Ez jellemzően a fejlesztői gépen, vagy CI serverben, a build részeként történik.

Component testing: Ugyanaz, mint a unit teszt, csak real objectekkel. A kichekoutolás miatt én arra tippelek, hogy valójában erre gondolsz. Itt jellemzően nulláról alá tolunk egy db-t (sok esetben valami in-memoryt).

Integration testing: Telepített környezetet tesztelünk, ahol az alkalmazás minden része össze van kötve, a db is alatta van stb. Fontos, hogy ezekben a környezetekben már jellemzően manuális teszt is zajlik, ezért nem illik csak úgy legyilkolni tesztadatokat. Ez visszavezet ahhoz amit az elején is mondtam, hogy csak úgy különböző verziók között rángatni a db-t nem lehet, mert igen nehéz megoldani, hogy egy A->B->A verzióváltás ne járjon adatvesztéssel (gyakorlatilag nem lehet).

A legtöbb alkalmazásnál az működik, hogy van egy környezet a prodban épp kint lévő verziókkal, az esetleges patcheket ebben a környezetben teszteljük. Illetve van egy másik, ahol a következő tervezett prod verzió tesztelése zajlik. Ennek megfelelő db verziókkal, amik ezek miatt mindig egy irányba (előre) közlekednek. Meg persze a fejlesztői környezetek, snapshot verziókkal, ahol szabadabban lehet rugdosni a db-t is.

Ahol különböző ügyfeleknek különböző ütemben, eltérő feature settel release-elgetjük ugyanazt az appot, ezért N branch kell egyszerre... Az már sok kérdést felvet, pl hogy jó-e, fenntartható-e ez a modell :)

Természetesen nem jár rituális kivégzéssel, ha esetenként ezek a határok összemosódnak :), pl nekünk van olyan integrációs tesztünk, ami telepített, de mockos környezetben fut. De ami fontos, hogy a tesztek mindig konkrét, előre definiált adatokkal történnek, beleértve a db-t is, azokat a teszt a setup fázis alatt összeállítja magának, majd lefut a teszt, lefutnak az assertek, a teardownban pedig eltávolítja ezeket. Máskülönben nem lesz megbízható a teszt.

Jó hosszúra eresztettem... A lényeg, hogy ahogy látszik is, sokkal hamarabb fogsz koncepcionális problémába ütközni, minthogy egy jó tool (a liquibase szerintem az) megengedné-e, hogy jobbra-balra tekergesd a db-t.
54

Nekem ha bármi lassabb, mint

inf3rno · 2014. Feb. 7. (P), 05.40
Nekem ha bármi lassabb, mint 2-3 másodperc, az már lassú.

Nálam az az integrációs teszt, ami megmutatja, hogy a programod jól illeszkedik a környezetébe. Akár még az aktuális programnyelv beépített feature-eihez is csinálhatsz integrációs tesztet, ha biztos akarsz lenni, hogy jól működnek, és nincsen self-test és/vagy gyenge a dokumentáció hozzájuk... (Egyébként ezzel rengeteg időt lehet spórolni bármilyen verzió váltásnál vagy db migrációnál...)

Az én esetemben azért számít a sebesség, mert nem szívesen írok unit teszteket az adatbázis nyelvén, amikor stored procedure-öket, view-okat tesztelek. Inkább integrációs tesztekkel szoktam megnézni, hogy minden okés e. TDD nélkül neki se lehet kezdeni stored procedure írásának, mert annyira rosszul debuggolható... Egyébként is el lehetne felejteni a debug-ot, a TDD sokkal egyszerűbb...
55

A liquibase előnyeinek nagy

inf3rno · 2014. Május. 15. (Cs), 04.09
A liquibase előnyeinek nagy része elveszik, ha event storage-et használ az ember. Annál ugyanis a read cache a relációs adatbázis, és annak a szerkezete deploy esetében bármikor módosítható, az adatok meg visszaállíthatóak egy event replay-el. Nagy mennyiségű adatra ez a megoldás nem feltétlen kielégítő (sebesség tesztek kellenek), viszont kis mennyiségű adatnál teljesen jól működik. A migrációval szintén ugyanez a helyzet. Egyébként teljesen ugyanazon az elven működik az event storage, event replay, mint amit a liquibase is használ, szóval hogy a változásokat mentjük, nem pedig az aktuális állapotot. Ez utóbbi csak a read cache-be megy...

A liquiface-el használva működőképes megoldás lehet a liquibase, viszont magában nekem nem szimpatikus, nem szeretek config fájlokat gépelni, inkább kattintgatós típus vagyok.
56

Közben jobban

inf3rno · 2015. Szep. 18. (P), 18.41
Közben jobban kikristályosodott. A klasszikus active record-os orm-es monolit alkalmazásokhoz (amilyen a legtöbb) a liquibase a jó választás, mert event storage nem használható domain event-ek híján. A DDD-vel fejlesztett alkalmazásokhoz, ahol domain model van, ott viszont értelemszerűen event storage-et kell használni, és nem liquibase-t. Az olvasási adatbázis az event storage alapján bármikor újra felépíthető, és akár még módosítható is a szerkezete, típusa (pl mysql helyett mongodb), szóval ebben az esetben nincs értelme azt verziózni.
57

Miért nincs?

BlaZe · 2015. Szep. 20. (V), 21.05
Miért nincs? Hogyan tudod máskülönben megoldani, hogy az alkalmazásod különböző verziói különböző struktúrájú adatbázissal dolgoznak? Verziózni kell. Az adatmigrációt megúszhatod verziók között, illetve egész pontosan más forrásból migrálsz (event storage a db helyett). Ez vagy jobb, vagy nem. Pl ha van egy rendszered, ami másodpercenként 1000 requestet szolgál ki, jó eséllyel nem fogsz akarni replayezni :)
58

A schema szerkesztését meg

inf3rno · 2015. Szep. 21. (H), 03.43
A schema szerkesztését meg tudod csinálni mondjuk a doctrine yml (vagy bármi hasonló schema leíró fájl) átírásával, aminek a változásait a git ugyanúgy mutatja két verzió között és mivel ORM-ről van szó, ugyanúgy SQL dialect független a dolog. A liquibase ennél a cikk alapján annyival tud csak többet, hogy van changelog-ja. Az event storage-nál nincs szükség a changelog-ra, mert az adatokat replay-el töltöd vissza (a régi adatbázis törölhető, esetleg megtartható az átmenet idejére, vagy a/b tesztelésre is - több microservice verzió is futhat akár ugyanannál a projektnél egyszerre).

Az event storage-es megoldás és a kódból történő schema generálás annyiból is előnyösebb, hogy tovább lehet szélesíteni a támogatott adatbázisok körét, pl mongoose-nál vagy hasonló megoldásoknál is van egy struktúra, ami szerint rendezed az adatokat, és ez a struktúra változhat verziónként, ami szintén az adatbázis tartalmának átalakításához vezet. A liquibase csak SQL-t támogat, szóval egyáltalán nem silver bullet.

A ddd és event storage mellé cqrs, eventual consistency és microservices dukál, ami sok apró adatbázissal operál, amikre projection-el megy fel az adat. A projection-ök szűrnek domain event típusra ezért egy-egy microservice-re vonatkozó replay-kor csak a domain event-ek töredékét dolgozzák fel a microservice-hez tartozó projection-ök. Szóval a nagy adatbázis nem annyira probléma, mint elsőre tűnik, meg egyébként is, amelyik projekt ilyen technológiát használ, annál érzésem szerint nem gond a vas.

Persze továbbra sem vitatom el, hogy a monolit alkalmazásoknál nagyon hasznos a liquibase. Projekt méret függő a dolog, hogy melyik utat választod, sőt azt mondják, hogy érdemes monolittal kezdeni, és később refaktorálni ddd-re, ha már akkor a projekt, hogy kezelhetetlen. Én azért tanulgatom továbbra is a ddd-t, bár nem valószínű, hogy találkozni fogok akkora méretű projekttel valaha is, aminél indokolt lenne a használata, de ha ez a szoftverfejlesztői szakma jelenlegi csúcsa, akkor nagyon is érdekel... :-)
59

Én értettem elsőre is, hogy

BlaZe · 2015. Szep. 21. (H), 21.28
Én értettem elsőre is, hogy mire gondoltál. Arra próbáltam rávilágítani, hogy vannak olyan esetek, amikor a replay egyszerűen nem opció. Például rengeteg event, aminek a migrációja a kódbázison keresztül kivárhatatlan sok ideig tartana, összehasonlítva pl egy update-tel. Vagy ha rollbackelni kell és változott az event model. A régi kódbázis mit fog tudni kezdeni az újabb típusú eventekkel?

Érdemes még megemlíteni, hogy a liquibase és a hasonló eszközök inkrementális verziózást tesznek lehetővé, vagyis alkalmasak korábbi verzión lévő adatbázis bármilyen későbbi verzióra migrálására. És ugyanezt visszafelé is. Továbbá a change-eidre tudsz CI-ból pl upgrade-rollback teszteket futtatni stb. Ez azért lényegesen több, mint amit egy forrás szintű verziókezelővel önmagában el tudsz érni.

Mi használunk domain eventeket és DB-t is, ugyanazon projecten belül. Mindkettő tökjó dolog, de azért ezek nem feltétlenül egymást helyettesítő technológiák.
60

Ennyi erővel egy új

inf3rno · 2015. Szep. 22. (K), 10.19
Ennyi erővel egy új microservice fellövésénél is kivárhatatlanul sokat kell várnod, amíg végigpörgeti az addigi eseményeket. Annál nincs ilyen probléma?!
Azt elismerem, hogy egy alter table gyorsabb, és ha gyorsan kell valami, akkor nem válogatunk az eszközökben.
Rollback-nél attól függ, hogy meddig tartod meg a régi adatbázist, ha sokáig, akkor elég csak a friss eventeket felvinni rá.
Szvsz, kisebb részben vannak struktúrális változások, és gyakrabban kerülnek bele új eventek és property-k a rendszerbe. Nyilván ezeket nem lehetnek visszafele kompatibilisek. A régi kódbázis jó esetben ezért figyelmen kívül hagyja őket. Ha struktúrális változás történt, pl szétszedtél egy nagy eventet több kicsire vagy átneveztél egy eventet, akkor megoldás lehet az event storage migrációja, és közben a régi és új eventek közötti átalakítás. Ez időigényes lehet, úgyhogy jobb elkerülni. Egyébként ugyanez a kérdés felmerül a liquibase-nél is, mi van akkor, ha a régi rendszerben néhány oszloppal kevesebb volt egy táblában? Törlődnek az oszlopok, és elveszik az adat?
Én nem értek egyet, szerintem helyettesíthetik egymást, de nem muszáj mindenben egyetértenünk. :-)
61

Ennyi erővel egy új

BlaZe · 2015. Szep. 24. (Cs), 22.52
Ennyi erővel egy új microservice fellövésénél is kivárhatatlanul sokat kell várnod, amíg végigpörgeti az addigi eseményeket. Annál nincs ilyen probléma?!
De, nyilván van. Nagyobb adatmennyiséggel dolgozó rendszereknél ilyet nyilván nem is csinálnak.

Rollback-nél attól függ, hogy meddig tartod meg a régi adatbázist, ha sokáig, akkor elég csak a friss eventeket felvinni rá.
Rollbacknél az a nehéz, hogy az újonnan érkezett adatokat kell visszamigrálnod egy régi szerkezetbe. Ez mindenképpen migrációval jár, ha a verziók között ezt igénylő változás volt. Akár DB, akár replay van.

Egyébként ugyanez a kérdés felmerül a liquibase-nél is, mi van akkor, ha a régi rendszerben néhány oszloppal kevesebb volt egy táblában? Törlődnek az oszlopok, és elveszik az adat?
Ennek semmi köze a liquibase-hez. De nem, az ökölszabály az, hogy adatot nem törlünk soha.

Én nem értek egyet, szerintem helyettesíthetik egymást, de nem muszáj mindenben egyetértenünk. :-)
Az adatbázis nagymennyiségű adat tárolására és relációk mentén történő lekérdezésre való. A DB verziózása gyakorlatilag ezen DB verziók közötti migrációját jelenti, explicit módon definiált szabályok mentén. Az event replay pedig a történelem visszajátszására való, amiben korábbi eseményeket tudsz újra (vagy épp először) feldolgozni valamilyen verziójú és helyességű alkalmazáslogikával. Itt ami kiemelendő, hogy előbbi esetben te kontrollálod a migrációt, általad definiált szabályok mentén, utóbbi esetben pedig az alkalmazáslogikára vagy utalva, annak minden bugjával együtt. Ezt végiggondolva nem kérdés, hogy minőségbiztosítás szempontjából melyiket érdemes választani. Lehet mindenféle migráció validációs scriptekkel utána nekimenni a replay alapján előállt új adatbázisnak és összevetni a régivel. De ott sokkal nagyobb az emberi hibázási lehetőség. Ha valamire nem gondolsz, akkor potenciálisan elrontod az adataitad. És akkor most a korábban említett adatmennyiségből eredő időigény különbséget nem említettem. Valamint azt sem, hogy mennyire megnöveli a komplexitást, ha utólag kell sok forrásból érkező eventeket összefésülni úgy, hogy logikailag helyes eredményt adjon bármelyik verzióval.

Mondom mindezt úgy, hogy a pet projectem milliárdos nagyságrendű események visszagörgetésén alapul.