LINUX.ORG.RU

Django+Postgresql репликации

 ,


0

1

Реализую архитектуру в которой мастер-сайт находится на локальных ресурсах и изменения контента и каталога происходят локально с последующей автоматической репликацией (логической) на зеркала в докер-контейнерах размещённых в разных местах (странах), файловый контент раздаётся через CDN, тут схема отточена и прекрасно работает

Но большой зуд вызывает обратный поток данных в виде заказов и запросах (из ЛК) поступающих на зеркальные ноды от клиентов. например, когда на российском и арабских сайтах создаётся заказ с совпадающим порядковым номером

  1. логическая репликация не подходит по причине дублирования примарикей на зеркалах и пересечения на мастербазе

  2. кастомные типы для примарикей доставляют проблемы на уровне django: миграции, тестирование и автоматическое развёртывание

  3. замена примарикей на UUID тоже не самое приятное решение, так как придётся всё переделывать и точно так же не даст 100% гарантии от дублей, а мне не охота потом разруливать эти проблемы

пока вижу решения либо celery очереди выполнять на мастер сайте, но тогда возникает проблема в живучести этих сетевых очередей и потере запросов

либо обернуть всё входящее api врапером и пушить из зеркальной очереди на мастер апи-сервер со своей внутренней очередью с последующей уже написанной пост-обраткой на мастере

может быть ещё какой то вариант есть реализации обратной репликации?

★★

Последнее исправление: fMad (всего исправлений: 2)

Стоп, у тебя есть master остальные бд должны быть в slave, которые забирают с master информацию, и они не имеют право писать в себя инфу, писать можно только в master иначе получается какая-то чушь. А вот как ты будешь писать в master, это уже другой вопрос.

ggrn ★★★★★
()
Ответ на: комментарий от ggrn

Формально я могу сделать публикацию таблицы на слейве и подписку на таблицу на мастере

Только n-дублей примарикей от n-слейвов будут конфликтовать

При это не хочется лишний раз трогать мастер с этими подписками

fMad ★★
() автор топика
Последнее исправление: fMad (всего исправлений: 1)

Вопрос не до конца понятен. Про UUID могу сказать, что за всё время использования UUID в качестве первичных ключей, ни разу не столкнулся с коллизией, включая работу над очень высоконагруженными приложениями.

Вообще, проблемами уникальных ключей много кто занимается, вот, например, статья от Instagram, возможно что-то из там перечисленного подойдёт: https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c

emorozov
()
Ответ на: комментарий от ggrn

похоже что пока лучший вариант сделать так:

[client]>…->[mirror-nginx]->[wsgi]->[django]->[middlware]->[(post/put)+header]->[slave-celery]->[requests]->[master-nginx]->[wsgi]->[django]->[proxy-api]->[replay (post/put)+header]-[requests]->[master-api]->[postgresql]->[PUBLICATION]->[n-slave]

то-есть, мидлварь перехватывает POST/PUT запросы, отдаёт celery, а та гарантрованно пересылает в очередь мастера, где он перезапускает запросы вместе с хидерами на мастере

либо перехватывать api к слейвам на уровне nginx и оттуда класть в slave-celery, тогда можно будет не переделывать авторизацию для клиентов

то-есть, необходимо добавить 1 мидлварь или перестроить nginx, добавить 1 таск, добавить 1 api на мастере

вроде проще чем заменить кучу primary key на uuid

fMad ★★
() автор топика
Последнее исправление: fMad (всего исправлений: 2)
Ответ на: комментарий от emorozov

спасибо за ссылку

думаю это будет последний вариант, для которого придётся перестроить все зависимые таблицы с ключами, обновить все связанные таблицы, переделать шаблоны где используется типа {% url ‘order’ order.id %}, исправить кучу ссылок в админке, прочесать роуты и тесты, заменить миграции и авторазворот

если бы до меня сделали примарикей как uuid этой темы скорее всего не возникло

fMad ★★
() автор топика
Последнее исправление: fMad (всего исправлений: 2)
Ответ на: комментарий от fMad

{% url ‘order’ order.id %}

Вот это точно не поменяется, с чего бы? В urlconf придётся поменять определение url для order, но шаблон точно прежним останется.

И возможно такое решение будет гораздо более простым, нежели придумывать сложные схемы синхронизации через Celery.

Рефакторинг на UUID: много усилий сразу, легче поддержка потом.

Сложная схема синхронизации: меньше усилий сразу, огромный геморрой при поддержке и развитии в будущем.

emorozov
()
Последнее исправление: emorozov (всего исправлений: 1)
Ответ на: комментарий от emorozov

Прочитал статью - пошел Гуглить, что за зверь:

SELECT nextval('insta5.table_id_seq') %% 1024
Похоже весь Интернет уже десять лет гуглит, что за %%

)

Там как-то слишком хитро. Так-то свой, ручной «serial» привязанный к (допустим) имени сервера - первое что в голову приходит.

Правда не пробовал ) Надо будет как-нибудь поиграть в эту игру.

Там, где видел что-то подобное - это вообще вручную синхронизируется по timestamp в заданиях PG. С id записи и id сервера. Наверное, колхозный вариант. Но работает же у людей.

Toxo2 ★★★★
()
Ответ на: комментарий от emorozov

а в чём вы видите сложность?

я пока вижу, что на зеркалах можно выкинуть все celery таски связанные с проектом, можно просто создать контейнер celery с одной единственной таской proxy-requests

так же можно спокойно выкинуть со всех зеркал целую кучу бакендного функционала, что сократит время разворачивание проекта и расходы на его содержание

получается, на зеркалах будет связка контейнеров [nginx[django+postgres[зеркало с подпиской]+celery[proxy-requests]]

развернуть такую схему можно где угодно и она автоматически будет передавать данные на мастер и получать с него обновления, скрывая при этом инфраструктуру сайта

в случае с заменой primary key на UUID появляется необходимость не просто развернуть новую ноду но и открыть на ней порт постгресса, а так же самое неприятное это добавить подписку на мастер-базе, а затем удалять её при отключении зеркала

при этом если зеркала используются в схеме балансировки трафика, то клиент запостивший запрос в техподдержку на одной ноде вынужен быть привязан именно к этой ноде, так как на другой ноде его запрос будет отсутствовать

fMad ★★
() автор топика
Последнее исправление: fMad (всего исправлений: 1)
Ответ на: комментарий от Toxo2

Про %% очевидно опечатка.

В UUID можно выделить несколько бит под тег сервера. Мне кажется, что я даже видел хорошую статью об этом, к сожалению, не сохранил в закладки, а искать лень.

Про синхронизацию по timestamp не понял, можно развернуть, пожалуйста?..

emorozov
()
Ответ на: комментарий от fMad

Я просто не понимаю архитектуру в таком описании. Непонятно, что сейчас есть: один мастер PG и несколько слейвов? Каждый сайт представляет собой почти независимую копию синхронизируемую вручную?

В любом случае, архитектура кажется какой-то неоптимальной.

Передавать все запросы через celery - это супер медленно и ненадежно. Появляется несколько промежуточных этапов, каждый из которых будет вносить непредсказуемую задержку, это немасштабируемо.

Кроме того, Celery - не супернадежное решение, лично я сталкивался в жизни несколько раз с его зависанием.

emorozov
()
Ответ на: комментарий от emorozov

Я просто не понимаю архитектуру в таком описании. Непонятно, что сейчас есть: один мастер PG и несколько слейвов? Каждый сайт представляет собой почти независимую копию синхронизируемую вручную?

Есть 1 pg мастер, на нем есть create publication site for table список таблиц

Есть контейнер зеркала, который при сборке восстанавливает базу из схемы, а затем запускает CREATE SUBSCRIPTION site connection master pg

То есть на разворачивние зеркала тратится 2 минуты, а далее зеркало само отслеживает изменение на мастере и автообновляется

Таким образом да, каждое зеркало действительно является автономной копией мастер сайта и зависит от CREATE SUBSCRIPTION site_[prefix], я предполагаю использовать различные наборы данных для публикаций на мастере под разные задачи. То-есть, одно зеркало может содержать только _ru контент, а другое только _en и тд.

Скорость доставки post запросов думаю вообще не критична, это же как письмо, написал, проверил и отправил, главное чтобы 100% было доставлено

Кроме того, Celery - не супернадежное решение, лично я сталкивался в жизни несколько раз с его зависанием.

зависание celery лечится автоматическим перезапуском задачи? или возможно потерять данные задачи?

к тому же на стороне клиента все POST запросы будут одинаково быстры – заполнил форму, отправил и через 1+n минут тебе перезвонил менеджер

fMad ★★
() автор топика
Последнее исправление: fMad (всего исправлений: 6)
Ответ на: комментарий от emorozov

Про синхронизацию по timestamp не понял, можно развернуть, пожалуйста?

Так оно прямое, как палка - в таблице поле метки времени изменения, триггер на update, и dblink'ом всё, что новое. Ну и всякие незначительные детали вроде асинхронного dblink'а ещё, если надо.

Toxo2 ★★★★
()

Но большой зуд вызывает обратный поток данных в виде заказов и запросах (из ЛК) поступающих на зеркальные ноды от клиентов. например, когда на российском и арабских сайтах создаётся заказ с совпадающим порядковым номером.

Самое простое решение – все данные должны писаться в мастер, а читаться из слейвов. Да, это нагрузка на мастер, но зато 100% защита от дублей + настраивать практически ни чего не надо в джанге

Возможно, хорошим решением будет выделить таблицы для арабов и для русских. И научить сервера баз данных быть мастером для одних таблиц и слейвом для других таблиц.

пока вижу решения либо celery очереди выполнять на мастер сайте, но тогда возникает проблема в живучести этих сетевых очередей и потере запросов

Точно нет, селери может зависнуть/упасть/работать медленно. Очень ненадежно.

либо обернуть всё входящее api врапером и пушить из зеркальной очереди на мастер апи-сервер со своей внутренней очередью с последующей уже написанной пост-обраткой на мастере

Если ты про то, что создать микросервис на мастере, то будут просадки по быстродействию.

dicos ★★
()
Ответ на: комментарий от dicos

в целом я тоже склоняюсь к такой архитектуре, потому что проанализировав работающие API стало очевидно, что запросы на слейвах ожидают ответов, а от результатов зависит фронт и проксирование на мастер с последующей репликацией мастера на слейв выглядит более логичным и естественным

проблема в том, что мастер может быть не доступен, вот буквально сегодня, часть менеджеров сидящих на удалёнке несколько минут не имели доступа к мастеру по причине видимо проблем локальных провайдеров

так, что скорее всего придётся применить комбинированную архитектуру прямого проксирования на мастер + celery очередь в случае не доступности мастера

fMad ★★
() автор топика
Последнее исправление: fMad (всего исправлений: 1)
Ответ на: комментарий от fMad

проблема в том, что мастер может быть не доступен, вот буквально сегодня, часть менеджеров сидящих на удалёнке несколько минут не имели доступа к мастеру по причине видимо проблем локальных провайдеров

Мне кажется тут есть изъян в ситуации, если мастер отвалился. Браузер сделал запрос в слейв, слейв не достучался в мастер, поставил в очередь. Какой ответ слейв должен вернуть браузеру? По идеи какой-то ID, с которым дальше будут какие-то манипуляции. Но его нет. Все сразу становится очень сложным.

Мне кажется лучшей идеей будет создать несколько баз данных. По одной на каждый регион.

dicos ★★
()
20 ноября 2023 г.