Сейчас идёт тестирование: код на GitHub будет открыт после завершения.

B · Занято

Структураrefresh

Ответ на Acquire request (A), не принятый из-за предела queue ключа или глобального admission key/waiter/pending/memory. Этот запрос точно не получил lock, и его frame не был неверным.

owner — значение запроса, возвращённое как correlator; оно точно определяет отклонённый acquire (см. правила сопоставления).

Обработка клиентом

Официальные клиенты сразу возвращают коррелированный B caller как ошибку Busy/Capacity и не повторяют тот же логический acquire внутри. Для новой попытки caller задаёт общий deadline и backoff, затем явно начинает новый acquire с новым owner.

Реально наблюдаемый B доказывает неуспех захвата, поэтому отдельный новый call безопасен. Нельзя выводить B из отправки без ответа; это Indeterminate.

Обозначение байтов: op — символ ASCII, owner — бинарный (u64, big-endian), key — текст в UTF-8 (переменной длины, N в структуре). Завершающий \n — это 0A. Фиксированный заголовок — бинарное значение и может содержать 0x0A, поэтому его нужно сначала вычитать по количеству байт, и лишь затем искать перевод строки.

Поток

Клиент
Сервер
A · acquire (key · owner)
очередь этого ключа достигла max_waiters
B · busy (эхо owner)
вернуть Busy — caller решает о новом логическом acquire
A · новый acquire (опциональный retry)
когда появится место → A · acquired (token)
запросответ

Лимиты — memory guardrails. Они отклоняют новые acquires до OOM daemon, сохраняя ресурсы для exact-token release, одобренных ответов, cleanup и Raft recovery. Причины key_busy, per_key_limit, global_overload и т. п. различаются в metrics/logs, не в wire.