Доброго времени суток!
Хочу поделиться с Вами историей о внедрении cache на БД Tarantool и своих особенностях работы.
Я работаю Java разработчиком в телекоммуникационной компании. Основная задача: реализация бизнес логики для платформы, которую компания купила у вендора. Из первых особенностей это работа по soap и практически полное отсутствие кэширования, кроме как в памяти JVM. Все это конечно хорошо до тех пор, пока количество экземпляров приложения не переваливает за два десятка…
В ходе работы и появления понимания особенностей работы платформы была предпринята попытка сделать кэширование. На тот момент была уже запущена MongoDB и как следствие особых положительных результатов как в тесте мы не получили.
При дальнейшем поиске альтернатив и совета моего хорошего друга mr_elzor было принято решение попробовать БД Tarantool.
При беглом изучении появилось только сомнение в lua, так как я раньше на нем не писал от слова «совсем». Но отодвинув все сомнения в сторону приступил к установке. Про закрытые сети и фаерволы думаю мало кому интересно, но советую постараться их обойти и ставить все из публичных источников.
Тестовые сервера с конфигурацией: 8 Cpu, 16 GB Ram, 100 Gb HDD, Debian 9.4.
Установка была по инструкции с сайта. И вот я получил example вариант. Появилась сразу мысль о визуальном интерфейсе, с которым удобно будет работать поддержке. При беглом поиске нашел и настроил tarantool-admin. Работает в Docker и покрывает задачи поддержки на 100%, по крайней мере пока что.
Но давайте поговорим о более интересном.
Следующей мыслью было настроить свой вариант в конфигурации master — slave в рамках одного сервера, так как в документации только примеры с двумя разными серверами.
Потратив какое-то время на понимание lua и описание конфигурации и запускаю мастер.
# systemctl start tarantool@master
Job for tarantool@master.service failed because the control process exited with error code.
See "systemctl status tarantool@master.service" and "journalctl -xe" for details.
Сразу впадаю в ступор и не понимаю почему ошибка, но вижу что он в статусе «loading».
# systemctl status tarantool@master
● tarantool@master.service - Tarantool Database Server
Loaded: loaded (/lib/systemd/system/tarantool@.service; enabled; vendor preset: enabled)
Active: activating (start) since Tue 2019-02-19 17:03:24 MSK; 17s ago
Docs: man:tarantool(1)
Process: 20111 ExecStop=/usr/bin/tarantoolctl stop master (code=exited, status=0/SUCCESS)
Main PID: 20120 (tarantool)
Status: "loading"
Tasks: 5 (limit: 4915)
CGroup: /system.slice/system-tarantool.slice/tarantool@master.service
└─20120 tarantool master.lua <loading>
Feb 19 17:03:24 tarantuldb-tst4 systemd[1]: Starting Tarantool Database Server...
Feb 19 17:03:24 tarantuldb-tst4 tarantoolctl[20120]: Starting instance master...
Feb 19 17:03:24 tarantuldb-tst4 tarantoolctl[20120]: Run console at unix/:/var/run/tarantool/master.control
Feb 19 17:03:24 tarantuldb-tst4 tarantoolctl[20120]: started
Запускаю slave:
# systemctl start tarantool@slave2
Job for tarantool@slave2.service failed because the control process exited with error code.
See "systemctl status tarantool@slave2.service" and "journalctl -xe" for details.
И вижу туже самую ошибку. Вот тут я вообще начинаю напрягаться и не понимать что происходит, так как об этом вообще ничего нет в документации… Но при проверке статуса вижу что он вообще не запустился, хотя пишет что статус «running»:
# systemctl status tarantool@slave2
● tarantool@slave2.service - Tarantool Database Server
Loaded: loaded (/lib/systemd/system/tarantool@.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Tue 2019-02-19 17:04:52 MSK; 27s ago
Docs: man:tarantool(1)
Process: 20258 ExecStop=/usr/bin/tarantoolctl stop slave2 (code=exited, status=0/SUCCESS)
Process: 20247 ExecStart=/usr/bin/tarantoolctl start slave2 (code=exited, status=1/FAILURE)
Main PID: 20247 (code=exited, status=1/FAILURE)
Status: "running"
Feb 19 17:04:52 tarantuldb-tst4 systemd[1]: tarantool@slave2.service: Unit entered failed state.
Feb 19 17:04:52 tarantuldb-tst4 systemd[1]: tarantool@slave2.service: Failed with result 'exit-code'.
Feb 19 17:04:52 tarantuldb-tst4 systemd[1]: tarantool@slave2.service: Service hold-off time over, scheduling restart.
Feb 19 17:04:52 tarantuldb-tst4 systemd[1]: Stopped Tarantool Database Server.
Feb 19 17:04:52 tarantuldb-tst4 systemd[1]: tarantool@slave2.service: Start request repeated too quickly.
Feb 19 17:04:52 tarantuldb-tst4 systemd[1]: Failed to start Tarantool Database Server.
Feb 19 17:04:52 tarantuldb-tst4 systemd[1]: tarantool@slave2.service: Unit entered failed state.
Feb 19 17:04:52 tarantuldb-tst4 systemd[1]: tarantool@slave2.service: Failed with result 'exit-code'.
Но в то же время мастер начал работать:
# ps -ef | grep taran
taranto+ 20158 1 0 17:04 ? 00:00:00 tarantool master.lua <running>
root 20268 2921 0 17:06 pts/1 00:00:00 grep taran
Перезапуск slave не помогает. Интересно почему?
Останавливаю мастер. И выполняю действия в обратном порядке.
Вижу что slave пытается запустится.
# ps -ef | grep taran
taranto+ 20399 1 0 17:09 ? 00:00:00 tarantool slave2.lua <loading>
Запускаю мастер и вижу что он не поднялся и вообще перешел в статус orphan, а slave вообще упал.
# ps -ef | grep taran
taranto+ 20428 1 0 17:09 ? 00:00:00 tarantool master.lua <orphan>
Становится еще интереснее.
В логах на slave вижу что он даже увидел мастера и пытался засинхронизироваться.
2019-02-19 17:13:45.113 [20751] iproto/101/main D> binary: binding to 0.0.0.0:3302...
2019-02-19 17:13:45.113 [20751] iproto/101/main I> binary: bound to 0.0.0.0:3302
2019-02-19 17:13:45.113 [20751] iproto/101/main D> binary: listening on 0.0.0.0:3302...
2019-02-19 17:13:45.113 [20751] iproto D> cpipe_flush_cb: locking &endpoint->mutex
2019-02-19 17:13:45.113 [20751] iproto D> cpipe_flush_cb: unlocking &endpoint->mutex
2019-02-19 17:13:45.113 [20751] main D> cbus_endpoint_fetch: locking &endpoint->mutex
2019-02-19 17:13:45.113 [20751] main D> cbus_endpoint_fetch: unlocking &endpoint->mutex
2019-02-19 17:13:45.113 [20751] main/101/slave2 I> connecting to 1 replicas
2019-02-19 17:13:45.113 [20751] main/106/applier/replicator@tarantuldb-t D> => CONNECT
2019-02-19 17:13:45.114 [20751] main/106/applier/replicator@tarantuldb-t I> remote master 825af7c3-f8df-4db0-8559-a866b8310077 at 10.78.221.74:3301 running Tarantool 1.10.2
2019-02-19 17:13:45.114 [20751] main/106/applier/replicator@tarantuldb-t D> => CONNECTED
2019-02-19 17:13:45.114 [20751] main/101/slave2 I> connected to 1 replicas
2019-02-19 17:13:45.114 [20751] coio V> loading vylog 14
2019-02-19 17:13:45.114 [20751] coio V> done loading vylog
2019-02-19 17:13:45.114 [20751] main/101/slave2 I> recovery start
2019-02-19 17:13:45.114 [20751] main/101/slave2 I> recovering from `/var/lib/tarantool/cache_slave2/00000000000000000014.snap'
2019-02-19 17:13:45.114 [20751] main/101/slave2 D> memtx_tuple_new(47) = 0x7f99a4000080
2019-02-19 17:13:45.114 [20751] main/101/slave2 I> cluster uuid 4035b563-67f8-4e85-95cc-e03429f1fa4d
2019-02-19 17:13:45.114 [20751] main/101/slave2 D> memtx_tuple_new(11) = 0x7f99a4004080
2019-02-19 17:13:45.114 [20751] main/101/slave2 D> memtx_tuple_new(17) = 0x7f99a4008068
И попытка была успешной:
2019-02-19 17:13:45.118 [20751] main/101/slave2 D> memtx_tuple_new(40) = 0x7f99a40004c0
2019-02-19 17:13:45.118 [20751] main/101/slave2 I> assigned id 1 to replica 825af7c3-f8df-4db0-8559-a866b8310077
2019-02-19 17:13:45.118 [20751] main/101/slave2 D> memtx_tuple_new(40) = 0x7f99a4000500
2019-02-19 17:13:45.118 [20751] main/101/slave2 I> assigned id 2 to replica 403c0323-5a9b-480d-9e71-5ba22d4ccf1b
2019-02-19 17:13:45.118 [20751] main/101/slave2 I> recover from `/var/lib/tarantool/slave2/00000000000000000014.xlog'
2019-02-19 17:13:45.118 [20751] main/101/slave2 I> done `/var/lib/tarantool/slave2/00000000000000000014.xlog'
Он даже запустился:
2019-02-19 17:13:45.119 [20751] main/101/slave2 D> systemd: sending message 'STATUS=running'
Но по непонятным причинам он потерял соединение и упал:
2019-02-19 17:13:45.129 [20751] main/101/slave2 D> SystemError at /build/tarantool-1.10.2.146/src/coio_task.c:416
2019-02-19 17:13:45.129 [20751] main/101/slave2 tarantoolctl:532 E> Start failed: /usr/local/share/lua/5.1/http/server.lua:1146: Can't create tcp_server: Input/output error
Попытка снова запустить slave не помогает.
Теперь удаляю файлы созданные инстансами. В моем случае удаляю все из каталога /var/lib/tarantool.
Запускаю сначала slave, а только потом master. И о чудо…
# ps -ef | grep tara
taranto+ 20922 1 0 17:20 ? 00:00:00 tarantool slave2.lua <running>
taranto+ 20933 1 1 17:21 ? 00:00:00 tarantool master.lua <running>
Никакого объяснения такому поведению я не нашел, кроме как «фича данного софта».
Такая ситуация будет появляться каждый раз, если у вас полностью перезагружался сервер.
При дальнейшем анализе архитектуры данного софта выясняется что предусматривается использование только одного vCPU для одного инстанса и еще много ресурсов остается свободными.
В идеологии от n vCPU мы можем поднять master и n-2 slave'ов для чтения.
Учитывая что на тестовом сервере 8 vCPU мы можем поднять мастер и 6 инстансов для чтения.
Копирую файл для slave, правлю порты и запускаю, т.е. добавляется еще несколько slave'ов.
Важно! При добавлении еще одного инстанса вы обязательно должны прописать его на мастере.
Но запускать надо сначала новый slave, а только потом перезапускать мастер.
Пример
У меня уже была запущена конфигурация с мастером и двумя slave.
Я решил добавить третий slave.
Прописал его на мастере и перезапустил сначала мастер, и вот что я увидел:
# ps -ef | grep tara
taranto+ 20922 1 0 Feb19 ? 00:00:29 tarantool slave2.lua <running>
taranto+ 20965 1 0 Feb19 ? 00:00:29 tarantool slave3.lua <running>
taranto+ 21519 1 0 09:16 ? 00:00:00 tarantool master.lua <orphan>
Т.е. мастер у нас стал одиночкой, и репликация развалилась.
Запуск нового slave уже не поможет и закончится ошибкой:
# systemctl restart tarantool@slave4
Job for tarantool@slave4.service failed because the control process exited with error code.
See "systemctl status tarantool@slave4.service" and "journalctl -xe" for details.
А в логах я увидел малоинформативную запись:
2019-02-20 09:20:10.616 [21601] main/101/slave4 I> bootstrapping replica from 3c77eb9d-2fa1-4a27-885f-e72defa5cd96 at 10.78.221.74:3301
2019-02-20 09:20:10.617 [21601] main/106/applier/replicator@tarantuldb-t I> can't join/subscribe
2019-02-20 09:20:10.617 [21601] main/106/applier/replicator@tarantuldb-t xrow.c:896 E> ER_READONLY: Can't modify data because this instance is in read-only mode.
2019-02-20 09:20:10.617 [21601] main/106/applier/replicator@tarantuldb-t D> => STOPPED
2019-02-20 09:20:10.617 [21601] main/101/slave4 xrow.c:896 E> ER_READONLY: Can't modify data because this instance is in read-only mode.
2019-02-20 09:20:10.617 [21601] main/101/slave4 F> can't initialize storage: Can't modify data because this instance is in read-only mode.
Останавливаем мастера и запускаем новый слейв. Так же будет ошибка, как и при первом запуске, но мы увидим что он статусе loading.
# ps -ef | grep tara
taranto+ 20922 1 0 Feb19 ? 00:00:29 tarantool slave2.lua <running>
taranto+ 20965 1 0 Feb19 ? 00:00:30 tarantool slave3.lua <running>
taranto+ 21659 1 0 09:23 ? 00:00:00 tarantool slave4.lua <loading>
Но при запуске master новый slave падает, а master не переходит с статус «running».
# ps -ef | grep tara
taranto+ 20922 1 0 Feb19 ? 00:00:29 tarantool slave2.lua <running>
taranto+ 20965 1 0 Feb19 ? 00:00:30 tarantool slave3.lua <running>
taranto+ 21670 1 0 09:23 ? 00:00:00 tarantool master.lua <orphan>
В этой ситуации выход один. Как и писал раньше удаляю файлы созданные инстансами и запускаю сначала slave'ы, а затем master.
# ps -ef | grep tarantool
taranto+ 21892 1 0 09:30 ? 00:00:00 tarantool slave4.lua <running>
taranto+ 21907 1 0 09:30 ? 00:00:00 tarantool slave3.lua <running>
taranto+ 21922 1 0 09:30 ? 00:00:00 tarantool slave2.lua <running>
taranto+ 21931 1 0 09:30 ? 00:00:00 tarantool master.lua <running>
Все запустилось успешно.
Вот так методом проб и ошибок выяснил как надо настраивать и запускать корректно репликацию.
В итоге была собрана следующая конфигурация:
2 сервера.
2 master. Горячий резерв.
12 slave'ов. Все активные.
В логике работы tarantool был применен http.server что бы не городить дополнительный адаптер(вспоминаем про вендора, платформу и soap) или прикручивать к каждому бизнес-процессу библиотеку.
Для того что бы избежать расхождения между мастерами, на балансировщике (NetScaler, HAProxy или любой другой ваш любимый) прописываем правило резерва, т.е. операции insert, update, delete идут только на первый активный мастер.
В это время второй просто реплицирует записи с первого. Slave'ы сами подключаются к первому указанному мастеру из конфигурации, что в данной ситуации нам и необходимо.
На lua реализовал CRUD операции для key-value. На данный момент этого достаточно для решения поставленной задачи.
В виду особенности работы с soap был реализован проксирующий бизнес-процесс, в котором и заложена логика работы с тарантулом по http.
Если данные по ключу присутствуют, то они возвращаются сразу. Если нету, то идет запрос в мастер систему, и сохраняются в БД Tarantool.
В итоге один бизнес-процесс в тестах обрабатывает до 4к запросов. При этом время ответа от тарантула ~1мс. В среднем время ответа до 3мс.
Вот небольшая информация с тестов:
Было 50 бизнес процессов ходят в 4 мастер системы и кэшируют данные у себя в памяти. Дублирование информации в полный рост на каждом инстансе. Учитывая что java и так любит память… перспектива не самая лучшая.
Сейчас
50 бизнес процессов запрашивают информацию через кэш. Теперь информация с 4 инстансов мастера хранится в одном месте, и не кэшируются в памяти на каждом инстансе. Удалось значительно сократить нагрузку на мастер систему, отсутствуют дубли информации и сократилось потребление памяти на инстансах с бизнес логикой.
Пример размера хранения информации в памяти тарантула:
Под конец дня эти цифры могут увеличится в два раза, но «просадки» в производительности нет.
В бою текущий вариант создает 2к — 2,5к запросов в секунду реальной нагрузки. Среднее время ответа аналогично тестам до 3мс.
Если посмотреть htop на одном из серверов с tarantool, то мы увидим что они «прохлаждаются»:
Итоги
Не смотря на все тонкости и нюансы работы БД Tarantool можно добиться большой производительности.
Надеюсь данный проект будет развиваться и данные неудобные моменты будут решены.
Автор: seet61