Ну, в целом можно считать да, хотя каждая транзакция это зачастую не одна изменяемая сущность, а десяток (состояние счета, лог аудита, свойства клиента иногда, задания на рассылки нотификаций) и опять же, даже эти маленькие транзакции надо ведь гарантированно синхронизировать между собой, нельзя ведь просто положиться на то, что «изменения атомарно и точно не будут записываться в БД параллельно с непредсказуемым итоговым порядком
xmoonlight, ну, в пределах одной достаточно большой секции, доступной этой группе локальных серверов - да. Или я может неправильно мысль понял? А можно на небольшом примере пожалуйста?
xmoonlight, Что мы собственно сейчас и делаем, блокируем синхронизацию так, что по данным одного клиента одновременно идет импорт/экспорт только с одного удаленного сервера. И все остальные удаленные сервера ждут своей очереди на синхронизацию. Их много - очередь растет, синхронизация не успевает проходить за таймауты. Увеличение таймаутов/снижение частоты синхронизации = деградация доступности, которой мы хотим избежать.
Насчет перезаписи данных - да, эту проблему считаем что решаем на логическом уровне приложением, это не проблема.
Насчет того, что ревизию (у нас это фактически просто bigint'ы из sequence'а SQL сейчас) проставляет приложение, а не БД - это знаем что косяк. Хотели от этого уйти в сторону ChangeTracking'а SQL сервера, но собираемся с него уходить. Так что хотим найти какое-то универсальное решение, по возможности не привязанное к БД, либо доступное в Postgre.
И да, сейчас транзакции импорта как раз блокируют друг друга, к этому решению нам пришлось прийти чтобы система работала стабильно, и работает оно неплохо, когда параллельно синхронизирующихся клиентов немного. Стало больше - стали ловить проблемы с тем, что эта блокировка - узкое место всей синхронизации данных. Решение снести проставление ревизии на "последний момент перед записью в БД", ровно как и делать его триггерами - тоже рассматривали, это сильно снижает вероятность возникновения проблемы, но у нас бывает такое, что именно коммит транзакции идет довольно долго, плюс, в пределах транзакции данные флашатся периодически, а не одним большим куском, что добавляет сложностей с этим. Увы, пока что думаем над решением.
Так, давайте на примере расскажу как у нас происходит работа.
Есть некоторые счета. Возьмём 2 счёта, А и Б на которых в начале лежат 100 рублей. И есть 3 сервера. Наш основной, стоящий у нас. И 2 «клиентских», опять же, сервера наши, но стоят лояльно у клиентов.
T0: на всех трёх серверах, есть оба счета, их балансы 100, ревизия обоих счетов 1 (изменены оба были в рамках одной транзакции) её считают последней все сервера.
Т1: на обоих клиентских серверах (К1 и К2) происходят операции с счетами . На К1 пополняют А на 20, на другом с Б списывают 10. Они создают у себя транзакции вида «баланс такой-то изменён на Х рублей» и готовят их к экспорту.
Т2: Для К1 настало время синхронизации, он выгружает данные на главный сервер (пусть будет М). М принимает все транзакции с него (включая баланс А изменён на 20 рублей) и начинает обрабатывать. При этом он достаёт из БД ревизию 2 и помечает все изменённые данные ей для сохраннния. Данных много, обрабатывает он их скажем 30 секунд, это время К1 ждёт ответа.
Т2: данные решает синхронизировать К2. У него только одна транзакция списания, М берет из БД ревизию 3 и присваивает обновлённому состоянию счета Б, с балансом 90, и считаем что сразу коммитит данные в БД. Тут же собирает ответные данные для К2, со всеми даннными с ревизией больше 1, туда попадает только счёт Б( поскольку пакет от К1 завис на коммите из-за блокировок в БД например), данные отправляются на К2, он их сохраняет и работает.
Т3: заканчивается импорт данных с К1, данные сохраняются в БД, для К1 отправляются все данные с ревизией больше 1, оба счета уходят корректно.
Теперь состояние системы:
Главный сервер: счёт А 120р, счёт Б 90р., последняя ревизия 3
К1: счёт А 120р, счёт Б 90р, ревизия 3
К2: счёт А 100р, счёт Б 90р, ревизия 3.
Данные по изменению счета А не попали на К2, так как ревизия у них меньше чем была последняя актуальная на момент завершения коммита транзакции в БД.
Сейчас решаем проблему тем, что на время всего импорта берём на М блокировку на родительский объект, объединяющий К1 и К2 ( по сути на все данные с которыми они работают) и Т3 гарантировано заканчивается до Т2.
Это начало работать плохо, когда вместо 2 дочерних серверов их стало 50 с одним набором данных и любой импорт дольше 6 секунд стал приводить к накоплению очереди запросов.
Нет, видимо не совсем точно описал. Отдельные объекты естественно имеют свои собственные ревизии, и версионность идет именно на уровне отдельных объектов. Данные - это заказы клиентов, информация о самих клиентах и оплаты по ним.
Тысячи штук - нет, у нас есть логика "батчинга" обмена, все данные которые надо прогрузить между серверами у нас передаются пакетами по 5000 штук. Плюс, синхронизация каждые 5 минут сделана для того, чтобы данные не копились, а передавались оперативно небольшими пакетами. Периодически, после некоторых массовых операций синхронизируется 100-200 тыс. объектов, но все идет отдельными пакетами, пакеты между собой полностью изолированы и в целом не мешают работе системы. Размеры основной БД - примерно по 7млн записей в 10 основных таблицах.
Проблема наступает только когда параллельно такие пакеты пытаются просинхронизировать 30+ клиентов, и не успевают это сделать за таймауты, что вызывает накопление запросов в очередях.
На импорте отрабатывает достаточно много логики, в том числе валидации + констрейнты на уровне БД, если про технические проблемы - это про это.
Про "данные такие, что клиенты не могут их перезаписать" - не совсем понял вопрос, можно поподробнее, что имеется ввиду?
Версионность по конкретным объектам есть, проблема в том, как мы выдаем ревизии, это мы понимаем, что оно сделано "по историческим причинам", и должно быть переписано, собственно об этом и есть этот вопрос, в каком направлении переписывать. Пока что у нас есть видение только в сторону более мягких блокировок, но такое ощущение что мы делаем то, что и так по хорошему уже реализовано на уровне БД и это костыль.
Написано
Войдите на сайт
Чтобы задать вопрос и получить на него квалифицированный ответ.