Mnesia replication

84 views
Skip to first unread message

Artiom Di

unread,
Jul 10, 2010, 11:01:05 AM7/10/10
to erlang-...@googlegroups.com
Вкратце: поставил rabbitmq (1.7) на двух серверах (ноды repo1@repo1, repo2@repo2), соединил их в кластер, в принципе я ожидал что данные между ними будут реплицироватся,
однако опытным путём выясняется что нифига они не реплицируются.
Пример:
напихал в ноду repo2@repo2 миллион сообщений в очередь test1, нода в РАМе распухла до 500Мб (изначально была 19), как бы, вроде бы всё хорошо.
Если на сервере repo1 запустить rabbitmqctl list_queues видна очередь test1 в которой лежит миллион сообщений и ждёт пока его кто-то обработает.
Данные как бы вроде реплицировались. Однако repo1@repo1 нода так и осталась прежнего размена в RAM т.е. 19мб. И увеличиваться не думает.
Беру для эксперимента и рестартую repo2@repo2 и весь миллион сообщений испаряется! На repo1 в очереди test1 0. Причём сообщения я посылал (в кролике это так называется) durable,
т.е. они должны были идти в таблицу rabbit_durable_queue которая disc_copies.
На диске базы /mnt/rabbitmq/mnesia/repo1@repo1 /mnt/rabbitmq/mnesia/repo2@repo2 занимают сущие килобайты.

Кто виноват и что делать? :)


(repo1@repo1)1> mnesia:info().
---> Processes holding locks <---
---> Processes waiting for locks <---
---> Participant transactions <---
---> Coordinator transactions <---
---> Uncertain transactions <---
---> Active tables <---
rabbit_queue : with 10 records occupying 575 words of mem
rabbit_user_permission: with 1 records occupying 351 words
of mem rabbit_config : with 0 records occupying 316 words
of mem rabbit_reverse_route: with 10 records occupying 536
words of mem rabbit_route : with 10 records occupying 536
words of mem rabbit_exchange: with 7 records occupying 496
words of mem rabbit_durable_route: with 1 records occupying
357 words of mem rabbit_durable_queue: with 1 records
occupying 345 words of mem rabbit_durable_exchange: with 7
records occupying 496 words of mem rabbit_vhost : with 1
records occupying 330 words of mem rabbit_listener: with 2
records occupying 362 words of mem rabbit_user : with 1
records occupying 333 words of mem schema : with 13
records occupying 1891 words of mem ===> System info in version
"4.4.13", debug level = none <=== opt_disc. Directory
"/mnt/rabbitmq/mnesia/repo1@repo1" is used. use fallback at restart =
false running db nodes = [repo2@repo2,repo1@repo1]
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [rabbit_exchange,rabbit_listener,rabbit_queue,
rabbit_reverse_route,rabbit_route]
disc_copies = [rabbit_config,rabbit_durable_exchange,
rabbit_durable_queue,rabbit_durable_route,rabbit_user,
rabbit_user_permission,rabbit_vhost,schema]
disc_only_copies = []
[{repo1@repo1,disc_copies},{repo2@repo2,disc_copies}] =
[schema,rabbit_user, rabbit_vhost,
rabbit_durable_exchange,
rabbit_durable_queue,
rabbit_durable_route,
rabbit_config,
rabbit_user_permission]
[{repo1@repo1,ram_copies},{repo2@repo2,ram_copies}] = [rabbit_listener,
rabbit_exchange,
rabbit_route,
rabbit_reverse_route,
rabbit_queue]
155 transactions committed, 0 aborted, 2 restarted, 7 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
ok


(repo2@repo2)1> mnesia:info().
---> Processes holding locks <---
---> Processes waiting for locks <---
---> Participant transactions <---
---> Coordinator transactions <---
---> Uncertain transactions <---
---> Active tables <---
rabbit_queue : with 10 records occupying 607 words of mem
rabbit_user_permission: with 1 records occupying 351 words
of mem rabbit_config : with 0 records occupying 316 words
of mem rabbit_reverse_route: with 10 records occupying 536
words of mem rabbit_route : with 10 records occupying 536
words of mem rabbit_exchange: with 7 records occupying 496
words of mem rabbit_durable_route: with 1 records occupying
357 words of mem rabbit_durable_queue: with 1 records
occupying 341 words of mem rabbit_durable_exchange: with 7
records occupying 496 words of mem rabbit_vhost : with 1
records occupying 330 words of mem rabbit_listener: with 2
records occupying 362 words of mem rabbit_user : with 1
records occupying 333 words of mem schema : with 13
records occupying 1891 words of mem ===> System info in version
"4.4.13", debug level = none <=== opt_disc. Directory
"/mnt/rabbitmq/mnesia/repo2@repo2" is used. use fallback at restart =
false running db nodes = [repo1@repo1,repo2@repo2]
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [rabbit_exchange,rabbit_listener,rabbit_queue,
rabbit_reverse_route,rabbit_route]
disc_copies = [rabbit_config,rabbit_durable_exchange,
rabbit_durable_queue,rabbit_durable_route,rabbit_user,
rabbit_user_permission,rabbit_vhost,schema]
disc_only_copies = []
[{repo1@repo1,disc_copies},{repo2@repo2,disc_copies}] =
[schema,rabbit_user, rabbit_vhost,
rabbit_durable_exchange,
rabbit_durable_queue,
rabbit_durable_route,
rabbit_config,
rabbit_user_permission]
[{repo1@repo1,ram_copies},{repo2@repo2,ram_copies}] = [rabbit_listener,
rabbit_exchange,
rabbit_route,
rabbit_reverse_route,
rabbit_queue]
76 transactions committed, 3 aborted, 0 restarted, 3 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
ok

Slav Pankratov

unread,
Jul 10, 2010, 11:53:41 AM7/10/10
to erlang-...@googlegroups.com
Я конечно мега-нуб, но у нод не должны быть одинаковые куки?

Artiom Di

unread,
Jul 10, 2010, 12:02:11 PM7/10/10
to Erlang в России
у нод одинаковые куки, и я могу между ними спокойно переключатся из
erl-а, связь существует.

Igor Karymov

unread,
Jul 11, 2010, 12:51:17 AM7/11/10
to Erlang в России
Привет. Репликации сообщений как таковых раббите нету.
Реплицирует он конфигурацию броккера (т.е например эксчендж
объявленный на одной ноде кластера виден и на другой),
что позволяет масштабировать производительность брокера.
Однако надёжности без дополнительных плясок добится не получится.
+ эрланговсий клиент к рабиту сам не умеет переключаться между нодами
кластера (по крайней мере это справедливо для 1.6, нужен ручной
допил).

В качестве развёрнутого ответа, опубликую краткую версию моего
отчёта, который когда-то писал по работе (сейчас он особой ценности
уже не имеет, так как раббит мы решили не использовать). Тут опущены
некоторых интимные подробности и исследований реализации (читай
копания в исходниках :))


Введение.
Разработчиками предоставляется возможность кластеризации серверов
rabbitMQ.
Основное назначение предоставляемого кластера,- распределение
входящей нагрузки и базовые механизмы восстановления системы в случае
отказа.
Инструкцию по запуску серверов и включению их в кластер можно
посмотреть на: http://www.rabbitmq.com/clustering.html
Основная задача данного отчета, - дать представление о возможностях
использования и базовом внутреннем устройстве предоставляемого
решения.
Устройство кластера RabbitMQ.
Кластерное решение rabbitMQ преимущественно основано на механизмах
репликации распределенной БД mnesia.
Информация о очередях (queues), обменниках (exchanges), и маршрутах
(bindings) хранится в таблицах БД,
при этом, таблицы серверов объединенных в кластер поддерживаются в
непротиворечивом состоянии средствами mnesia (реплицируются).

Для подключения узла к кластеру, необходимо, чтобы приложение-сервер
было на нём остановлено, и таблицы БД сервера имели непротиворечивое
состояние с таблицами кластера (зачастую, практически, этого можно
добиться исключительно сбросом таблиц сервера).

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

Механизмы отказоустойчивости.
durable объекты.

Сохранение на диск информации об объектах (очередях, обменниках,
маршрутах), с целью их восстановления при перезапуске сервера. Для
того, чтобы воспользоваться этим механизмом необходимо объявлять
объекты с флагом durable = true.
В случае кластера такой механизм может быть полезен, исключительно для
обеспечения возможности перезапуска кластера с нуля (после остановки
всех узлов), это обусловлено тем, что за время которое узел был
неактивен, состояние таблиц кластера, могло изменится => для его
подключения к кластеру потребуется сброс таблиц базы.

persistent сообщения.
Сообщения такого типа дублируются в файловой системе, до момента их
полной обработки. В случае перезапуска сервера, они будут
восстановлены из слепка в фс и доставлены адресатам. Необходимо
сделать два важных замечания:

* Сообщения сохраняются локально в фс (не в таблицы mnesia !) и
следовательно не реплицируются другими узлами кластера.
* Сообщения именно дублируются в фс (т.е их экземпляр есть как в
фс так и в памяти), это может быть важно, при попытки таким образом
"пропихнуть" сообщения большого объёма (для записи persistent
исключительно в фс сушествует плагин !!!ссылка!!!)


транзакции.

Необходимо на практике проверить реализацию механизма. (как выяснилось
позже, и тут тоже шлак).


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

1. Для процессов пользователей система не выглядит единым целым,
подключение осуществляется к конкретному её узлу (хост, порт...).
2. Для клиентов не обесчена прозрачность отказов (это отчасти
обусловлено пунктом 1, отчасти особенностями применённого подхода
репликации). В случае отказа узла, соединение разрывается явным
образом, и для продолжения работы (даже если оно возможно), клиент
должен содержать логику переподключения к другому узлу системы.


К другим недостаткам можно отнести, отсутствие централизованного
инструмента/механизма контроля состояния (желательно в духе OTP) и
управления кластером.

Как можно заметить, большая часть недостатков лежит в области
обеспечения отказоустойчивости брокера. Сами разработчики утверждают,
что задача повышения надежности имеется у них в roadmap, однако не
имеет высокого приоритета, так как по их словам: "не так много людей
которым это действительно необходимо".

Возможные решения проблемы отказоустойчивости.
Подытоживая всё вышесказанное, можно выделить следующие возможные
варианты решения проблем обеспечения отказоустойчивости с
использованием продукта rabbitMQ:

Создание библиотеки связи.
Идея решения заключается в том, чтобы создать обертку библиотеки-
транспорта, способную самостоятельно переключаться на другие узлы
кластера, в случае отказа текушего узла.
С учетом, особенностей имеющегося кластерного решения, потребуется,
пересоздание очередей на новом экземпляре кластера.[ проверить
реализованно ли это автоматически пока что руки не дошли ]
Так же необходим механизм обеспечения надежности передачи сообщений
(предлагаются два варианта):
Использование встроенного механизма транзакций.
При отправке всех "важных" сообщений используется механизм транзакций.
В случае разрыва соединения, и по истечению таймаута все транзакции
признаются "неудачными".
Данный способ видится наиболее простым в реализации, но влечет за
собой увелечение задержек при передачи сообшений.
Общее хранилише persistent соосбщений.
Суть решения в отправке всех "важных" сообщений как persistent и
создании их обшего разделяемого хранилища.
Без модификации исходных кодов rabbitMQ в качестве такого хранилища
можно использовать общий сетевой ресурс (в том числе DFS).
С модификацией исходного кода любую подходящую СУБД.
Потребуется модификация исходного кода сервера позволяющая всем узлам
кластера работать с единым persistent хранилишем.

Artiom Di

unread,
Jul 11, 2010, 5:21:10 AM7/11/10
to erlang-...@googlegroups.com

On Sat, 10 Jul 2010 21:51:17 -0700 (PDT)
Igor Karymov <ingh...@gmail.com> wrote:
> Привет. Репликации сообщений как таковых раббите нету.

Но она есть в mnesia, насколько я понимаю, и именно mnesia я пытался
заставить реплицировать, что что-то явно делаю не так.

> Реплицирует он конфигурацию броккера (т.е например эксчендж
> объявленный на одной ноде кластера виден и на другой),
> что позволяет масштабировать производительность брокера.
> Однако надёжности без дополнительных плясок добится не получится.
> + эрланговсий клиент к рабиту сам не умеет переключаться между нодами
> кластера (по крайней мере это справедливо для 1.6, нужен ручной
> допил).

Клиент у меня на руби и его я уже научил переключатся между нодами,
если натыкается на неработающую (или текущая вырубилась).



> В качестве развёрнутого ответа, опубликую краткую версию моего
> отчёта, который когда-то писал по работе (сейчас он особой ценности
> уже не имеет, так как раббит мы решили не использовать).

А какая альтернатива? Что вы взяли на вооружение вместо кролика?

Igor Karymov

unread,
Jul 11, 2010, 6:07:21 AM7/11/10
to Erlang в России
> Но она есть в mnesia, насколько я понимаю, и именно mnesia я пытался
> заставить реплицировать, что что-то явно делаю не так.

Там всё не так просто, чтобы мнезия начала реплицировать сообщения, их
надо туда записывать, чего не делается.
Если будете слать сообщения с опцией persistent, то они начнут
сторится на диск (если мне память не изменяет, делается это опять таки
не через мнезию, а через собственный персистер). Но это поможет лишь
тем, что после перзапуска упавшей ноды кластера, она эти сообщения
подберёт, другая живая нода кластера не умеет подбирать сообщения
упавшей ноды

Используем qpid.

Но вообще, классов задач в которых требуется репликация обеспечиваемая
брокером не так и много (т.е таких где профиты перевешивают накладные
расходы).

В случае если у вас обмен сообщений идёт по схеме отличной от "много
клиентов обращаются к множеству равнозначных серверов-обработчиков
разделяющих общее состояние" то возможно, лучше подойдёт вариант с
обычными ack, перепосылками и.т.п.

Выбор как всегда идёт между производительностью и надёжностью.

Artiom Di

unread,
Jul 11, 2010, 6:22:10 AM7/11/10
to erlang-...@googlegroups.com
Hello Igor,

On Sun, 11 Jul 2010 03:07:21 -0700 (PDT)
Igor Karymov <ingh...@gmail.com> wrote:
> Там всё не так просто, чтобы мнезия начала реплицировать сообщения, их
> надо туда записывать, чего не делается.

OMG, очевидно в этом моя главная ошибка. Я отталкивался от мысли что
сообщения лежат в mnesia (видел таблицы rabbitmq_queues,
rabbitmq_durable_queues) и можно выстраивать собственные конфигурации
репликации не вмешиваясь в работу кролика.
Для меня критична надёжность. Огромного количества сообщений у меня
нет, даже пары сотен в секунду.

Чёрт! Какая же досадная ошибка :(

Max Lapshin

unread,
Jul 11, 2010, 6:33:37 AM7/11/10
to erlang-...@googlegroups.com
2010/7/11 Artiom Di <kro...@gmail.com>:

> Для меня критична надёжность. Огромного количества сообщений у меня
> нет, даже пары сотен в секунду.
>
> Чёрт! Какая же досадная ошибка :(

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

Max Lapshin

unread,
Jul 11, 2010, 6:57:47 AM7/11/10
to erlang-...@googlegroups.com
2010/7/11 Artiom Di <kro...@gmail.com>:
>
> Исторически сложилось так что мы на амазоне хостимся (это как D&G,
> дорого и глупо). IO там ниже плинтуса, просто ходячий труп. Не может
> быть и речи о PG, ведь ему IO критично.

Вы уже проверяли? Если вам нужна надежность, вам в любом случае нужно много IO.

Artiom Di

unread,
Jul 11, 2010, 7:01:43 AM7/11/10
to erlang-...@googlegroups.com

On Sun, 11 Jul 2010 14:57:47 +0400
Max Lapshin <max.l...@gmail.com> wrote:
> Вы уже проверяли? Если вам нужна надежность, вам в любом случае нужно
> много IO.

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

Artiom Di

unread,
Jul 11, 2010, 7:26:02 AM7/11/10
to erlang-...@googlegroups.com
On Sun, 11 Jul 2010 03:07:21 -0700 (PDT)
Igor Karymov <ingh...@gmail.com> wrote:
> Там всё не так просто, чтобы мнезия начала реплицировать сообщения, их
> надо туда записывать, чего не делается.

полазил по исходникам
src/rabbit_amqqueue.erl

store_queue(Q = #amqqueue{durable = true}) ->
ok = mnesia:write(rabbit_durable_queue, Q, write),
ok = mnesia:write(rabbit_queue, Q, write),
ok;
store_queue(Q = #amqqueue{durable = false}) ->
ok = mnesia:write(rabbit_queue, Q, write),
ok.

версия tip

Igor Karymov

unread,
Jul 11, 2010, 7:38:19 AM7/11/10
to Erlang в России
On 11 июл, 18:26, Artiom Di <kro...@gmail.com> wrote:
> On Sun, 11 Jul 2010 03:07:21 -0700 (PDT)
>
> Igor Karymov <ingha...@gmail.com> wrote:
> > Там всё не так просто, чтобы мнезия начала реплицировать сообщения, их
> > надо туда записывать, чего не делается.
>
> полазил по исходникам
> src/rabbit_amqqueue.erl
>
> store_queue(Q = #amqqueue{durable = true}) ->
>     ok = mnesia:write(rabbit_durable_queue, Q, write),
>     ok = mnesia:write(rabbit_queue, Q, write),
>     ok;
> store_queue(Q = #amqqueue{durable = false}) ->
>     ok = mnesia:write(rabbit_queue, Q, write),
>     ok.
>
> версия tip

Записали конфигурацию очереди в мнезию. В случае если она дурайбл в
одну таблицу (видимо пишушиюся на диск), в противном случае в другую.
Репликация сообщений от этого не появилась.

Если уж хочется, всё проверить самому, то вот самый простой способ:
на ноде где находится рабит забустить tv:start(). и поглядеть
мнезийные таблицы.

Reply all
Reply to author
Forward
0 new messages