Привет! Сегодня я хочу познакомить вас с ПО, которое мы разрабатываем в нашей компании — кластерной СУБД и сервером приложений на языке Rust. Мы профессионально занимаемся созданием и эксплуатацией решений на основе Tarantool и с некоторых пор начали разработку своего ПО, о котором и пойдёт речь.
Примечание: мы публикуем в нашем корпоративном блоге обзоры и статьи про Picodata, в которых вы найдете много интересных технических подробностей и деталей реализации наших продуктов. Материал ниже был впервые опубликован на Хабре в июне 2023 г.
В этой статье я расскажу, почему и зачем существует наш продукт, какие у него преимущества, и как, в конце концов, его покрутить в руках прямо сейчас.
Но сначала немного теории.
Как известно, Tarantool — это открытая резидентная (in-memory) NoSQL-база данных с сервером приложений на борту. Данные в Tarantool персистентны за счет использования механизмов журнала упреждающей записи и точек восстановления на диске. При этом, в зависимости от размера БД, можно выбирать разные движки хранения: для полностью резидентного режима работы, когда все данные помещаются в ОЗУ и режима хранения «холодных» данных на диске, если их много.
Основная идея — выполнение приложений рядом с данными, за счёт чего снижаются сетевые и транзакционные издержки. В Tarantool стандартно имеется встроенный интерпретатор Lua, с возможностью трансляции just-in-time, низкоуровневое API доступа по первичным и вторичным индексам, а также язык ANSI SQL. Tarantool применяется во множестве проектов, где требуется обработка больших потоков данных, для ускорения кластерных вычислений, а также в качестве кэширующей БД.
Picodata — это дальнейшее развитие истории Tarantool, в которой учтен опыт эксплуатации этой СУБД и предложены решения как архитектурных, так и функциональных недостатков открытой версии Tarantool. Также, наше ПО проще запускать, настраивать и поддерживать в рабочем состоянии благодаря единой точке входа и интеграции всего инструментария в одном исполняемом файле. Мы создавали Picodata как изначально кластерную СУБД, которой удобно пользоваться. Если не верите, что российская СУБД может быть удобной, попробуйте — в конце этой статьи есть раздел Практикум, где можно за несколько простых шагов собрать кластер самому на паре-тройке виртуальных машин или на вашем локальном компьютере. Сейчас же будет немного теории о том, как вообще работает распределенный кластер, что именно не так в “ванильном” Tarantool и что нам пришлось сделать чтобы это исправить.
Как устроен кластер Tarantool
Основное применение Tarantool — создание не только быстрых, но еще и отказоустойчивых и масштабируемых систем. Для этого используется два базовых механизма: репликация данных, гарантирующая сохранность данных при выходе из строя одного из узлов, и шардирование, позволяющее гибко разделять большие объёмы данных на независимые партиции, каждая из которых обслуживается одним набором реплик. При увеличении нагрузки или объёма данных администратор может добавить в кластер новые узлы.
Однако, интегрированное решение в виде такого кластера не появляется из ниоткуда, ведь сам Tarantool — это лишь сервер приложений, который выполняется на одном ядре процессора.
Решения на Tarantool используют комбинацию из нескольких “строительных блоков”, к которым, помимо собственно БД, относятся библиотека/модуль vshard для включения шардирования, модуль Cartridge для «объединения» отдельных инстансов в кластер и управления репликацией, модуль migrations для обновления схемы данных, отдельно стоящий координатор фейловера (Etcd) и многое другое, включая средства развертывания для управления конфигурацией узлов (Ansible), или управления их жизненным циклом (K8S).
С одной стороны, такая комбинация обеспечивает гибкость в проектировании продуктовых решений, с другой — усложняет администрирование системы. Каждый компонент/библиотека является «вещью в себе» в том смысле, что отвечает только за свою часть функциональности и может быть просто “не в курсе” работы других компонентов.
В результате, на практике решение на основе Tarantool быстро становится сложным, трудноуправляемым и пестрым как лоскутное одеяло. Наличие разных компонентов, нескольких API и необходимость в ручном вмешательстве при любом сбое на границе модулей или систем усложняют разработку и внедрение. Эксплуатация кластера тоже страдает: чинить кластер, который ушел в read-only из-за временного разрыва сети между датацентрами тоже приходится руками, и результат не всегда предсказуем.
Преимущества Picodata
Если коротко, то из всех преимуществ Picodata основное — возможность без лишних затрат запустить консистентную и действительно кластерную СУБД с сервером приложений. Наша система способна эффективно заменить сложносоставные и слабо интегрированные друг с другом компоненты Cartridge+Vshard+Migrations, используемые при эксплуатации Tarantool.
В Picodata необходимые функции кластера интегрированы в едином программном продукте. Кроме того, мы оптимизировали некоторые менее удачные алгоритмы, такие как управление конфигурацией кластера и схемой данных через двухфазный коммит, на более современные и переписали всё на языке Rust.
Это лишь часть того, что мы делаем. Например, помимо Lua, мы поддерживаем также исполнение кода на Rust и разрабатываем собственный крейт (Tarantool Rust SDK) для создания модулей для Tarantool на Rust. Это позволит писать более надежное ПО для работы в кластере.
С точки зрения эксплуатации, управлять кластером Picodata проще за счёт меньшего числа точек отказа и, как следствие, уменьшения нагрузки на администраторов. Топологию кластера можно менять даже после выхода из строя большей части узлов кластера, а горизонтальное масштабирование (перебалансировка данных, миграция и перераспределение нагрузки приложения, работающего в распределённом сервере приложений) происходит автоматически: администратору достаточно лишь добавить оборудование в кластер, после чего Picodata сама сформирует репликасеты, восстанавит узлы после деградации или аварии и запустит ребалансировку данных.
“Домены отказа” — умное формирование репликасетов
Для того чтобы управлять отказоустойчивостью кластера смог алгоритм, а не системный администратор, мы перешли от императивного интерфейса взаимодействия с менеджером кластера, при котором ПО исполняет команды добавления и удаления узлов в репликасеты, ребалансировки, к декларативному: DBA добавляет или удаляет в кластер вычислительные ресурсы, и описывает их местоположение, а решение о том, что с ними делать, принимает алгоритм. Дополнительные параметры (метаданные), которые описывают узлы кластера, мы назвали “доменами отказа” (failure domains). Администратор описывает физическое размещение серверов (название дата центра, стойки или компьютера), а Picodata уже делает так, чтобы в один репликасет попадали реплики из разных локаций, чтобы в случае аварии в одном датацентре копия данных оставалась на другом, физически удаленном сервере в другом ДЦ. Репликация в Picodata работает таким образом, чтобы обеспечить доступность данных в случае аварии как в пределах одного датацентра, так и при потере связности между двумя или более ДЦ.
Picodata изначально была задумана как кластерная СУБД. Сколько бы ни было репликасетов в кластере, наше ПО поддерживает единую схему данных на всех узлах и автоматически управляет её версионированием. Это — заслуга алгоритма репликации Raft, объединяющего все узлы кластера и используемого внутри Picodata для распространения изменений схемы данных, конфигурации шардинга, ролей. В итоге мы получили аналогичный Cartridge сервис, но основанный на линеаризуемом консистентном хранилище метаданных. Отдельный модуль Picodata с названием “губернатор” (governor) следит за переносом голосующих узлов Raft в случае аварии, выполняет операции по созданию или удалению пользователей, таблиц, индексов, добавлению или удалению узлов в кластер. Своё собственное состояние он также сохраняет на всех узлах кластера, используя реплицируемый raft-журнал. “Губернатор” позволяет добавлять новые узлы в кластер и менять схему данных, не требуя, чтобы все 100% существующих узлов были доступны. Это сводит к минимуму необходимость делать повторную инициализацию (bootstrap) кластера, или восстанавливать его из бэкапа. Если в Cartridge миграции (изменения схемы данных) возможны только после подключения отдельного модуля migrations, то в Picodata такая функциональность доступна сразу: схемы данных хранятся в raft-журнале, а на каждом инстансе есть таблицы с записями Raft.
Консистентность кластера (и его данных) на уровне алгоритма Raft достигаются так: если в строю находится больше половины «голосующих» (в терминах Raft) узлов, то схема данных и топология по-прежнему остаются доступными и на чтение и на запись, не требуют вмешательства оператора при аварии, и автоматически применяются на недоступных узлах после возвращения их в кластер. Ручная корректировка схемы данных на отдельных узлах в Picodata не требуется. В любой момент кластер можно продолжать конфигурировать: например, добавлять в него новые инстансы или удалять (expel) старые (об этом чуть ниже).
В больших кластерах не все узлы голосуют (участвуют в кворуме). При разработке Picodata мы решили остановиться на схеме из 5 голосующих узлов. Таким образом, если у вас распределенная система из 100 узлов, правом голоса будут обладать всегда только 5 из них (какие именно — решает “губернатор”). Из этих 5 узлов Raft требует наличия в строю большинства, т.е. минимум 3-х. Выполнение этого условия гарантирует, что кластер останется рабочим и доступным на запись. Если представить, что одновременно вышли из строя 3 из 5 узлов, то запись через Raft прекратится, и кластер останется доступным только на чтение, но при этом на оставшихся обычных узлах сохранится вся история изменений. На самом деле, такая ситуация крайне маловероятна не только сама по себе, но и благодаря использованию failure domain, о котором мы писали выше: голосующие узлы будут удалены друг от друга и размещаться на физически разных серверах. Если у нас есть в распоряжении два датацентра и некоторое (>5) число узлов с экземплярами Picodata, то Raft распределит по 2 голосующих узла в каждом ДЦ, а 5-й постарается разместить в третьей локации вне этих ДЦ и будет использовать его исключительно как арбитра: при недоступности одного из ДЦ, арбитр обеспечит наличие кворума: создаст большинство (3) голосующих узлов. Таким образом, при возникновении проблем, например при потере сетевой связности с одним ДЦ, кластер останется доступным для записи и изменения схемы данных.
На самом деле, ситуация «датацентр в огне» случается редко; гораздо чаще инженеры по эксплуатации сталкиваются с ограниченным незапланированным downtime, всевозможными сетевыми проблемами и прочими временными нарушениями связности между узлами кластера.
Raft vs. Cartridge: устойчивое к сбоям шардирование данных
Преимущество Raft в архитектуре распределенной системе заметно и при шардировании данных. Функция шардирования, которую в Tarantool обеспечивает модуль vshard, не может нормально работать в случае проблем со связностью между узлами. При этом, не так важно, по какой причине не проходит ping до сервера с инстансом Tarantool: отключился ли сервер насовсем, либо это кратковременная проблема. Существует много вариантов downtime и network partition, но в любом случае, с точки зрения vshard такой инстанс считается недоступным и приводит к интенсивному наполнению журнала сообщениями об ошибках. Для кластера Tarantool-Cartridge такая ситуация показана на скриншоте ниже:
Недоступность инстанса приводит к тому, что, подключаясь к веб-интерфейсу Cartridge с него и с других инстансов, мы видим разную картину: в зависимости от точки наблюдения, состав недоступных реплик будет отличаться:
Подобное поведение Cartridge связано с особенностями реализации в нём механизма discovery, а также с использованием для миграций в кластере двухфазного коммита (2pc). Напомним, что алгоритм двухфазного коммита — классический централизованный оптимистичный алгоритм распределенного консенсуса из баз данных для подтверждения распределенных транзакций. Это значит, что после того, как мы завершили транзакцию, ее надо атомарно подтвердить на всех участниках (participants). В сочетании с использованием централизованной конфигурации (clusterwide config) для изменения топологии кластера, без ручного вмешательства Cartridge требует для работы доступности всех узлов. Такое строгое требование может привести к остановке записи в кластер даже если ломается всего один узел. Хуже того, возможная потеря консистентности означает, что кластер выводится в read-only и далее вы вручную разбираетесь с версиями схемы данных на отдельных узлах.
Использование Picodata позволяет избежать неприятной ситуации недоступности кластера на запись. Однако, обрабатывать недоступность отдельных узлов все равно приходится, так как при эксплуатации проблемы случаются независимо от желания админа. Управлением состояниями отдельных узлов также занимается модуль “губернатор”; для отслеживания состояния узлов мы ввели концепцию уровней, или “грейдов”. Для чего она нужна и какую пользу приносит — см. ниже.
Управление уровнями инстансов
Если связь с узлом прерывается, то объективно его состояние узнать невозможно. Часть узлов кластера может видеть инстанс как доступный, а другая — как недоступный. В случае асимметричного разрыва сетевой связности, инстанс может видеть весь кластер или большую часть узлов, а трафик в обратную сторону не будет ходить, или доставлять лишь часть пакетов. В такой ситуации “самооценка” инстанса в виде его транслируемого параметра status только затуманивает картину. Для соблюдения порядка необходим некий авторитет, централизованная сущность, которая будет следить за состояниями инстансов и транслировать на весь кластер единое видение конфигурации каждого из них. Именно эту роль выполняет “губернатор”, который представляет собой отдельный fiber, выполняющийся на лидере raft-группы. Для принятия решения о реконфигурации кластера “губернатор” оперирует “грейдами” (grade) инстансов. Grade — это то, как воспринимается инстанс «глазами» его соседей, т. е. взгляд на настройку инстанса со стороны. Вся информация о потере доступности от всех участников кластера поступает “губернатору”, который на ее основе автоматически принимает решение о реконфигурации (например, решает убрать недоступную реплику из репликасета или конфигурации шардинга) и затем спускает это решение остальным узлам. Такой подход предотвращает ситуации, когда на “мертвую” реплику в кластере продолжают идти запросы на запись или чтение, что иногда происходит с Tarantool+Cartridge. Для того, чтобы учесть пожелания системного администратора, мы предусмотрели две категории грейдов: текущий (current) и желаемый (target). Например, для того чтобы остановить инстанс, алгоритм меняет его target-грейд, а “губернатор” оперативно следит за тем, чтобы current оперативно переходил в target и, главное, об этом узнали все участники кластера.
Удаление инстанса из кластера производится отдельной командой picodata expel. Как только запрос на expel дойдет до губернатора, он отметит, что указанный инстанс удален, и отправит команды на переконфигурацию остальным инстансам. Если удаляемый инстанс запущен, он завершится, если не запущен — примет информацию о своем удалении при запуске и затем завершится. При последующих запусках удаленный инстанс будет сразу завершаться. Даже если его попытаться запустить извне с помощью Systemd, Docker или другого инструмента, то он все равно уж не будет принят в кластер.
Наверное, на данном этапе теории достаточно, и можно перейти к практическому примеру.
Практикум
Для запуска Picodata от вас потребуется совсем немного вводных данных:
- имя кластера
- фактор репликации
В большинстве случаев этого достаточно, хотя, если реализовывать промышленную конфигурацию, то стоит добавить еще “домен отказа”, о котором мы писали выше.
Минимальный вариант кластера
Picodata может создать кластер, состоящий всего из одного экземпляра/инстанса. Обязательных параметров у него нет, что позволяет свести запуск к выполнению всего одной простой команды:
picodata run
В дальнейшем можно добавлять сколько угодно других инcтансов, указывая им параметрыpeer
и listen
(подробнее об этом ниже). Все инстансы будут подключаться к созданному на старте кластеру. За первоначальное создание (bootstrap) кластера отвечает встроенный в Picodata механизм похожий на plug&play, который ищет остальных участников кластера на основе частичной информации. Исходными данными для discovery являются переданные пользователем аргументы командной строки (picodata …), либо, если их недостаточно, значения по умолчанию. Он реализует взаимное обнаружение пиров (инстансов) по принципу “многие ко многим” и призван быстро и надежно определить бутстрап-лидера, т.е. лидера raft-группы. Наличие единого лидера нужно для объединения всех инстансов именно в один кластер, а не несколько разрозненных. Механизм обнаружения предполагает, что часть его сообщений может не дойти до адресатов, либо доходить очень долго, и поэтому может работать с неполным набором данных, учитывая возможные проблемы с сетевой связностью.
Каждому инстансу следует задать отдельную рабочую директорию (параметр --data-dir
), а также указать адрес и порт для приема соединений (параметр --listen
) в формате <host>:<port>
. В качестве хоста можно указывать как IP-адрес, так и DNS-имя, по желанию и в зависимости от задач. Например, если используется Kubernetes, то pod с инстансом Picodata может при перезапуске получить другой IP-адрес, и в таком случае использование DNS будет более оправданным. Однако, для простоты и удобства в примерах ниже я буду использовать IP.
Фактор репликации по умолчанию равен 1 — каждый инстанс образует отдельный репликасет. Если для listen указать только порт, то будет использован IP-адрес по умолчанию (127.0.0.1):
picodata run --data-dir i1 --listen :3301
picodata run --data-dir i2 --listen :3302
picodata run --data-dir i3 --listen :3303
Значение параметра listen не хранится в кластерной конфигурации внутри СУБД и может меняться при перезапуске инстанса. Параметр listen может содержать только номер порта (по умолчанию для первого инстанса используется 3301), что означает указание использовать текущий хост. В настоящем распределенном кластере указывать IP-адрес в данном параметре обязательно.
Кластер из нескольких инстансов
Покажем более подробно создание кластера Picodata из нескольких инстансов. Для данного примера допустим, что в локальном кластере (127.0.0.1/localhost
) будет 4 инстанса с фактором репликации равным 2, что означает наличие 2-х репликасетов. Запустим первый инстанс, указав необходимые параметры:
picodata run --init-replication-factor=2 --listen :3301 --data-dir=i1
Следует обратить внимание на следующие моменты:
- Параметр
init-replication-factor
задается лишь один раз в момент первого запуска кластера и дальше не требуется. Число 2 в примере означает, что репликасет будет состоять из 2 реплик: активной и резервной. Перезапускать данный инстанс в дальнейшем можно без этого параметра, но если попробовать поменять его при перезапуске, то произойдёт ошибка — расширять фактор репликации существующего кластера нужно отдельной командой. - Параметр
data-dir
указывает на директорию, в которой будут храниться персистентные данные текущего инстанса (файлы*.snap
и*.xlog
). Если при первом запуске задать несуществующую директорию, то она будет автоматически создана.
Будет создан кластер со стандартным названием demo
, т.к. явно не указан параметр cluster-id.
Аналогично следует запустить остальные 3 инстанса, указав им уникальные порты и отдельные рабочие директории. В случае с кластером на удаленных узлах потребуется также указать данным инстансам параметры peer
и advertise
. Для чего они нужны?
Во-первых, надо дать инстансам возможность обнаружить друг друга и собрать все найденные экземпляры Picodata в один кластер. Для этого в параметре --peer
нужно указать адрес какого-либо соседнего инстанса. По умолчанию значение параметра --peer
установлено в 127.0.0.1:3301
. Параметр --peer
не влияет больше ни на что, кроме механизма обнаружения других инстансов.
Во-вторых, параметр --advertise
используется для установки публичного IP-адреса/DNS-имени и порта инстанса. Параметр в явном виде сообщает, по какому адресу остальные инстансы должны обращаться к текущему.
Работа с кластером через API
Наше недавнее нововведение — полноценный Lua API, в котором можно следить за состоянием кластера, управлять инстансами и работать с данными. API доступен после подключения к инстансу кластера командой picodata connect <host:port>
(если вы запустили инстанс и остались в его консоли, то делать ничего не надо).
Все функции имеют префикс pico.*, есть подсказка и автодополнение по Tab. Подробнее о них можно почитать в нашей документации, а пока посмотрим как можно мониторить кластер.
Мониторинг состояния кластера
Для мониторинга состояния кластера удобно использовать команды, показывающие состояние raft-группы, отдельных инстансов и собранных из них репликасетов. Примеры команд и их выводов приведены ниже.
Узнать лидера raft-группы, а также ID и статус текущего инстанса:
pico.raft_status()
Пример вывода:
---
- term: 2
leader_id: 1
raft_state: Leader
id: 1
...
Просмотр состава raft-группы и данных инстансов:
box.space._pico_instance:fselect()
Пример вывода:
- - +-----------+-------------+-------+-------------+-----------------------+-------------+------------+--------------+
- |instance_id|instance_uuid|raft_id|replicaset_id| replicaset_uuid |current_grade|target_grade|failure_domain|
- - +-----------+-------------+-------+-------------+-----------------------+-------------+------------+--------------+
- | "i1" |"68d4a766..."| 1 | "r1" |"e0df68c5-e7f9-395f..."|["Online",1] |["Online",1]| {} |
- | "i2" |"24c4ac5f..."| 2 | "r1" |"e0df68c5-e7f9-395f..."|["Online",1] |["Online",1]| {} |
- | "i3" |"5d7a7353..."| 3 | "r2" |"eff4449e-feb2-3d73..."|["Online",1] |["Online",1]| {} |
- | "i4" |"826cbe5e..."| 4 | "r2" |"eff4449e-feb2-3d73..."|["Online",1] |["Online",1]| {} |
- - +-----------+-------------+-------+-------------+-----------------------+-------------+------------+--------------+
Просмотр списка репликасетов, их UUID и веса (наполненности):
box.space._pico_replicaset:fselect()
Пример вывода:
---
- — +-------------+--------------------------------------+---------+------+----------------------+
- |replicaset_id| replicaset_uuid |master_id|weight|current_schema_version|
- +-------------+--------------------------------------+---------+------+----------------------+
- | "r1" |"e0df68c5-e7f9-395f-86b3-30ad9e1b7b07"| "i1" | 1 | 0 |
- | "r2" |"eff4449e-feb2-3d73-87bc-75807cb23191"| "i3" | 1 | 0 |
- +-------------+--------------------------------------+---------+------+----------------------+
...
Создание спейса
Перед тем как начать пользоваться СУБД, необходимо подключиться к инстансу кластера (picodata connect <host:port>
) и создать таблицу, которая в терминологии Tarantool называется спейс (space). Пусть это будет шаблон списка друзей Свинки Пеппы, в котором будет два поля: идентификатор записи и имя друга:
pico.create_space({
name = 'friends_of_peppa',
format = {
{name = 'id', type = 'unsigned', is_nullable = false},
{name = 'name', type = 'string', is_nullable = false},
},
primary_key = {'id'},
distribution = 'global',
timeout = 3,
})
Обратите внимание на тип спейса, который мы передаем в ключе distribution
. В Picodata можно создавать глобальные спейсы (global) и шардированные (sharded). Первые будут доступны на всех инстансах, вторые — распределены по нескольким. Таймаут означает время в секундах перед возвращением управления пользователю (т.к. действие производится на всем кластере, оно может занять продолжительное время).
Спейс является необходимым элементом схемы данных, распространяемой на все узлы кластера. Каждое действие по созданию/удалению спейсов в Picodata увеличивает номер схемы данных. В пустом кластере изначально схема данных имеет индекс 0, после чего он инкрементируется при каждом изменении содержимого кластера. Любое действие по созданию/изменению/удалению спейсов, работы с индексами хранения и т.д. является изменением схемы данных.
Посмотреть на текущую версию схемы можно так:
box.space._pico_property:get("current_schema_version")
---
- ['current_schema_version', 1]
...
Запись данных в спейс
Запись данных, т.е. вставка строк, происходит с помощью следующей команды:
pico.cas(
{space = 'friends_of_peppa', kind = 'insert', tuple = {1, "Suzy"} },
{index = box.space._raft_state:get('applied').value,
term = box.space._raft_state:get('term').value, ranges = {} }
)
pico.cas
— способ надежно управлять данными в распределенном кластере, т.к. помимо проведения самих операций (insert | replace | delete) он умеет проверять предикат (индекс схемы, терм и диапазон значений параметра, для которого делается запрос). В примере выше мы не использовали явную проверку, и по умолчанию запрос использовал текущий индекс, текущий терм и пустой (разрешающий всё) диапазон. Подробнее см. тут.
Чтение данных
Для чтения данных из спейса подойдёт такая команда:
box.space.friends_of_peppa:select()
Отдельно можно узнать, какие именно поля (названия столбцов) есть в спейсе:
box.space.friends_of_peppa:format()
Как видно, пользоваться БД совсем несложно.
Что дальше?
Picodata постоянно развивается и приобретает новые функции. Пару лет назад мы начинали с минимально рабочего прототипа, который умел запускаться, находить другие экземпляры и формировать кластер. В 2022-м году мы очень сильно продвинулись вперед и добавили в Picodata репликацию, шардирование и автоматическую балансировку кластера.
В июне 2023-го вышла новая версия Picodata, в которой подвезли ещё больше новых и очень крутых штук. У нас появился полноценный Lua API, единая схема данных, которая позволила отказаться от механизма миграций, возможность создавать на выбор глобальные или шардированные спейсы, механизм записи данных в БД на основе Compare and Swap, встроенная справка и много улучшений в консольной утилите picodata.
У нас большие планы на ближайшее время: в работе долгожданный Web UI, улучшение поддержки распределенных SQL-транзакций, удобная поставка нашего ПО в Kubernetes и много других новых функций.
Текущий этап для нас — это уверенное начало пути к тому, чтобы стать одним из лучших решений In-memory data grid (IMDG) на российском рынке. Очень много работы для достижения этой цели еще впереди.
Если у вас возникли вопросы — спрашивайте в комментариях и задавайте их в нашем Telegram-канале @picodataru. Узнать больше о возможностях и вариантах использования, познакомиться с документацией можно на нашем отдельном сайте документации https://docs.picodata.io/picodata, а также на основном сайте https://picodata.io. Мы активно проводим тренинги по Tarantool и Picodata и стремимся приносить пользу сообществу и экосистеме современных резидентных СУБД. Stay tuned!