rails
January 14, 2021

Consul, distributed locks и как мы с этим работаем (Lusnoc)

За последние пару лет Consul плотно вошёл в наш технологический стэк. Сначала как хранилище конфигурации потом как service provider для внутренних систем таких как prometeus и traefik и наконец как инструмент для distributed locks и leader election. В конце я еще приведу список полезных ссылок по теме с комментариями, а пока речь пойдет о нашей Ruby библиотеке, реализующей семантику мьютекса поверх функционала, предоставляемого Consul’ом. Библиотека, она же гем, получила название Lusnoc.

Надо больше блокировок!

Что это такое и зачем оно надо?

В процесса работы нам потребовалось гарантировать выполнение кода только одним процессом (сервисом) и избежать гонок. Поскольку существует уже веками устоявшийся примитив мьютекс было решено реализовать функционал именно в таком виде. В Ruby для этого существует стандартный класс Mutex и мы сделали всё аналогично.

Основная задача библиотеки Lusnoc - реализация распределенного механизма для блокировки ресурса.

Библиотека задумывалась как простая в использовании, надёжная и без “черной магии” под капотом. Когда создавалась эта библиотека ещё был жив проект consul-mutex (сейчас его выпилили, но сохранился мой форк). Беда была в том, что он совершал слишком много фоновой магии и автоматизировал то, что на мой взгляд автоматизировать не стоит.

Чем Lusnoc лучше других решений?

Прежде всего “других” решений не так много, а те что есть обладают рядом проблем (например блокировки поверх redis как самописные так и использующие алгоритм redlock). А красивых и надёжных, использующих кластер Consul нет совсем.

Что насчет consul-mutex или diplomat?

  • Во-первых consul-mutex уже канул в лету
  • Во вторых он создавал фоновый поток в котором исполнял ваш код и мог прибить его в любое время без предупреждения, если считал что владение ресурсом потеряно
  • С гемом diplomat всё проще — он предоставляет лишь базовую реализацию API для работы с сессиями Consul и никакого механизма по управлению ими

На Ruby есть реализации блокировок с использованием Zookeeper, Etcd и пр. но мы их не рассматривали, поскольку Consul уже прописался в нашей инфраструктуре.

Чем же так хорош Lusnoc что его стоит использовать?

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

Использовать как обычный мьютекс

При создании сессий используется TTL и через него общий ресурс защищен от зависания вашего кода и прочих трудно воспроизводимых но очень распространенных вещей.

Lusnoc задумывался как максимально простой и понятный - "Keep it simple, stupid"

Библиотека Lusnoc не делает за вас “умных” действий — например обновление сессии необходимо выполнять руками внутри критической секции и не прозевать момент когда сессия протухнет. Для помощи в этом нелегком деле фоновый поток всё же запускается, но он имеет “консультативный” характер и не прибьёт рабочий поток без предупреждения.

Ответственность по обновлению сессии лежит на нас

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

Как использовать?

Пример и описание в одном флаконе

Что на выходе?

В результате мы имеем привычную для любого разработчика семантику мьютексов, позволяющую просто и понятно выполнять достаточное сложные действия — например leader election или просто иметь строгую гарантию выполнения определенного действия строго одним процессом.

О конкретном примере использования данной библиотеки я напишу в следующей статье, а напоследок список полезных материалов по теме: