База данных как API
Oct. 31st, 2014 04:31 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Наткнулся на баг в Firebird: http://tracker.firebirdsql.org/browse/CORE-2848
В трекере написано, что исправлено (после перехода на 2.5.3 и backup/restore) но у меня продолжает появлятся, причем только в сложносочиненных условиях (нагруженные продакшены, с сотнями апдейтов в секунду и сотнями юзеров).
Сама по себе ошибка глобальных проблем не несет - у меня процессы умеют в самовосстановление после ошибок, но иногда то ли эта же ошибка, то ли что-то смежное в менеджере блокировок приводит к странному эффекту: один из процессов fb_inet_server.exe зависает, начиная бесконечно крутится, кушая CPU в цикле рекурсивных вызовов типа
"fb_inet_server!down_grade+0x62", в то время, как все остальные процессы ждут завершения этого, ничего не записывая и не читая. Если грохнуть кушающий процесс - все продолжает работать, накрывается только работа того коннекта, который обрабатывал зависший процесс.
Воспроизвести на столе пока не получается, если по хорошему, то надо бы дампы повисшего сервера, базы данных и блокировок спихнуть разработчикам на изучение.
Один из вариантов обойти ошибку (и заодно добавить новой полезной функциональности) на данный момент: сократить нагрузку на таблицу, на которой возникает проблема (в ней сотни раз в секунду выполняются update, и в это же время ее читают несколько десятков транзакций). Это можно сделать, если изменения таблицы в БД заменить на изменения на ее копии в памяти сервера приложений, в БД скидывать результат раз в 15-30 секунд (чтобы восстановить состояние сервиса при его рестарте).
Так вот, внезапно при такой переделке оказывается, что БД и конкретно эта таблица у нас играет роль API между десятком различных модулей, UI и разного рода сервисами.
Т.е. самый прямой вариант обмениваться данными между подсистемами, который работает всегда - это реляционная БД, с транзакциями, лаконичным SQL в качестве языка преобразования данных в нужный формат и заодно разного рода аудитом на триггерах, при необходимости.
А если я это перенесу в сервис приложений, то внезапно вместо 3 почти однообразных клиентских подключений к СУБД (дельфи-клиенты, .NET веб-сервис и clojure сервис-приложений), у меня появится необходимость взаимодействовать с сервером приложений через некий велосипедный протокол (скорее всего, что-нибудь на тему RESTful сервисов, они на CRUD хорошо укладываются). Или же придется во всю эту конструкцию еще и добавлять MQ сервис и тащить в продакшен или ActiveMQ (жаба в виде кложури уже есть) или RabbitMQ (тогда нашим инженерам придется осиливать эксплуатацию эрланговских приложений), потому что задача на очереди укладывается еще лучше чем на БД.
И поверх этого - разного рода костыли и велосипеды на предмет аудита, преобразований и расширения данных (вот например, чем заменить join на справочники, если у тебя вместо таблицы в БД - какая-то хреновина не пойми на каком языке, да еще не факт что подключенная к БД, а не получающая информацию из очередей).
В трекере написано, что исправлено (после перехода на 2.5.3 и backup/restore) но у меня продолжает появлятся, причем только в сложносочиненных условиях (нагруженные продакшены, с сотнями апдейтов в секунду и сотнями юзеров).
Сама по себе ошибка глобальных проблем не несет - у меня процессы умеют в самовосстановление после ошибок, но иногда то ли эта же ошибка, то ли что-то смежное в менеджере блокировок приводит к странному эффекту: один из процессов fb_inet_server.exe зависает, начиная бесконечно крутится, кушая CPU в цикле рекурсивных вызовов типа
"fb_inet_server!down_grade+0x62", в то время, как все остальные процессы ждут завершения этого, ничего не записывая и не читая. Если грохнуть кушающий процесс - все продолжает работать, накрывается только работа того коннекта, который обрабатывал зависший процесс.
Воспроизвести на столе пока не получается, если по хорошему, то надо бы дампы повисшего сервера, базы данных и блокировок спихнуть разработчикам на изучение.
Один из вариантов обойти ошибку (и заодно добавить новой полезной функциональности) на данный момент: сократить нагрузку на таблицу, на которой возникает проблема (в ней сотни раз в секунду выполняются update, и в это же время ее читают несколько десятков транзакций). Это можно сделать, если изменения таблицы в БД заменить на изменения на ее копии в памяти сервера приложений, в БД скидывать результат раз в 15-30 секунд (чтобы восстановить состояние сервиса при его рестарте).
Так вот, внезапно при такой переделке оказывается, что БД и конкретно эта таблица у нас играет роль API между десятком различных модулей, UI и разного рода сервисами.
Т.е. самый прямой вариант обмениваться данными между подсистемами, который работает всегда - это реляционная БД, с транзакциями, лаконичным SQL в качестве языка преобразования данных в нужный формат и заодно разного рода аудитом на триггерах, при необходимости.
А если я это перенесу в сервис приложений, то внезапно вместо 3 почти однообразных клиентских подключений к СУБД (дельфи-клиенты, .NET веб-сервис и clojure сервис-приложений), у меня появится необходимость взаимодействовать с сервером приложений через некий велосипедный протокол (скорее всего, что-нибудь на тему RESTful сервисов, они на CRUD хорошо укладываются). Или же придется во всю эту конструкцию еще и добавлять MQ сервис и тащить в продакшен или ActiveMQ (жаба в виде кложури уже есть) или RabbitMQ (тогда нашим инженерам придется осиливать эксплуатацию эрланговских приложений), потому что задача на очереди укладывается еще лучше чем на БД.
И поверх этого - разного рода костыли и велосипеды на предмет аудита, преобразований и расширения данных (вот например, чем заменить join на справочники, если у тебя вместо таблицы в БД - какая-то хреновина не пойми на каком языке, да еще не факт что подключенная к БД, а не получающая информацию из очередей).
no subject
Date: 2014-10-31 02:39 pm (UTC)Варианты:
1. Впихивать пока впихивается.
2. Искать движки, более подходящие под требуемое подмножество задач.
3. Писать ещё один, свой.
no subject
Date: 2014-10-31 02:48 pm (UTC)Это можно сделать, если изменения таблицы в БД заменить на изменения на ее копии в памяти сервера приложений, в БД скидывать результат раз в 15-30 секунд (чтобы восстановить состояние сервиса при его рестарте).
Если в firebird действительно критические баги, то лучше уж на постгрес переехать.
no subject
Date: 2014-10-31 03:27 pm (UTC)А переезд пока осуществляется методом "новые проекты поддерживают разные СУБД" и медленной миграцией функционала из СУБД в сервера приложений. Вот описанное в посте - это одновременно обход бага, добавление новых юзеро-полезных фич и убирание части вендор-лока на определенную СУБД.
no subject
Date: 2014-10-31 09:59 pm (UTC)no subject
Date: 2014-10-31 06:05 pm (UTC)no subject
Date: 2014-10-31 09:45 pm (UTC)no subject
Date: 2014-11-01 08:58 am (UTC)Причем, судя по количеству аналогичных багов, формальные доказательства правильности с менеджером блокировок там вроде рядом не ходили.
Ну и есть идея таки вынести логику из БД, чтобы можно было разные СУБД использовать, поэтому рано или поздно что-нибудь подобное делать придется.
no subject
Date: 2014-11-03 12:22 pm (UTC)Гм. А зачем?
no subject
Date: 2014-11-03 12:52 pm (UTC)Гм. А зачем?
no subject
Date: 2014-11-03 01:28 pm (UTC)no subject
Date: 2014-11-03 04:59 pm (UTC)Кроме того, привязка к конкретной СУБД позволяет интересоваться только особенностями одной СУБД, а так придется изучать все, которые придут в голову клиентам.
Ну и напоследок, вынос функционала из СУБД приведет к необходимости 1) регулярно таскать данные туда-сюда 2) реализовывать кучу функционала по работе с большими данными ручками и 3) ручками заботиться о целостности данных
Поэтому если речь идет не о примитивных случаях, замах на кроссбазоданность вызывает у меня подозрения.
no subject
Date: 2014-11-03 07:26 pm (UTC)2) функционал больших данных будет делать БД. С ним как раз проблем нет, проблемы с маленькой горячей табличкой.
3) целостность данных тоже будет делать БД, это все СУБД делают более-менее одинаково.
основная проблема - это то, что общение с БД из любых языков программирования - стандартная много раз решенная задача, а вот если БД заменить на аппсервер или там общаться через очереди, начинается всякая херня - в одном языке AMQP одной версии, в другом STOMP клиент собирается только по четным фазам луны, в третьем клиент недописан и нужно ручками отвечат на heartbeat запрос и прочее такое.
no subject
Date: 2014-10-31 10:09 pm (UTC)Апдейт таблицы заменить на апдейт вьюшки.
Сделать триггер перед апдейтом, который выполняет роль шейпера - например, делает инсерт в другую таблицу.
Сервис по тихой грусти переебашивает вьюшку и апдейтит исходную табличку.
no subject
Date: 2014-11-01 08:59 am (UTC)Делается инсерт в таблицу, на ней триггер. который обновляет вот эту горячую таблицу (первая таблица, условно говоря - события, вторая - состояние объектов-источников событий).
no subject
Date: 2014-11-05 12:32 pm (UTC)