Загальний огляд
Реплікація - це автоматичне дублювання даних з однієї бази в одну чи декілька інших баз даних. Надалі база даних що відправляє дані називається "транслятор", база даних що отримує дані від транслятора називається "приймач".
Якщо одна база виступає лише в ролі транслятора а інша лише у ролі приймача, така реплікація називається однонапрямленою. Може використовуватися для того, щоб мати напоготові "живу" копію робочої бази даних - для тестування, чи то для важких звітів що нічого не міняють у базі але суттєво навантажують систему, або просто у якості гарячого резерву.
Якщо обидві бази є одночасно і трансляторами і приймачами, така реплікація називається двонапрямленою; використовується здебільшо для синхронізації даних між кількома робочими базами.
У "Рахівниці" реплікація працює наступним чином. Після створення, зміни, або видалення запису в таблиці бази-транслятора автоматично створюється SQL запит щоб повторити цю операцію в іншій (такій самій) базі даних. Далі ці запити зберігаються в окремій таблиці звідки їх зчитує зовнішня служба і запускає їх у базі-приймачі. Після успішного запуску у всіх приймачах, відпрацьовані запити видаляються з транслятора.
Налаштування бази даних
Налаштування реплікації робиться через IBExpert у таблицях з префіксом RPL. Їхнє призначення наступне (у логічному порядку):
- RPL_DATABASES - перелік баз-приймачів куди будуть транслюватися дані. Для кожної бази даних окрім параметрів доступу також зберігається ІД останнього успішно реплікованого запису.
- RPL_TABLES - перелік таблиць, котрі беруть участь у реплікації. Це означає, що реплікуватися будуть не всі дані а лише ті таблиці, котрі тут перераховані. При цьому усі приймачі будуть отримувати усі дані з перерахованих таблиць. Технічно можливо реалізувати детальнішу фільтрацію, але наразі в тому не виникало потреби. Поле IS_DOCHEADER вказує чи є конкретна таблиця заголовком документу. Документи реплікуються лише після того, як вони були завершені (значення у полі COMMITED = 1). Це зроблено навмисне, щоб не бентежити користувачів віддалених баз даних незавершеними документами. Якщо з відповідної таблиці потрібно синхронізувати не всі поля, тоді треба вказати 0 (false) у RPL_ALLFIELDS і перерахувати лише потрібні поля у таблиці RPL_FIELDS.
- У таблиці RPL_LOGS зберігаються всі SQL запити що потрібно транслювати приймачам. В нормі, коли всі дані синхронізовано, ця таблиця має бути порожньою.
Після заповнення таблиці RPL_TABLES (та при необхідності RPL_FIELDS) потрібними даними, потрібно запустити процедуру RPL_INSTALL. Вона створить тригери для всіх задіяних таблиць котрі якраз і відповідальні за генерування SQL запитів для даних що мають реплікуватися. Щоб побачити створені тригери, можливо потрібно перезапустити IBExpert. Процедура RPL_UNINSTALL відповідно видаляє реплікаційні тригери. Обидві процедури є відносно безпечними і їх можна запускати багато разів в процесі початкового налаштування, єдине зауваження - до бази при цьому не має бути під'єднано інших програм окрім IBExpert-a.
До речі, ці процедури викликаються лаунчером під час кожного поновлення бази даних - перед початком запуску скриптів поновлення запускається RPL_UNINSTALL, а по завершенню - RPL_INSTALL. Це робиться щоб під час оновлення у базі даних не було сторонніх об'єктів що можуть створювати нетипові залежності.
Створені тригери мають кілька особливостей. По-перше всі вони мають суфікс REPL і виконуються останніми у ланцюжку тригерів. По-друге, всі вони перевіряють чи в активній сесії встановлено змінну replicating_now і виконуються лише якщо цієї змінної немає. Це зроблено для того, щоб ті дані, котрі прийшли з транслятора на приймач у двосторонній реплікації, ігнорувались і не реплікувались повторно. І по-третє, в кінці роботи вони відправляють подію replicate котра є сигналом для Abasyn що для нього є робота.
Abasyn
Мікросервіс на Python/Flask котрий запускається на тій самій машині що й база-транслятор. Після запуску, він чекає на появу події replicate після чого пробує під'єднатись до всіх баз даних перерахованих у RPL_DATABASES і відправити їм усі дані з RPL_LOG що мають ІД більший аніж LAST_ID.
На додачу у ньому реалізовано наступний АРІ:
* GET /api/status/
Повертає власний статус у вигляді наступного JSON
{
"events_processed":3,
"queue_size":0,
"records_processed":0,
"remote_db_status":{ "connected":false, "last_connect":"2024-07-20 12:37:34" },
"status":"listening"
}
events_processed - кількість оброблених подій
records_processed - кількість відправлених запитів (для документів одна подія буде містити багато запитів - по одному для кожної позиції документу плюс один або два для заголовку)
status - може бути processing якщо програма прямо в даний момент синхронізує дані, listening якщо програма слухає дані (швидше за все єдиний що можна реально отримати), та stopped. В цьому випадку програма запущена але по якійсь причині не слухає локальну базу даних. Потрібно переконатись що Abasyn має доступ до бази-транслятора і перезапустити його.
remote_db_status - може бути або { "connected":false, "last_connect":"2024-07-20 12:37:34" } у випадку коли status=listening, або { "connected":true, "since":"2024-07-20 12:37:34" } якщо status=processing.
- GET
/api/repl/statusПовертає статус реплікації у вигляді наступного JSON - receiver_count - кількість зареєстрованих приймачів;
- receivers - перелік приймачів і їх доступність; щоб отримати цей перелік абасин намагається під'єднатись з 10-секундним таймаутом до кожної зареєстрованої бази даних; відповідно відповідь на цей АРІ запит може затриматись до 10 секунд помножену на кількість недоступних отримувачів
- stale_count - кількість нереплікованих записів у таблиці RPL_LOG; якщо вона більша за 0 то імовірно якийсь з приймачів недоступний для реплікації
-
status - наразі завжди "ok"
-
GET
/api/repl/check/{tovar_id}/Перевіряє статус синхронізації вказаного товару; повернути перелік відміносттей якщо такі є -
GET
/api/repl/check_all/Перевіряє статус синхронізації всіх товарів; повернути перелік ІД товарів що мають відмінності в синхронізації -
POST
/api/repl/initialize/Ініціалізує реплікацію у локальній базі даних. При цьому таблиця RPL_TABLES заповнюється даними за замовчуванням - всіма документами і довідниками плюс всіма таблицями що використовуються в обліку. -
GET
/api/repl/receivers/Отримати перелік всіх приймачів. -
GET/POST/DELETE
/api/repl/receiver/{id}/Отримати інформацію, додати, або видалити приймача. Для додавання ID вказувати не треба, при цьому у полі POST має бути наступна структура:Приклад додавання нового приймача{ "alias": "Назва приймача", "dbname": "server:database", "dbuser": "SYSDBA", "dbpass": "masterkey" }# стандартні ім'я і пароль доступу до бази даних curl --request POST --header "Content-Type: application/json" --data '{"alias": "test2", "dbname":"10.20.30.40:test2"}' localhost:5000/api/repl/initialize/ # спеціальні ім'я і пароль для реплікації curl --request POST --header "Content-Type: application/json" --data '{"alias": "test2", "dbname":"10.20.30.40:abacus", "dbuser": "REPL", "dbpass": "xFd$#1)lk"}' localhost:5000/api/repl/initialize/