Бізнес-логіка у базі даних за допомогою 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

Додати коментар або відгук