metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2014-10-31 04:31 pm

База данных как API

Наткнулся на баг в 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 на справочники, если у тебя вместо таблицы в БД - какая-то хреновина не пойми на каком языке, да еще не факт что подключенная к БД, а не получающая информацию из очередей).

[identity profile] justy-tylor.livejournal.com 2014-10-31 02:39 pm (UTC)(link)
Для этих задач применяются логические движки, с поддержкой forward chaining и backward chaining. Вот только они обычно недоразвитые (предназначены для мелкого подмножества задач, когда-то затребованного при их создании) и сильно муторны в интеграции. По сути, в SQL (или SPARQL) СУБД используются такие же недоразвитые движки, но процесс интеграции намного более привычен для широких народных масс.

Варианты:
1. Впихивать пока впихивается.
2. Искать движки, более подходящие под требуемое подмножество задач.
3. Писать ещё один, свой.

[identity profile] Дмитрий Васильев (from livejournal.com) 2014-10-31 02:48 pm (UTC)(link)
Все правильно делаете.
Это можно сделать, если изменения таблицы в БД заменить на изменения на ее копии в памяти сервера приложений, в БД скидывать результат раз в 15-30 секунд (чтобы восстановить состояние сервиса при его рестарте).
Если в firebird действительно критические баги, то лучше уж на постгрес переехать.

[identity profile] ynot.livejournal.com 2014-10-31 06:05 pm (UTC)(link)
не очень понятно, "вы жалуетесь или хвастаетесь" - т.е. если вам все равно эту новую функциональность нужно/хочется добавлять, то тут уж конечно нужно делать "как надо" - а если вам можно просто быстро косяк побороть (и он где-то вроде бы не исключено, что про кривые блокировки), может быть, попробовать разгрузить эту самую табличку чисто архитектурными и датабейзными методами - вынести чтение "текущего состояния" куда-нибудь на отдельную табличку, посмотреть на подозрительно длинные или неявно стартующие транзакции, партицировать табличку, вручную там лочить все подряд "с запасом" - я не очень в курсе, чо firebird может предложить. Т.е. в пределах недели трудозатрат там должен быть маневр для применения "метода интеллектуального тыка" с ненулевой вероятностью положительного результата.
Edited 2014-10-31 18:12 (UTC)

[personal profile] zaharchenko 2014-10-31 09:45 pm (UTC)(link)
А не легчели в конечном счете будет сам баг пофиксить, а то чую костыли эти ваши ещё маленьку тележку багов наплодят

[identity profile] berezovsky.livejournal.com 2014-10-31 10:09 pm (UTC)(link)
А если так.
Апдейт таблицы заменить на апдейт вьюшки.
Сделать триггер перед апдейтом, который выполняет роль шейпера - например, делает инсерт в другую таблицу.
Сервис по тихой грусти переебашивает вьюшку и апдейтит исходную табличку.

[identity profile] dmitry shamov (from livejournal.com) 2014-11-05 12:32 pm (UTC)(link)
Я так развлекался в университете. Препод попросил написать "аську". Я взял сделал файлик аксесс. И там лог с транзакциями. Все апдейтят, клиенты по таймеру читают все что выше их транзакции. У препода волосы шевелились. Я говорю вопервых тут используется TCP-IP ибо сетевой диск как раз через сетку и расшарен и даже сокеты. Только я решил использовать уровень выше.