Skip to content

Загальний огляд

Реплікація - це автоматичне дублювання даних з однієї бази в одну чи декілька інших баз даних. Надалі база даних що відправляє дані називається "транслятор", база даних що отримує дані від транслятора називається "приймач".

Якщо одна база виступає лише в ролі транслятора а інша лише у ролі приймача, така реплікація називається однонапрямленою. Може використовуватися для того, щоб мати напоготові "живу" копію робочої бази даних - для тестування, чи то для важких звітів що нічого не міняють у базі але суттєво навантажують систему, або просто у якості гарячого резерву.

Якщо обидві бази є одночасно і трансляторами і приймачами, така реплікація називається двонапрямленою; використовується здебільшо для синхронізації даних між кількома робочими базами.

У "Рахівниці" реплікація працює наступним чином. Після створення, зміни, або видалення запису в таблиці бази-транслятора автоматично створюється 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":2,
        "receivers": {"test1":"Not available","Автостанція":"Available"},
        "stale_count":0,
        "status":"ok"
    }
    
  • 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/