Aldrig har det været, og nu er det igen!
I et nyt projekt besluttede vi at bruge Liquibase fra starten for at undgå problemer i fremtiden. Det viste sig, at ikke alle unge teammedlemmer ved, hvordan man bruger det korrekt. Jeg afholdt en intern workshop, som jeg derefter besluttede at gøre til en artikel.
Artiklen indeholder nyttige tips og beskriver tre af de mest åbenlyse fælder, man kan falde i, når man arbejder med værktøjer til migrering af relationelle databaser, især Liquibase. Den er rettet mod Java-udviklere på junior- og mellem-niveau; mere erfarne udviklere kan finde den interessant til strukturerings- og repetitionsformål for det, der sandsynligvis allerede er kendt.

Liquibase og Flyway er de primære konkurrenceprægede teknologier til kontrol af versioner af relationelle strukturer i Java-verdenen. Den første er fuldstændig gratis, og i praksis vælges den oftere til brug, så det er netop Liquibase, der er valgt som fokus for denne publikation. Ikke desto mindre kan nogle af de beskrevne praksisser være universelle, afhængigt af din applikations arkitektur.
Migrering af relationelle strukturer er en nødvendighed for at bekæmpe den begrænsede fleksibilitet i relationelle datalagre. I den tid, hvor OOP var på mode, indebar måden at arbejde med databaser på, at vi engang beskrev skemaet og ikke ville røre det igen. Men virkeligheden er altid sådan, at alt ændrer sig, og ændringer i tabelstrukturer er nødvendige ret ofte. Naturligvis kan processen i sig selv være smertefuld og ubehagelig.
Jeg vil ikke gå i dybden med teknologien og instruktionerne til at tilføje biblioteket til dit projekt; der er skrevet tilstrækkeligt om dette emne:
Derudover har der allerede været en fremragende artikel om nyttige tips:
Tips
Jeg vil gerne dele mine råd og kommentarer, der er opstået gennem sved, blod og smerte ved løsning af migrationsproblemer.
1. Før du begynder, bør du gøre dig bekendt med afsnittet om bedste praksis på Liquibase
Der er beskrevet enkle, men meget vigtige ting, uden hvilke brugen af biblioteket kan komplicere dit liv. For eksempel vil en ustruktureret tilgang til håndtering af changesets før eller senere føre til forvirring og brudte migrationer. Hvis ændringer i databasen og logikken i tjenesterne, der afhænger af hinanden, ikke udføres samtidig, er der stor risiko for, at dette vil føre til fejltagelser eller ødelagte miljøer. Desuden indeholder anbefalingerne for brug af Liquibase på den officielle hjemmeside et punkt om udvikling og test af rollback-scripts sammen med de primære migrationsscripts. Og i artiklen findes der kodeeksempler relateret til migrationer og rollback-mekanismen.
2. Hvis du er begyndt at bruge migrationsværktøjer – må du ikke lave manuelle ændringer i databasens struktur
Som man siger: "Én gang Persil – altid Persil". Hvis din applikations database begynder at blive styret af Liquibase, vil enhver manuel ændring øjeblikkeligt føre til en inkonsistent tilstand, og tilliden til changesets bliver lig med nul. Potentielle risici inkluderer flere timers arbejde på at genskabe databasen; i værste fald kan det føre til en ødelagt server. Hvis du har en DBA-arkitekt i dit team, der er "gammeldags", skal du tålmodigt og omhyggeligt forklare ham, hvordan alt vil gå galt, hvis han blot redigerer databasen efter eget skøn fra en hypotetisk SQL Developer.
3. Hvis changesetet allerede er blevet pushed til repositoryet, skal du undgå redigering
Hvis en anden udvikler har lavet en pull og anvendt et changeset, der senere vil blive redigeret, vil han helt sikkert nævne dig med venlighed, når han får en fejl ved opstart af applikationen. Hvis redigeringen af changesetet på en eller anden måde sniger sig ind i udviklingen, vil det være nødvendigt at gå en usikker vej med hotfixes. Kernen i problemet ligger i valideringen af ændringer efter hash-værdi – den grundlæggende mekanisme i Liquibase. Når koden i changesetet redigeres, ændres hash-værdien. Redigering af changesets er kun mulig, når der er mulighed for at genopbygge hele databasen fra bunden uden tab af data. I sådanne tilfælde kan refaktorering af SQL- eller XML-koden faktisk lette arbejdet og gøre migrationer mere læsevenlige. Et eksempel kan være situationen, hvor skemaet for den oprindelige database blev koordineret internt i teamet ved opstart af applikationen.
4. Hav altid verificerede databasebackups, hvis det er muligt
Her tror jeg, det hele er klart. Hvis migreringen skulle mislykkes, kan alt blive gendannet. I Liquibase er der et værktøj til at rulle ændringer tilbage, men scripts til tilbagerulning skrives også af udvikleren, og der kan være problemer med dem med samme sandsynlighed som med scripts fra den primære changset. Det betyder, at det er en god idé at være forsigtig med backups i alle tilfælde.
5. Brug verificerede databasebackups i udviklingen, hvis det er muligt
Hvis det ikke strider mod kontrakter og privatliv, der ikke er personlige data i databasen, og den ikke fylder som to sunne — før du anvender migreringen på live-servere, kan du teste, hvordan det fungerer på udviklermaskinen, og identificere næsten 100% af de potentielle problemer ved migreringen.
6. Kommuniker med andre udviklere i teamet
I en korrekt organiseret udviklingsproces ved alle i teamet, hvem der laver hvad. I virkeligheden er det ofte ikke tilfældet, så hvis du forbereder ændringer i database-strukturen i forbindelse med din opgave, er det bedst at informere hele teamet om dette. Hvis nogen laver ændringer parallelt — skal I organisere jer forsigtigt. Det er værdifuldt at kommunikere med kolleger, også efter arbejdet er færdigt, ikke kun i starten. Mange potentielle problemer med changsets kan blive løst i kodegennemgangen.
7. Tænk over, hvad du gør!
Det virker som en selvfølgelighed, anvendelig i enhver situation. Dog ville mange problemer kunne undgås, hvis udvikleren tog et øjeblik til at analysere, hvad han gør, og hvilken indflydelse det kan have. Arbejdet med migrationer kræver altid ekstra opmærksomhed og omhu.
Fælder
Lad os nu se på typiske fælder, man kan falde i, hvis man ikke følger ovenstående råd, og hvad skal man egentlig gøre?
Situation 1. To udviklere forsøger samtidigt at tilføje nye changsets

Vasya og Petya ønsker at oprette changset version 4 uden at vide om hinanden. De har foretaget ændringer i database-strukturen og har sendt pull request med forskellige changset-filer. Følgende handlingsmekanisme foreslås:
Hvordan man løser det
- På en eller anden måde skal kollegerne blive enige om, i hvilken rækkefølge deres changsets skal anvendes, for eksempel, at Petyas skal anvendes først.
- En person skal hælde den anden i sig og markere Vasja's changetset med version 5. Dette kan gøres gennem Cherry Pick eller en forsigtig merge.
- Efter ændringerne skal man altid kontrollere gyldigheden af de udførte handlinger.
Faktisk vil Liquibase mekanismerne tillade at have to changets med version 4 i arkivet, så det kan efterlades som det er. Det vil sige, at du bare vil have to ændringer til version 4 med forskellige navne. Med denne tilgang bliver det meget vanskeligt at navigere i versionerne af databasen senere.
Desuden opbevarer Liquibase, ligesom hobbitternes hjem, mange hemmeligheder. En af dem er nøglen validCheckSum, der blev introduceret med version 1.7 og gør det muligt at angive en gyldig værdi for checksummen for et bestemt changetset uanset hvad der er lagret i databasen. Dokumentation siger følgende:
Tilføj en checksum, der betragtes som gyldig for dette changeSet, uanset hvad der er lagret i databasen. Bruges primært, når du skal ændre et changeSet og ikke ønsker fejl på databaser, som det allerede har kørt på (ikke en anbefalet procedure)
Ja, ja, denne procedure anbefales ikke. Men nogle gange besidder en stærk lys magiker også mørke teknikker.
Situation 2. Migrationen afhænger af data

Antag, at du ikke har mulighed for at bruge backup af databaser fra live servere. Petja oprettede et changetset, testede det lokalt og med fuld tillid til sin egen retfærdighed lavede han en pull request til udviklingen. Projektlederen bekræftede ligeledes, om Petja havde tjekket det og derefter fusionerede det. Men udrulningen på udviklingsserveren fejlede.
Faktisk er det muligt, og ingen kan være sikker mod dette. Det sker, når ændringer i tabelstrukturen på en eller anden måde er bundet til specifikke data fra databasen. Det er tydeligt, at hvis Petjas database kun er fyldt med testdata, kan den muligvis ikke dække alle problematiske sager. For eksempel, når en tabel slettes, viser det sig, at der er poster i andre tabeller med Foreign Key, der er relateret til poster i den slettede. Eller når datatypen af en kolonne ændres, viser det sig, at ikke 100% af dataene kan konverteres til den nye type.
Hvordan man løser det
- Skrive særlige scripts, som anvendes én gang sammen med migrationen og bringer data i den rette form. Dette er en generel løsning på problemet med at overføre data til nye strukturer efter migrationerne, men noget lignende kan også anvendes før, i særlige tilfælde. En sådan løsning er naturligvis ikke altid tilgængelig, da det kan være farligt og endda ødelæggende at redigere data på live-servere.
- En anden vanskelig løsning er at redigere det eksisterende changeset. Problematikken ligger i, at alle databaser, hvor det allerede er blevet anvendt i sin nuværende form, skal genskabes. Det er helt muligt, at hele backend-teamet tvinges til at køre databasen lokalt fra bunden.
- Og den mest universelle løsning er at overføre datafejlen til udviklerens miljø med en genskabelse af den samme situation og tilføjelse af et nyt changeset, som kan omgå problemet.

Generelt gælder, at jo mere ens databasen er i forhold til produktionsserveren, desto mindre chance er der for, at migrationsproblemerne strækker sig langt. Og selvfølgelig, før du sender changesetet til depotet, bør du overveje flere gange, om det vil ødelægge noget.
Situation 3. Liquibase begynder at blive anvendt efter produktionen er startet
Antag, at teamlederen har bedt Peter om at implementere Liquibase i projektet, men projektet er allerede i produktion, og der er en eksisterende database-struktur.
Problemet er derfor, at dataene i tabellerne skal genskabes fra bunden på alle nye servere eller udviklermaskiner, mens det eksisterende miljø skal forblive i en konsistent tilstand, klar til at modtage nye changesets.
Hvordan man løser det
Der er også flere veje at gå her:
- Den første og mest åbenlyse er at have et separat script, der skal anvendes manuelt ved initialization af det nye miljø.
- Den anden — mindre åbenlys, er at have en Liquibase-migration, der befinder sig i en anden Liquibase-kontekst, og anvende den. Du kan læse mere om Liquibase-kontekst her: . Generelt er det et interessant mekanisme, der kan anvendes med succes, f.eks. til testformål.
- Den tredje vej består af flere trin. Først skal der oprettes en migration for de allerede eksisterende tabeller. Dernæst skal den anvendes i et bestemt miljø, og på den måde opnås dens hash-værdi. Næste trin er at initialisere tomme Liquibase-tabeller på vores ikke-tomme server, og man kan manuelt indsætte en post om en "tilsyneladende anvendt" changset i historik-tabellen med de allerede eksisterende ændringer i databasen. På den måde vil historikken på den eksisterende server starte fra version 2, mens alle nye miljøer vil opføre sig analogt.

Situation 4. Migrationer bliver enorme og når ikke at blive udført.
I begyndelsen af udviklingen af tjenesten bruges Liquibase normalt som en ekstern afhængighed, og alle migrationer behandles ved opstarten af applikationen. Men med tiden kan du støde på følgende tilfælde:
- Migrationer bliver enorme og tager lang tid at udføre.
- Der opstår behov for migration i distribuerede miljøer, for eksempel på flere instanser af databaser samtidig.
I så fald vil for lang anvendelse af migrationer føre til timeout ved opstart af applikationen. Desuden kan anvendelsen af migrationer for hver applikationsinstans separat føre til, at forskellige servere ikke er synkroniserede.
Hvordan man løser det
I sådanne tilfælde er dit projekt allerede stort, måske endda myndigt, og Liquibase begynder at fungere som et separat eksternt værktøj. Problemet er, at Liquibase som bibliotek samles i en jar-fil og kan fungere både som en afhængighed inden for projektet og autonomt.
I autonom tilstand kan anvendelsen af migrationer overlades til dit CI/CD-miljø eller til de stærke skuldre hos dine systemadministratorspecialister inden for implementering. Dette kræver kommandolinjen Liquibase. . I denne tilstand er det muligt at starte applikationen først, efter at alle nødvendige migrationer er blevet udført.
Output
Der er faktisk langt flere feller, når man arbejder med DB-migrationer, og mange af dem kræver en kreativ tilgang. Det er vigtigt at forstå, at hvis værktøjet anvendes korrekt, kan de fleste af disse feller undgås. Personligt har jeg mødt alle de nævnte problemer i forskellige former, og nogle af dem var resultatet af mine egne fejl. Det sker hovedsageligt på grund af uopmærksomhed, men nogle gange også på grund af kriminel manglende evne til at bruge værktøjet.
Kilde: habr.com


