/hz-queue

Tests of hazelcast queue

Primary LanguageJavaMIT LicenseMIT

hz-queue

Tests of hazelcast queue

hazelcast 3.2.3

Plan

ХЗ - хороший продукт. Общие декларации.. На больших и критичных задачах для бизнеса нам нужно точное определение применимости и без испытаний не обойтись.

Параметры JVM для всех тестов:

  • ограничиваем память
  • сбрасываем дамп при OOM
  • убиваем приложение при OOM

-Xms64m -Xmx64m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp -XX:OnOutOfMemoryError="kill -9 %p"

Test1

Один узел, все в памяти

460k 0.026ms - OOM

Test2

Один узел, MockQueueStore. Укажем параметр "memory-limit=0" для того чтобы совсем исключить хранение данных в памяти (по умолчанию хранится 1000 элементов).

980k 0.030ms - OOM

Ожидали отсутствие OOM. Но реализация очереди у hazelcast такова, что этого не происходит. Внутри QueueContainer, качестве элемента очереди используется объект QueueItem содержащий уникальный ключ элемента очереди и данные элемента. В случае подключения хранилища могут не хранится данные в памяти, но сами вспомогательные объекты QueueItem остаются. Этим объясняется увеличение емкости - 980k против 460k в первом тесте.

Test3

Читая исходный код hazelcast подглядел одну особенность реализации \ баг. Один узел, MockQueueStore, используем транзакции при добавлении элемента

250k 0.061ms - OOM

Получили еще меньшую чем в первом тесте вместимость. Это связано с двумя моментами. Первый - в качестве внутреннего элемента очереди используется не QueueItem а его потомок - TxQueueItem. Он обладает доп полями и побольше потребляет память. Второй момент - для элементов помещаемых в рамках транзакции не используется сброс данных в хранилище. Они остаются в памяти.

Test4

Два узла (по умолчанию) - все в памяти. 100 тыс записей. Пишем и читаем с владельца очереди.

INFO: add 100000 0.255ms INFO: poll 100000 0.223ms

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

Test 4_1

Тоже что Test4 но пишем и читаем с другого узла (не владельца очереди)

INFO: add 100000 0.215ms INFO: poll 100000 0.201ms

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

Test5

Тоже что Test4-1 + Роняем владельца очереди после ее заполнения и проверяем очередь читая с другого узла.

INFO: add 100000 0.267ms INFO: poll 100000 0.025ms

Один оставшийся узел начинает работать на порядок быстрее. Он остается один в кластере и не тратить ресурсы на коммуникации по созданию бэкапа.

Test6

Тоже что и Test5, но выключаем бэкап.

INFO: add 100000 0.022ms

Большая скорость работы с владельцем очереди. Он не тратит ресурсы на бэкап. Но после его падения - вся очередь теряется.

Test7

Тоже что и Test6, но с подключенным хранилищем в памяти.

INFO: add 100000 0.023ms INFO: poll 100000 0.018ms

Видим что очередь восстановилась на оставшемся узле. В процессе восстановления очереди сначала считываются все ключи из хранилища и определяется наибольшее значение из них для дальнейшей генерации новых. Сразу всеми элементами очереди, но без данных, заполняется внутреннее хранилище списка LinkedList (в реализации QueueContainer). Для того чтобы порядок элементов в очереди после восстановления из хранилища не менялся - хранилище должно выдавать их в правильном порядке в наборе (Set). Далее по необходимости подгружаются данные из хранилища. Подгрузка идет пачками. По умолчанию по 250 штук.