Кластерные швы¶
ОСНОВНОЙ план ↔ LB API (docs/superpowers/specs/2026-09-21-main-lb-api-communication-design.md)
заменяет прямой доступ узла к MySQL и Redis в MAIN на подписанный API. Фаза 0
подготавливает код без изменения какого-либо поведения. В каждом месте, где узел достигает главного
состояние, и каждое место, где MAIN достигает узла, проходит через один класс. У каждого класса есть один
современный сервер, устаревший, который делает именно то, что раньше делали сайты вызовов.
На более поздних этапах замените этот сервер и оставьте сайты вызовов в покое.
Каждый шов снабжен крючком для проверки и последующей транспортировки (useSink(),
useLoader() или useTransport()). Передача null восстанавливает устаревшую серверную часть.
Узел → ГЛАВНЫЙ¶
| Шов | Обертывания | Устаревший сервер | Серверная часть API (фаза) |
|---|---|---|---|
Core\Cluster\SignalDispatcher |
47 INSERT INTO signals сайтов (удаление, задания кэширования, действия root) |
LegacySqlSignalSink |
команды и события (4/5) |
Domain\Stream\StreamStateWriter |
состояние выполнения узла в streams_servers; отклоняет любой столбец за пределами STATE_FIELDS |
StreamRowMerge::apply() |
stream.state событие (5), хранящееся в собственном хранилище узла (Core\Cluster\StreamRuntime, 7) |
Domain\Stream\StreamSource |
строка потока, строка этого узла streams_servers, параметры потока и запись, считываемые перед запуском потока или записи; поток со строкой этого узла и его состоянием во время выполнения (nodeRow(), workerRow(), plainRow(), createdRow(), builtServerRow(), channelRow(), movieRow()) |
SQL | кэширование потоков узла, созданное cluster:apply из раздела R2 streams, как только поток запущен (ReplicaStreamCache, 7), с состоянием выполнения из собственного хранилища узла, как только оно заполнено (StreamRuntime, 7); stream_bundle при ошибке (не создано) |
Domain\Stream\NodeStreams |
списки потоков этого узла выбираются его администраторами и демонами в зависимости от их состояния во время выполнения (cron:streams, cron:vod, cron:cleanup, демон по требованию) |
SQL | кэширование потока и собственное хранилище узла или весь раздел R2 (ReplicaStreams) для списков cron:cleanup сокращает файлы на (7) |
Core\Cluster\LogSink |
записи о клиенте, потоке, ошибке потока, ошибке панели управления и повторном обнаружении потока; строки системного журнала root (syslog()) |
одна многострочная ВСТАВКА в пакет (по 1000 фрагментов); собственная ВСТАВКА вызывающего устройства mysql_syslog |
log.* события, отредактированные первыми (5); log.syslog (7) |
ОСНОВНАЯ сторона¶
| Шов | Роль |
|---|---|
Domain\Stream\StreamRowMerge |
Объединяет состояние выполнения узла только в строке этого узла. eventFields() сохраняет только столбцы времени выполнения и редактирует URL-адрес источника. |
Domain\Stream\StreamCacheBuilder |
Запись в кэше stream_<id>: столбцы, строки для каждого сервера и правило, согласно которому URL-адреса источника потока не попадают в кэш, если только это не прямой источник. cron:cache_engine, который запускается только в MAIN, создает его с помощью этого класса. |
Core\Cluster\Redactor |
Удаляет значения password=, token= и username=, сегменты /user/pass/ URL-адресов Xtream и user:pass@ из текста перед его записью в журнал. |
ГЛАВНАЯ → узел¶
| Шов | Обертывания | Каталог | Форма API (фаза 4) |
|---|---|---|---|
Core\Cluster\NodeRpc |
ApiClient::systemRequest() / asyncRequest(): запросы/ответы на запросы узла /api |
NodeRpc::ACTIONS |
node.rpc{action}, ответ через ack / rpc_result |
Core\Cluster\NodeActions |
действия root для RootSignalsCronJob: перезагрузка, службы, обновление/откат, порты, sysctl, certbot, модули, очистка списка блокировок, OPENSSL_EXTRA, а также действия с учетными данными, ключами и pin-кодом, которые выполняются только через cluster API |
NodeActions::ROOT_ACTIONS |
node.root{action} для cluster:root, с предоставлением артефакта, когда для действия требуется файл MAIN's |
Оба оператора отказываются выполнять действие, которого нет в их каталоге, поэтому новый вызов - это
намеренное изменение. NodeRpcActionsTest проверяет, использует ли каждый узел вызова
каталогизированное действие RPC и что RootSignalsCronJob обрабатывает каждый каталогизированный корень
действие.
Правила для нового кода¶
- Не записывайте
INSERT INTO signals,UPDATE streams_servers(для состояния выполнения), или ВСТАВЬТЕ непосредственно в таблицу журнала; используйте шов. - Не вызывайте
ApiClient::systemRequest()/asyncRequest()илиSignalDispatcher::rootAction()за пределамиCore\Cluster; добавьте действие в внесите в каталог и используйтеNodeRpc/NodeActions. - Код на стороне узла считывает определения потоков через
StreamSource, а не с помощью его собственные запросы кstreams,streams_optionsилиrecordings. Как только узел будет реплика владеет потоками (ПОТОКИ продолжаются), ее ответы поступают из потока созданные кэшиcluster:apply, но никогда из базы данных MAIN. Поток, считанный с состояние выполнения этого узла (объединенная строкаstreams ⨝ streams_serversили список, отфильтрованный поpidилиstream_status) проходит черезStreamSourceилиNodeStreamsтоже: сStreamSource::local()(реплика владеет потоки и заполняется собственное хранилище узлаStreamRuntime) во время выполнения колонки поступают из этого хранилища, в котором авторы ведут стримы, и в seam сохраняется инструкция MAIN для всего остального. Не подключайте повторно MAIN's database in a daemon whereStreamSource::local()holds. A столбец времени выполнения MAIN записывает сам себя для этого узла, который попадает только в это хранилище где код узла также сохраняет его (диктофон сохраняет основной VOD, подключенный к узел для законченной записи). - Код на стороне узла считывает настройки через
SettingsManagerполучатели и серверы черезServerRepository. Узел в режиме 2 или в режиме 1 с конфигурацией поток продолжается, загружается из своей реплики (ReplicaStage), как только приложение построит свою кэши: они поступают из кэшей реплики, и открывается любой другой запрос База данных MAIN загружается лениво, при первом использовании. Режим 1 открывает ее, подсчитанный на сайт запроса; режим 2 отклоняет его. Точки входа в потоковую передачу используются те же отложенный дескриптор (LegacyInitializer::initStreaming). На узле в режиме 1 или 2, клавиша настройки, расположенная за пределамиlb_settings_keys.php, засчитывается как пропущенная (SettingsAudit) и отображается в меню Серверы → Узлы кластера. - Не открывайте базу данных MAIN до того, как это потребуется для запроса. Подключение при загрузке или при
вершина точки входа засчитывается в семидневный нулевой показатель узла режима 1.
когда запрос завершается без запроса. Когда реплика может не ответить
(
ReplicaBoot::hybrid(): режим 1, в котором никогда не происходит отказа в подключении), прочитайте База данных MAIN работает с отложенным дескриптором, а не с новым. Чтобы узнать, работает ли База данных MAIN отвечает на запрос: значение ленивого дескриптораconnectedостается ложным до его первого запроса (StatusCommand::mainDatabaseAnswers()). - Не записывайте
new DatabaseHandler(). Возьмите дескриптор процесса (DatabaseAware,DatabaseFactory::get()), илиDatabaseFactory::connect(),connectLazy()илиopen(). Каждое подключение к MySQL или Redis в MAIN проходитConnectAudit::guard(): на узле в режиме 1 или 2 он подсчитывается с учетом его вызывающий и отображается на Серверах → Узлах кластера, а в режиме 2 он выдаетLbDatabaseAccessException. Не открывайте соединения PDO,\Redisили mysqli ваш собственный код, который запускает подсистема балансировки нагрузки. - Узел в режиме 2 (
NodeRole::refusesConnects()) никогда не возвращается к ОСНОВНОМУ база данных. Запись с использованием пути к агенту сначала проходит через свой шов (LogSink,LogSink::syslog()для строк системного журнала root,NodeStateSink), и корневое действие выполняется после своей строки журнала, что бы ни случилось с этой строкой (строка, которую не выполнил агент, остается в журнале ошибок панели). Действие которым все еще нужна база данных MAIN, в режиме 2 отказано заранее (RootSignalsCronJob::updatesHere()дляupdateиrollback). Работа, для которой требуются данные MAIN, но которые еще не перенесены ни в один раздел реплики, пропускается в режим 2 за именованным швом (CleanupCronJob::streamChecks(), который содержит как только реплика и собственное хранилище узла ответят), никогда не сталкивайтесь с пустой или неполный ответ. Запись о состоянии потока, которую агент не выполнял, остается в собственном хранилище узла в режиме 2 (StreamStateWriter::resend()отправляет его позже) вместо возврата к основному ряду; в режимах 0 и 1 он возвращается обратно, и хранилище перестает работать, как только оно появляется в строке MAIN (StreamRuntime::lapse()). - Файл, который требуется узлу от MAIN (пользовательское видео вне эфира, архив модуля)
является артефактом: MAIN называет его в
Domain\Cluster\ArtefactRegistryи предоставляет его с помощью подписанной команды (ArtefactGrants), и узел использует его только один разCore\Cluster\ArtefactStageпроверил его размер и SHA-256 на соответствие grant, так как он копирует его туда, где он находится. используется (собственный этап root для действия root). Не добавляйте извлечение из MAIN с помощью путь, URL-адрес или пароль. - Работа MAIN для узла проходит через
SignalDispatcherиNodeActions, никогда строкаsignals, написанная от руки: узел в режиме 2 не считывает ни одного. Там ОСНОВНОЕ отправляет задания кэширования в виде командnode.cacheв формеCacheJobs::job(), узел сразу запускает задания, которые он ставит в очередь для себя, и значение, считанное узлом обратно из своей собственной строки в базе данных MAIN поступает из копииNodeStateSinkсохраняет то, о чем он сообщал (NodeStateSink::reported()). - Файлы аудита узла (
storage/cluster/,config/cluster/audit.json) хранятся в где xc_vm может записывать. Корневой процесс записывает их только внутриSettingsAudit::asAgentUser(), который выполняет работу как xc_vm, никогда с собственные права root.
Тесты, подтверждающие эти правила: SignalDispatcherParityTest, StreamStateWriterTest,
StreamRowMergeTest, StreamCacheBuilderSourceTest, LogSinkTest,
NodeRpcActionsTest, ArchitectureTest, DbConnectRefusalTest,
ReplicaBootTest, ModeTwoPathsTest, ArtefactHashRefusalTest,
StreamRuntimeTest и StreamRuntimeReadersTest.