Как это работает
Эта страница описывает реальный поток сообщений между клиентом и сервером (кластером), чтобы объяснить, как работает Ticketing. Точная побайтовая спецификация того, что передаётся по сети, находится в разделах Протокол (API) и Протокол (кластер).
Общая структура
Сервисы, принимающие пользовательские запросы (например, серверы событий, обрабатывающие открытие продажи билетов), встают в очередь за один и тот же ключ к кластеру Ticketing, и только сервер, чья очередь подошла, продолжает работу — например, уменьшает остаток или назначает место.
Захват и освобождение блокировки
Базовый поток. Свободный ключ выдаётся немедленно; занятый ключ ждёт своей очереди в FIFO. Когда владелец освобождает блокировку, она передаётся напрямую первому в очереди без повторной конкуренции — следующий ожидающий продолжает немедленно с новым токеном, так что блокировка никогда не остаётся пустой, и никто не может влезть без очереди.
lease— это страховочная сеть. Если клиент умирает или забывает освободить блокировку, по истечении времениleaseсервер забирает ключ и передаёт его следующему ожидающему. Задавайте его с запасом — дольше, чем может занять худший случай вашей критической секции. v1 принимает только 1–250 секунд; более долгие работы явно не поддерживаются и отклоняются.wait— это верхняя граница времени ожидания клиента. Если очередь не подходит за это время, сервер отвечаетT(timeout) вместо выдачи блокировки, так что блокировка никогда не «утекает».0означает единственную немедленную попытку без постановки в очередь. Бесконечного ожидания нет.- Если тот же клиент пытается повторно захватить уже удерживаемый им ключ, он встаёт в очередь как любой другой ожидающий — это не реентерабельная блокировка.
Полные правила поведения сервера по состояниям ключа смотрите в Матрице запрос × состояние ключа, а диапазоны значений полей — в Ограничениях полей.
Fencing-токены
Даже идеальный сервер блокировок не может контролировать часы клиента. Одна лишь Ticketing не может предотвратить случай устаревшего владельца: клиент, который на время замер из-за пауз GC или перегрузки, удерживая блокировку, затем просыпается — не зная, что его lease уже истёк, — и продолжает свою работу. Именно поэтому token в каждом ответе на захват — это целое число u64, гарантированно возрастающее при каждой выдаче. Для финансовой или постоянной БД сравнение и обновление high-water fencing по ключу должно выполняться атомарно с реальной бизнес-записью в той же транзакции БД или одной условной записи. Обновить сначала только fencing-строку, а реальную запись выполнить позже — небезопасно.
Монотонность сохраняется и в масштабах кластера — сам счётчик токенов реплицируется через консенсус, поэтому даже после смены лидера новый лидер всегда продолжает с числа, большего предыдущего. Точный формат поля смотрите в Fencing-токен (спецификация).
Кластер — непрерывность через консенсус Raft
В режиме кластера узлы разделяют единое состояние блокировок через консенсус Raft. Только лидер обрабатывает клиентские запросы, и каждое изменение состояния блокировки (захват, освобождение, истечение) фиксируется только после того, как его записало большинство узлов. Подключение к нелидирующему узлу приводит к ответу M (moved) с указанием на лидера.
- Если лидер умирает, текущие настройки избирают нового лидера примерно за 2,3–2,5 секунды. Тот же логический acquire можно продолжить в оставшемся бюджете
waitтолько если не отправлен ни один байт запроса. Отправленный acquire без подтверждённого ответа —Indeterminate; он никогда не пересылается автоматически. - Даже истечение
leaseпроходит через консенсус. Блокировка исчезает только после того, как лидер зафиксирует команду истечения, поэтому небольшие расхождения часов между узлами никогда не приводят к расхождению состояния блокировки. - Если кластер теряет большинство (например, 2 из 3 узлов недоступны), он прекращает принимать записи, вместо того чтобы рискнуть выдать неверную блокировку (безопасность прежде всего). Если потеряно волатильное состояние процессов 2 из 3 (или 3 из 5) узлов, этот cluster/fencing domain нельзя восстанавливать или автоматически bootstrap под той же идентичностью.
Поскольку важно именно большинство, допускаются только 3 или 5 узлов. Чётное число узлов лишь добавляет стоимость, не улучшая отказоустойчивость, поэтому сервер отказывается запускаться с таким числом.
| Число узлов | Непрерывность | Допустимые одновременные отказы | Примечания |
|---|---|---|---|
| 1 | ✗ | — | Монотонность token гарантирована лишь на время жизни процесса; защита перезапускаемой постоянной БД не поддерживается |
| 3 | ✓ | 1 | Стандартная непрерывная конфигурация. Достаточно для большинства случаев |
| 5 | ✓ | 2 | Избыточность сохраняется, даже пока один узел на обслуживании |
Правила фрейминга и портов для RPC между узлами смотрите в Протокол (кластер), а процедуру поочерёдной замены — в непрерывной замене learner с новым NodeId.
Поведение клиента
Официальные клиенты разделяют следующее общее поведение (см. Библиотеки для API конкретных языков).
- Поддерживает одно постоянное соединение на адрес в фоне. При одном адресе клиент держит два соединения к этому же узлу, так что кратковременный обрыв одного сокета не прерывает обслуживание.
- Выбирает соединение сначала к лидеру, затем round-robin. Клиент запоминает лидера, к которому его последний раз перенаправили через
M, и отправляет последующие запросы прямо ему. - Запросы конвейеризуются. Следующий запрос можно отправить, не дожидаясь ответа на предыдущий — см. Правила сопоставления ответов о том, как ответы сопоставляются с запросами.
- Оборванное соединение постоянно повторяет попытки с экспоненциальной задержкой (от 0,1 с до потолка в 3,2 с). Мёртвое соединение не ломает объект брокера — он восстанавливается сам.
- Тот же логический acquire повторяется внутри только если не отправлен ни один байт запроса. Сопоставленный
B— определённый Busy/не-захват; он сразу возвращается вызывающему без внутреннего retry.M— только leader hint для следующего соединения; без owner/key он не является основанием переслать уже отправленный pending-запрос. Отправленный acquire без подтверждённого ответа сообщается какIndeterminate; входить в критическую секцию нельзя. - Явное release и компенсация известного token требуют точного совпадения owner/token/key и используют ограниченную выделенную очередь. Повторы прекращаются по абсолютному дедлайну в 5 секунд от request/enqueue, а не по оставшемуся lease. Без успешного ответа явное release не объявляется и не считается успешным; серверный lease остаётся последней страховкой.
Соединения и безопасность
Каждое соединение (клиентское или между узлами) устанавливается в порядке TCP → (TLS) → аутентификация challenge-response. Клиент хеширует nonce сервера вместе со своим токеном для ответа, поэтому токен никогда не передаётся по сети в открытом виде, а новый nonce при каждом соединении исключает повторное воспроизведение (replay). Точную спецификацию смотрите в Аутентификационном хендшейке.
Заметка о согласованности
- Взаимное исключение гарантируется консенсусом. Поскольку каждая выдача проходит через фиксацию большинством, один и тот же ключ никогда не выдаётся двум владельцам одновременно, даже во время разделения сети или смены лидера.
- Постоянные БД обязаны применять fencing. Только условие high-water по ключу, атомарно выполненное с защищаемой записью в той же транзакции/условной записи, останавливает клиента, проснувшегося после истечения
lease. Блокировка упорядочивает работу; fencing БД блокирует последнюю устаревшую запись.