Бізнес-логіка ў базе даных пры дапамозе SchemaKeeper

Мэта дадзенага артыкула – на прыкладзе бібліятэкі. schema-keeper паказаць прылады, якія дазваляюць істотна аблегчыць працэс распрацоўкі баз дадзеных у рамках PHP-праектаў, якія выкарыстоўваюць СКБД PostgreSQL.

Інфармацыя з гэтага артыкула, у першую чаргу, будзе карысная распрацоўнікам, якія па максімуме жадаюць выкарыстаць магчымасці PostgreSQL, але сутыкаюцца з праблемамі суправаджэння бізнэс-логікі, вынесенай у БД.

Артыкул не будзе апісваць перавагі або недахопы захоўвання бізнес-логікі ў базе дадзеных. Мяркуецца, што выбар ужо зроблены чытачом.

Будуць разгледжаны наступныя пытанні:

  1. У якім выглядзе захоўваць дамп структуры БД у сістэме кантролю версій (далей па тэксце - VCS)
  2. Як адсочваць змены ў структуры БД пасля захавання дампа
  3. Як пераносіць змены ў структуры БД на іншыя асяроддзі без канфліктаў і гіганцкіх файлаў міграцый
  4. Як наладзіць працэс паралельнай працы над праектам некалькіх распрацоўшчыкаў
  5. Як бяспечна дэплоіць большую колькасць змен у структуры БД на production-асяроддзе

    SchemaKeeper заменчаны пад працу з захоўваемымі працэдурамі, напісанымі на мове PL/pgSQL. Тэставанне з іншымі мовамі не праводзілася, адпаведна выкарыстанне можа быць не столь эфектыўна, альбо немагчыма.

У якім выглядзе захоўваць дамп структуры БД у VCS

Бібліятэка schema-keeper дае функцыю saveDump, якая захоўвае структуру ўсіх аб'ектаў з БД у выглядзе асобных тэкставых файлаў. На вынахадзе ствараецца дырэкторыя, утрымоўвальная структуру БД, пабітую на згрупаваныя файлы, якія лёгка дадаць у VCS.

Разгледзім пераўтварэнне аб'ектаў з БД у файлы на некалькіх прыкладах:

Тып аб'екта
схема
Назва
Адносны шлях да файла

табліца
грамадскасці
рахункі
./public/tables/accounts.txt

Захоўваная працэдура
грамадскасці
auth(hash bigint)
./public/functions/auth(int8).sql

прадстаўленне
браніраванне
тарыфы
./booking/views/tariffs.txt

Зместам файлаў з'яўляецца тэкставае прадстаўленне структуры канкрэтнага аб'екта БД. Напрыклад, для захоўваемых працэдур змесцівам файла будзе поўнае вызначэнне захоўваемай працэдуры, якое пачынаецца з блока CREATE OR REPLACE FUNCTION.

Як відаць з табліцы вышэй, шлях да файла захоўвае ў сабе інфармацыю аб тыпе, схеме і назову аб'екта. Такі падыход палягчае навігацыю па дампе і code review змен у БД.

пашырэнне .sql для файлаў з зыходным кодам захоўваемых працэдур абрана для таго, каб IDE аўтаматычна падавалі прылады па ўзаемадзеянні з БД пры адкрыцці файла.

Як адсочваць змены ў структуры БД пасля захавання дампа

Захаваўшы дамп бягучай структуры БД у VCS, мы атрымліваем магчымасць праверыць ці ўносіліся змены ў структуру базы пасля стварэння дампа. У бібліятэцы schema-keeper для выяўлення змен структуры БД прадугледжана функцыя verifyDump, якая без пабочных эфектаў вяртае інфармацыю аб адрозненнях.

Альтэрнатыўны спосаб праверкі - паўторна выклікаць функцыю saveDump, паказаўшы тую ж дырэкторыю, і праверыць у VCS наяўнасць змен. Бо ўсе аб'екты з БД захаваны ў асобных файлах, то VCS пакажа толькі якія змяніліся аб'екты.
Галоўны мінус дадзенага спосабу - неабходнасць перазапісу файлаў, каб убачыць змены.

Як пераносіць змены ў структуры БД на іншыя асяроддзі без канфліктаў і гіганцкіх файлаў міграцый

Дзякуючы функцыі deployDump зыходны код захоўваемых працэдур можа правіцца абсалютна гэтак жа як і звычайны зыходны код дадатку. Можна дадаваць/выдаляць новыя радкі ў кодзе захоўваемых працэдур і адразу ж адпраўляць змены ў сістэму кантролю версій, ці ствараць/выдаляць захоўваемыя працэдуры шляхам стварэння/выдаленні адпаведных файлаў у дырэкторыі з дампам.

Напрыклад, для стварэння новай захоўваемай працэдуры ў схеме public дастаткова стварыць новы файл з пашырэннем .sql у дырэкторыі public/functions, змясціць у яго зыходны код захоўваемай працэдуры, уключаючы блок CREATE OR REPLACE FUNCTION, затым выклікаць функцыю deployDump. Аналагічна адбываецца змена і выдаленне захоўваемай працэдуры. Такім чынам, код адначасова пападае і ў VCS, і ў базу дадзеных.

Калі ў зыходным кодзе якой-небудзь захоўваемай працэдуры з'явіцца памылка, альбо неадпаведнасць паміж назовамі файла і захоўваемай працэдуры, то deployDump не выканаецца, адлюстраваўшы тэкст памылкі. Разузгадненне захоўваемых працэдур паміж дампам і бягучай БД немагчыма пры выкарыстанні deployDump.

Пры стварэнні новай захоўваемай працэдуры няма неабходнасці ўручную ўводзіць правільную назву файла. Досыць, каб у файла было пашырэнне .sql. Пасля выкліку deployDump тэкст памылкі будзе змяшчаць правільную назву, якую можна выкарыстоўваць для перайменавання файла.

deployDump дазваляе змяняць параметры функцыі або які вяртаецца тыпу без дадатковых дзеянняў, у то час як пры класічным падыходзе прыйшлося было б
спачатку выканаць DROP FUNCTION, а толькі потым CREATE OR REPLACE FUNCTION.

На жаль, існуюць некаторыя сітуацыі, калі deployDump не ў стане аўтаматычна прымяніць змены. Напрыклад, калі выдаляецца трыгерная функцыя, якая выкарыстоўваецца хаця б адным трыгерам. Такія сітуацыі вырашаюцца ўручную пры дапамозе файлаў міграцый.

Калі за перанос змен у захоўваемых працэдурах адказвае сам schema-keeper, то для пераносу астатніх змен у структуры неабходна выкарыстоўваць файлы міграцый. Напрыклад, добрай бібліятэкай для працы з міграцыямі з'яўляецца doctrine/migrations.

Міграцыі павінны прымяняцца да запуску deployDump. Гэта дазваляе ўносіць усе змены ў структуру і вырашаць праблемныя сітуацыі, каб змены ў захоўваемых працэдурах у далейшым перанесліся без праблем.

Больш падрабязна праца з міграцыямі будзе апісана ў наступных раздзелах.

Як наладзіць працэс паралельнай працы над праектам некалькіх распрацоўшчыкаў

Неабходна стварыць скрыпт поўнай ініцыялізацыі БД, які будзе запускацца распрацоўшчыкам на сваёй працоўнай машыне, прыводзячы структуру лакальнай БД у адпаведнасць з захаваным у VCS дампам. Прасцей за ўсё падзяліць ініцыялізацыю лакальнай БД на 3 крокі:

  1. Імпарт файла з базавай структурай, які будзе называцца, напрыклад, base.sql
  2. Ужыванне міграцый
  3. выклік deployDump

base.sql - гэта адпраўная кропка, па-над якой ужываюцца міграцыі і выконваецца deployDump, Гэта значыць base.sql + миграции + deployDump = актуальная структура БД. Сфарміраваць такі файл можна з дапамогай утыліты pg_dump. Выкарыстоўваецца base.sql выключна пры ініцыялізацыі базы даных з нуля.

Назавем скрыпт поўнай ініцыялізацыі БД refresh.sh. Працоўны працэс можа выглядаць наступным чынам:

  1. Распрацоўнік запускае ў сваім асяроддзі refresh.sh і атрымлівае актуальную структуру БД
  2. Распрацоўнік пачынае працу над пастаўленай задачай, мадыфікуючы лакальную БД пад патрэбы новага функцыяналу.ALTER TABLE ... ADD COLUMN і г.д)
  3. Пасля выкананне задачы распрацоўшчык выклікае функцыю saveDump, каб зафіксаваць у VCS змены, зробленыя ў БД
  4. Распрацоўнік паўторна запускае refresh.sh, затым verifyDump, які зараз паказвае спіс змен для ўключэння ў міграцыю
  5. Распрацоўнік пераносіць усю змену структуры ў файл міграцыі, запускае яшчэ раз refresh.sh и verifyDump, і, калі міграцыя складзена карэктна, verifyDump пакажа адсутнасць адрозненняў паміж лакальнай БД і захаваным дампам

Апісаны вышэй працэс сумяшчальны з прынцыпамі gitflow. Кожная галінка ў VCS будзе змяшчаць сваю версію дампа, і пры зліццё галінак будзе адбывацца зліццё дампаў. У большасці выпадкаў пасля зліцця не трэба прадпрымаць ніякіх дадатковых дзеянняў, але калі ў розных галінах уносіліся змены, да прыкладу, у адну і тую ж табліцу, можа ўзнікнуць канфлікт.

Разгледзім канфліктную сітуацыю на прыкладзе: ёсць галінка. развіваць, ад якой адгалінаванае дзве галінкі: функцыя1 и функцыя2, якія не маюць канфліктаў з развіваць, але маюць канфлікты паміж сабой. Стаіць задача выканаць зліццё абедзвюх галінак у развіваць. Для такога выпадку рэкамендуецца спачатку выканаць зліццё адной з галінак у развіваць, а затым зліццё развіваць у астатнюю галінку, дазволіўшы пры гэтым канфлікты ў астатняй галінцы, пасля чаго выканаць зліццё апошняй галінкі ў развіваць. На этапе дазволу канфліктаў магчыма прыйдзецца выправіць файл міграцыі ў апошняй галінцы, каб ён адпавядаў фінальнаму дампу, які ўключыў у сябе вынікі зліцця.

Як бяспечна дэплоіць большую колькасць змен у структуры БД на production-асяроддзе

Дзякуючы наяўнасці ў VCS дампа актуальнай структуры БД з'яўляецца магчымасць праверыць production-базу на дакладную адпаведнасць патрабаванай структуры. Гэта гарантуе, што на production-базу паспяхова перанесліся ўсе змены, якія задумвалі распрацоўшчыкі.

Так як DDL у PostgreSQL з'яўляецца транзакцыйным, рэкамендуецца прытрымлівацца наступнага парадку дэплою, каб, у выпадку непрадбачанай памылкі, «бязбольна» выканаць ROLLBACK:

  1. Пачаць транзакцыю
  2. У транзакцыі выканаць усе міграцыі
  3. У гэтай жа транзакцыі выканаць deployDump
  4. Не завяршаючы транзакцыю, выканаць verifyDump. Калі памылак няма, выканаць COMMIT. Калі памылкі ёсць, выканаць ROLLBACK

Дадзеныя крокі дастаткова лёгка ўбудоўваюцца ў існуючыя падыходы да дэплою прыкладанняў, у тым ліку zero-downtime.

Заключэнне

Дзякуючы вышэйапісаным метадам можна выціскаць максімум прадукцыйнасці з "PHP + PostgreSQL" праектаў, ахвяруючы пры гэтым адносна невялікай колькасцю зручнасці распрацоўкі ў параўнанні з рэалізацыяй ўсёй бізнес-логікі ў асноўным кодзе дадатку. Больш за тое, апрацоўка дадзеных у PL/pgSQL часта выглядае больш празрыста і патрабуе меншай колькасці кода, чым той жа функцыянал, напісаны на PHP.

Крыніца: habr.com

Дадаць каментар