Tests of hazelcast queue
hazelcast 3.2.3
ХЗ - хороший продукт. Общие декларации.. На больших и критичных задачах для бизнеса нам нужно точное определение применимости и без испытаний не обойтись.
Параметры JVM для всех тестов:
- ограничиваем память
- сбрасываем дамп при OOM
- убиваем приложение при OOM
-Xms64m -Xmx64m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp -XX:OnOutOfMemoryError="kill -9 %p"
Один узел, все в памяти
460k 0.026ms - OOM
Один узел, MockQueueStore. Укажем параметр "memory-limit=0" для того чтобы совсем исключить хранение данных в памяти (по умолчанию хранится 1000 элементов).
980k 0.030ms - OOM
Ожидали отсутствие OOM. Но реализация очереди у hazelcast такова, что этого не происходит. Внутри QueueContainer, качестве элемента очереди используется объект QueueItem содержащий уникальный ключ элемента очереди и данные элемента. В случае подключения хранилища могут не хранится данные в памяти, но сами вспомогательные объекты QueueItem остаются. Этим объясняется увеличение емкости - 980k против 460k в первом тесте.
Читая исходный код hazelcast подглядел одну особенность реализации \ баг. Один узел, MockQueueStore, используем транзакции при добавлении элемента
250k 0.061ms - OOM
Получили еще меньшую чем в первом тесте вместимость. Это связано с двумя моментами. Первый - в качестве внутреннего элемента очереди используется не QueueItem а его потомок - TxQueueItem. Он обладает доп полями и побольше потребляет память. Второй момент - для элементов помещаемых в рамках транзакции не используется сброс данных в хранилище. Они остаются в памяти.
Два узла (по умолчанию) - все в памяти. 100 тыс записей. Пишем и читаем с владельца очереди.
INFO: add 100000 0.255ms INFO: poll 100000 0.223ms
По умолчанию включен синхронный бэкап на 1 узел. Видим потерю производительности на порядок по сравнению с первым тестом. Владелец очереди тратит ресурсы на бэкап.
Тоже что Test4 но пишем и читаем с другого узла (не владельца очереди)
INFO: add 100000 0.215ms INFO: poll 100000 0.201ms
Видим сравнимую скорость. Также как в тесте Test4 оба узла выполняют схожий набор операций и от перемены мест слагаемых результат практически не меняется. Фактическая работа идет с владельцем очереди.
Тоже что Test4-1 + Роняем владельца очереди после ее заполнения и проверяем очередь читая с другого узла.
INFO: add 100000 0.267ms INFO: poll 100000 0.025ms
Один оставшийся узел начинает работать на порядок быстрее. Он остается один в кластере и не тратить ресурсы на коммуникации по созданию бэкапа.
Тоже что и Test5, но выключаем бэкап.
INFO: add 100000 0.022ms
Большая скорость работы с владельцем очереди. Он не тратит ресурсы на бэкап. Но после его падения - вся очередь теряется.
Тоже что и Test6, но с подключенным хранилищем в памяти.
INFO: add 100000 0.023ms INFO: poll 100000 0.018ms
Видим что очередь восстановилась на оставшемся узле. В процессе восстановления очереди сначала считываются все ключи из хранилища и определяется наибольшее значение из них для дальнейшей генерации новых. Сразу всеми элементами очереди, но без данных, заполняется внутреннее хранилище списка LinkedList (в реализации QueueContainer). Для того чтобы порядок элементов в очереди после восстановления из хранилища не менялся - хранилище должно выдавать их в правильном порядке в наборе (Set). Далее по необходимости подгружаются данные из хранилища. Подгрузка идет пачками. По умолчанию по 250 штук.