Logica de afaceri în baza de date folosind SchemaKeeper

Scopul acestui articol este să demonstreze prin exemplul unei biblioteci schema-keeper instrumentele care facilitează semnificativ procesul de dezvoltare a bazelor de date în cadrul proiectelor PHP care utilizează SGBD PostgreSQL.

Informațiile din acest articol vor fi, în primul rând, utile dezvoltatorilor care doresc să valorifice la maximum posibilitățile PostgreSQL, dar se confruntă cu probleme de întreținere a logicii de afaceri, externalizată în baza de date.

Articolul nu va descrie avantajele sau dezavantajele stocării logicii de afaceri în baza de date. Se presupune că alegerea a fost deja făcută de cititor.

Vor fi discutate următoarele subiecte:

  1. În ce formă să stocăm backup-ul structurii bazei de date în sistemul de control al versiunilor (în continuare VCS)
  2. Cum să urmărim modificările structurii bazei de date după salvarea backup-ului
  3. Cum să transferăm modificările structurii bazei de date în alte medii fără conflicte și fișiere de migrare uriașe
  4. Cum să organizăm procesul de lucru paralel la proiect de către mai mulți dezvoltatori
  5. Cum să desfășurăm în siguranță un număr mai mare de modificări în structura bazei de date pe mediu de producție

    SchemaKeeper este utilizat pentru a lucra cu proceduri stocate scrise în limbajul PL/pgSQL. Testarea cu alte limbaje nu a fost efectuată, prin urmare utilizarea poate fi nu atât de eficientă sau imposibilă.

În ce formă să stocăm backup-ul structurii bazei de date în VCS

Biblioteca schema-keeper oferă funcția saveDump, care salvează structura tuturor obiectelor din baza de date sub formă de fișiere text separate. La final, se creează un director care conține structura bazei de date, împărțită în fișiere grupate, care pot fi adăugate ușor în VCS.

Să analizăm transformarea obiectelor din baza de date în fișiere prin câteva exemple:

Tipul obiectului
Schema
Denumire
Calea relativă către fișier

Tabel
public
accounts
./public/tables/accounts.txt

Procedura stocată
public
auth(hash bigint)
./public/functions/auth(int8).sql

Reprezentare
booking
tariffs
./booking/views/tariffs.txt

Conținutul fișierelor reprezintă o prezentare textuală a structurii unui obiect specific al bazei de date. De exemplu, pentru procedurile stocate, conținutul fișierului va fi definiția completă a procedurii stocate, începând cu blocul CREATE OR REPLACE FUNCTION.

După cum se vede din tabelul de mai sus, calea către fișier conține informații despre tipul, schema și numele obiectului. Această abordare facilitează navigarea și revizuirea modificărilor în backup-ul bazei de date.

Extensie .sql pentru fișierele cu cod sursă al procedurilor stocate, a fost ales pentru ca IDE-urile să ofere automat instrumente pentru interacțiunea cu baza de date la deschiderea fișierului.

Cum să urmărim modificările structurii bazei de date după salvarea backup-ului

Prin salvarea unui dump al structurii curente a bazei de date în VCS, obținem posibilitatea de a verifica dacă s-au efectuat modificări în structură după crearea dump-ului. În bibliotecă schema-keeper pentru identificarea modificărilor structurii bazei de date este prevăzută funcția verifyDump, care returnează informații despre diferențe fără efecte secundare.

O metodă alternativă de verificare este de a apela din nou funcția saveDump, specificând aceeași direcție, și a verifica în VCS existența modificărilor. Deoarece toate obiectele din baza de date sunt salvate în fișiere separate, VCS va arăta doar obiectele care s-au modificat.
Principalul dezavantaj al acestei metode este necesitatea de a rescrie fișierele pentru a vedea modificările.

Cum să transferăm modificările structurii bazei de date în alte medii fără conflicte și fișiere de migrare uriașe

Thanks to the function deployDump codul sursă al procedurilor stocate poate fi modificat exact ca un cod sursă obișnuit al aplicației. Poți adăuga/șterge noi linii în codul procedurilor stocate și trimite imediat modificările în sistemul de control al versiunilor, sau crea/șterge proceduri stocate prin crearea/ștergerea fișierelor corespunzătoare în directorul cu dump-ul.

De exemplu, pentru a crea o nouă procedură stocată în schema public este suficient să creezi un fișier nou cu extensia .sql în directorul public/functions, să plasezi în el codul sursă al procedurii stocate, inclusiv blocul CREATE OR REPLACE FUNCTION, apoi să apelezi funcția deployDump. La fel se procedează și în cazul modificării sau ștergerii unei proceduri stocate. Astfel, codul ajunge simultan atât în VCS, cât și în baza de date.

Dacă în codul sursă al oricărei proceduri stocate apare o eroare sau o neconcordanță între numele fișierului și procedura stocată, atunci deployDump nu va fi executată, afișând mesajul de eroare. Neconcordanța dintre procedurile stocate între dump și baza de date curentă este imposibilă folosind deployDump.

Atunci când se creează o nouă procedură stocată, nu este necesar să introduci manual numele corect al fișierului. Este suficient ca fișierul să aibă extensia .sql. După apelarea lui, deployDump mesajul de eroare va conține numele corect, care poate fi folosit pentru a redenumi fișierul.

deployDump permete modificarea parametrilor funcției sau tipului de returnare fără acțiuni suplimentare, în timp ce în abordarea clasică ar fi fost necesar să
mai întâi să execuți DROP FUNCTION, și abia apoi CREATE OR REPLACE FUNCTION.

Din păcate, există anumite situații în care deployDump nu se pot aplica automat modificările. De exemplu, dacă se șterge o funcție declanșatoare, care este utilizată de un declanșator. Astfel de situații se rezolvă manual cu ajutorul fișierelor de migrare.

Dacă transferul modificărilor în procedurile stocate este responsabilitatea însăși schema-keeper, atunci pentru transferul celorlalte modificări în structură este necesar să se utilizeze fișiere de migrare. De exemplu, o bibliotecă bună pentru lucrul cu migrarea este doctrine/migrations.

Migrațiile trebuie să fie aplicate înainte de lansare deployDump. Acest lucru permite efectuarea tuturor modificărilor în structură și rezolvarea problemelor, astfel încât modificările din procedurile stocate să fie transferate ulterior fără probleme.

Mai multe detalii despre lucrul cu migrațiile vor fi prezentate în secțiunile următoare.

Cum să organizăm procesul de lucru paralel la proiect de către mai mulți dezvoltatori

Este necesar să se creeze un script de inițializare completă a Bazei de Date, care va fi rulat de dezvoltator pe mașina sa de lucru, aducând structura bazei de date locale în conformitate cu dump-ul salvat în VCS. Cel mai simplu este să se împartă inițializarea bazei de date locale în 3 pași:

  1. Importarea unui fișier cu structura de bază, care se va numi, de exemplu, base.sql
  2. Aplicarea migrațiilor
  3. Apel deployDump

base.sql — aceasta este punctul de plecare, pe care se aplică migrațiile și se execută deployDump, adică base.sql + migrații + deployDump = structura actualizată a bazei de date. Un astfel de fișier poate fi generat cu ajutorul utilitarului pg_dump. Este utilizat base.sql exclusiv pentru inițializarea bazei de date de la zero.

Să numim scriptul de inițializare completă a bazei de date refresh.sh. Fluxul de lucru poate arăta în felul următor:

  1. Dezvoltatorul rulează în mediul său refresh.sh și obține structura actualizată a bazei de date
  2. Dezvoltatorul începe lucrul la sarcina primită, modificând baza de date locală conform nevoilor funcționalității noi (ALTER TABLE ... ADD COLUMN etc.)
  3. După finalizarea sarcinii, dezvoltatorul apelează funcția saveDump, pentru a înregistra în VCS modificările efectuate în baza de date
  4. Dezvoltatorul rulează din nou refresh.sh, apoi verifyDump, care acum arată lista modificărilor pentru includerea în migrație
  5. Dezvoltatorul transferă toate modificările structurii în fișierul de migrare, îl rulează din nou refresh.sh și verifyDump, și, dacă migrarea este corect formulată, verifyDump va arăta absența diferențelor între baza de date locală și dump-ul salvat.

Procesul descris mai sus este compatibil cu principiile gitflow. Fiecare ramură din VCS va conține propria versiune a dump-ului, iar la îmbinarea ramurilor se va face îmbinarea dump-urilor. În majoritatea cazurilor, după îmbinare nu este necesar să se efectueze acțiuni suplimentare, dar dacă modificări au fost făcute în ramuri diferite, de exemplu, în aceeași tabelă, pot apărea conflicte.

Să luăm în considerare o situație conflictuală pe exemplul: există o ramură develop, de la care s-au bifurcat două ramuri: feature1 și feature2, care nu au conflicte cu develop, dar au conflicte între ele. Obiectivul este de a îmbina ambele ramuri în develop. Pentru astfel de cazuri, se recomandă mai întâi îmbinarea uneia dintre ramuri în develop, iar apoi îmbinarea develop în ramura rămasă, rezolvând în același timp conflictele în ramura rămasă, după care se va efectua îmbinarea ultimei ramuri în develop. În etapa de rezolvare a conflictelor poate fi necesar să se corecteze fișierul de migrare din ultima ramură, astfel încât acesta să corespundă dump-ului final, care include rezultatele îmbinărilor.

Cum să desfășurăm în siguranță un număr mai mare de modificări în structura bazei de date pe mediu de producție

Datorită existenței în VCS a dump-ului structurii actuale a bazei de date, apare posibilitatea de a verifica baza de date de producție pentru a avea o corespondență exactă cu structura necesară. Acest lucru garantează că toate modificările pe care le-au intenționat dezvoltatorii s-au transferat cu succes în baza de date de producție.

Deoarece DDL în PostgreSQL este tranzacțional, se recomandă respectarea următoarei ordini de desfășurare, pentru a putea, în caz de eroare neprevăzută, să efectuezi „fără durere” ROLLBACK:

  1. Începe tranzacția
  2. În tranzacție, efectuează toate migrațiile
  3. În aceeași tranzacție, efectuează deployDump
  4. Fără a închide tranzacția, efectuează verifyDump. Dacă nu sunt erori, efectuează COMMIT. Dacă sunt erori, efectuează ROLLBACK

Acești pași sunt destul de ușor de integrat în abordările existente de desfășurare a aplicațiilor, inclusiv zero-downtime.

Concluzie

Datorită metodelor descrise mai sus, se poate extrage maximul de performanță din proiectele „PHP + PostgreSQL”, sacrificând astfel o cantitate relativ mică de confort în dezvoltare în comparație cu implementarea întregii logici de afaceri în codul principal al aplicației. Mai mult, procesarea datelor în PL/pgSQL aparent, este mai transparentă și necesită mai puțin cod decât aceeași funcționalitate scrisă în PHP.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster