9.4 KiB
Многопроцессный Beanstalkd
Статус: постановка на реализацию, код не реализован. Дата фиксации: 2026-10-15. База: исходники Beanstalkd 1.13 в этом каталоге.
Цель
Позволить одному логическому серверу использовать несколько CPU-ядер для обработки независимых tubes, сохранив модель заданий Beanstalkd и динамический жизненный цикл очередей.
Принятое решение: процессы-владельцы групп tubes. У каждого владельца собственный event loop, задания, очереди, таймеры и WAL; сетевые frontend-процессы направляют операции владельцам через IPC. Supervisor обеспечивает эксклюзивность owner slots и управление жизненным циклом процессов.
+-- owner slot 0: tubes, jobs, WAL
clients --> frontend(s) --IPC--+-- owner slot 1: tubes, jobs, WAL
+-- owner slot N: tubes, jobs, WAL
supervisor: topology, process generations, startup, shutdown, restart
Границы Первого Этапа
- Один хост, статическая карта распределения, отдельный WAL каждого владельца.
- Каждый
reserve/reserve-with-timeoutработает с одной наблюдаемой tube. Настройкаwatch target,ignore defaultдопускается. - Связь tube с владельцем определяется по имени через фиксированные логические разделы. Постоянного каталога дескрипторов всех tubes нет.
- Consumer может держать несколько заданий. Его
use, текущий watch list и ранее полученные резервации не обязаны относиться к одной tube или одному владельцу. - Политика перехода от существующего однопроцессного сервера определяется в эпике 01. Рекомендуется opt-in с сохранением legacy, но обязательная поддержка двух режимов пока не утверждена.
- Полный end-to-end путь команд, восстановление, диагностика и проверки отказов входят в результат, а не откладываются после демонстрации
put/reserve.
Вне Объёма
- Межшардовый выбор задания из нескольких наблюдаемых tubes.
- Распараллеливание одной tube, глобальный FIFO между tubes.
- Динамическая миграция, автоматическая балансировка и изменение карты при работе сервера.
- Кластер между хостами, репликация и автоматическое назначение данных упавшего владельца другому slot.
- Shared-memory allocator, zero-copy и io_uring как предварительное условие реализации.
- Exactly-once выполнение пользовательского кода.
Основные Инварианты
- Имя tube всегда имеет одного владельца в пределах версии топологии, даже когда объект tube не существует.
- Объект создаётся только владельцем и только по операции, которая предполагает создание по протоколу.
- Два процесса не изменяют данные одного owner slot одновременно.
- Переходы состояний job последовательны у владельца; права на reserved job принадлежат логической клиентской сессии.
- Один reserve имеет не более одного результата; job имеет не более одной действующей резервации.
- Очерёдность команд и ответов одного TCP-соединения сохраняется.
- Сбой, недоступность и неизвестный результат не маскируются под успешную операцию или отсутствие задания.
- Накопление IPC-запросов, ответов и тел ограничено памятью; медленный клиент не блокирует чужие очереди неограниченно.
- Перезапуск с WAL использует ту же топологию и корректное пространство ID; несовпадение конфигурации обнаруживается до записи данных.
Эпики И Зависимости
| ID | Постановка | Зависимости | Результат |
|---|---|---|---|
| 01 | Контракты и baseline | Нет | Решения по внешнему поведению, отказам, ID и конфигурации |
| 02 | Владение tubes | 01 | Детерминированная маршрутизация и локальное создание |
| 03 | IPC и сессии | 01; интерфейсы 02 | Межпроцессный транспорт и безопасный жизненный цикл сессий |
| 04 | Queue engine и протокол | 02, 03 | Все операции над очередями через владельцев |
| 05 | WAL и ID | 01, 02; интеграция 03, 04 | Восстановление, уникальные ID, изоляция журналов |
| 06 | Supervisor и эксплуатация | 02, 03; интеграция 04, 05 | Управляемый запуск, отказ и остановка процессов |
| 07 | Приёмка и производительность | Baseline после 01; итог после 02-06 | Воспроизводимые функциональные и нагрузочные доказательства |
Порядок не запрещает совместную разработку согласованных интерфейсов. Минимальный запуск дочернего процесса для тестов можно сделать раньше эпика 06; это не означает готовность supervisor. Тесты пишутся в каждом эпике, а не только в конце.
Общая Приёмка
- Все обязательные решения эпика 01 закрыты и документированы до реализации зависящего от них контракта.
- Исходные тесты используются как регрессионная база; отклонения допустимы только по согласованной матрице совместимости. Если выбран отдельный legacy-режим, он проходит прежний набор без ограничения multi-watch.
- Успешно проверены минимум два owner slots и несколько frontend-процессов, в том числе гонка создания одной tube через разные frontend.
- Протестированы job lifecycle, права сессий, таймеры, slow clients, аварии и WAL при одинаковых с baseline настройках durability.
- На независимых tubes подтверждён рост пропускной способности и реальная работа нескольких owner CPU; точный порог фиксируется после baseline, до финальных замеров.
- Для одной горячей tube ограничение последовательности явно показано в отчёте, а не скрыто агрегированной нагрузкой.
- Обновлены
README.md,doc/protocol.txt, исходник руководства и эксплуатационные инструкции при реализации. Внешние ограничения и совместимость старых данных описаны явно.
Основание
Текущий сервер последовательно выполняет prottick, socknext и callback в serv.c:srvserve. Реестры jobs/tubes и генератор ID глобальны. process_queue связывает job с Conn*, а WAL защищён от второго владельца. Поэтому разделение только listening socket или вызов fork() не дают корректного общего сервера.
Критический краткий контекст для последующей работы: ../AGENTS.md.