From 99edcbe818c3c5f584d9eeab309a3f84ed738acb Mon Sep 17 00:00:00 2001 From: Alexey <247128645+axkurcom@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:11:44 +0300 Subject: [PATCH] Runtime Model --- docs/model/MODEL.en.md | 285 +++++++++++++++++++++++++++++++++++++++++ docs/model/MODEL.ru.md | 285 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 570 insertions(+) create mode 100644 docs/model/MODEL.en.md create mode 100644 docs/model/MODEL.ru.md diff --git a/docs/model/MODEL.en.md b/docs/model/MODEL.en.md new file mode 100644 index 0000000..2d570cd --- /dev/null +++ b/docs/model/MODEL.en.md @@ -0,0 +1,285 @@ +# Telemt Runtime Model + +## Scope +This document defines runtime concepts used by the Middle-End (ME) transport pipeline and the orchestration logic around it. + +It focuses on: +- `ME Pool / Reader / Writer / Refill / Registry` +- `Adaptive Floor` +- `Trio-State` +- `Generation Lifecycle` + +## Core Entities + +### ME Pool +`ME Pool` is the runtime orchestrator for all Middle-End writers. + +Responsibilities: +- Holds writer inventory by DC/family/endpoint. +- Maintains routing primitives and writer selection policy. +- Tracks generation state (`active`, `warm`, `draining` context). +- Applies runtime policies (floor mode, refill, reconnect, reinit, fallback behavior). +- Exposes readiness gates used by admission logic (for conditional accept/cast behavior). + +Non-goals: +- It does not own client protocol decoding. +- It does not own per-client business policy (quotas/limits). + +### ME Writer +`ME Writer` is a long-lived ME RPC tunnel bound to one concrete ME endpoint (`ip:port`), with: +- Outbound command channel (send path). +- Associated reader loop (inbound path). +- Health/degraded flags. +- Contour/state and generation metadata. + +A writer is the actual data plane carrier for client sessions once bound. + +### ME Reader +`ME Reader` is the inbound parser/dispatcher for one writer: +- Reads/decrypts ME RPC frames. +- Validates sequence/checksum. +- Routes payloads to client-connection channels via `Registry`. +- Emits close/ack/data events and updates telemetry. + +Design intent: +- Reader must stay non-blocking as much as possible. +- Backpressure on a single client route must not stall the whole writer stream. + +### Refill +`Refill` is the recovery mechanism that restores writer coverage when capacity drops: +- Per-endpoint restore (same endpoint first). +- Per-DC restore to satisfy required floor. +- Optional outage-mode/shadow behavior for fragile single-endpoint DCs. + +Refill works asynchronously and should not block hot routing paths. + +### Registry +`Registry` is the routing index between ME and client sessions: +- `conn_id -> client response channel` +- `conn_id <-> writer_id` binding map +- writer activity snapshots and idle tracking + +Main invariants: +- A `conn_id` routes to at most one active response channel. +- Writer loss triggers safe unbind/cleanup and close propagation. +- Registry state is the source of truth for active ME-bound session mapping. + +## Adaptive Floor + +### What it is +`Adaptive Floor` is a runtime policy that changes target writer count per DC based on observed activity, instead of always holding static peak floor. + +### Why it exists +Goals: +- Reduce idle writer churn under low traffic. +- Keep enough warm capacity to avoid client-visible stalls on burst recovery. +- Limit needless reconnect storms on unstable endpoints. + +### Behavioral model +- Under activity: floor converges toward configured static requirement. +- Under prolonged idle: floor can shrink to a safe minimum. +- Recovery/grace windows prevent aggressive oscillation. + +### Safety constraints +- Never violate minimal survivability floor for a DC group. +- Refill must still restore quickly on demand. +- Floor adaptation must not force-drop already bound healthy sessions. + +## Trio-State + +`Trio-State` is writer contouring: +- `Warm` +- `Active` +- `Draining` + +### State semantics +- `Warm`: connected and validated, not primary for new binds. +- `Active`: preferred for new binds and normal traffic. +- `Draining`: no new regular binds; existing sessions continue until graceful retirement rules apply. + +### Transition intent +- `Warm -> Active`: when coverage/readiness conditions are satisfied. +- `Active -> Draining`: on generation swap, endpoint replacement, or controlled retirement. +- `Draining -> removed`: after drain TTL/force-close policy (or when naturally empty). + +This separation reduces SPOF and keeps cutovers predictable. + +## Generation Lifecycle + +Generation isolates pool epochs during reinit/reconfiguration. + +### Lifecycle phases +1. `Bootstrap`: initial writers are established. +2. `Warmup`: next generation writers are created and validated. +3. `Activation`: generation promoted to active when coverage gate passes. +4. `Drain`: previous generation becomes draining, existing sessions are allowed to finish. +5. `Retire`: old generation writers are removed after graceful rules. + +### Operational guarantees +- No partial generation activation without minimum coverage. +- Existing healthy client sessions should not be dropped just because a new generation appears. +- Draining generation exists to absorb in-flight traffic during swap. + +### Readiness and admission +Pool readiness is not equivalent to “all endpoints fully saturated”. +Typical gating strategy: +- Open admission when per-DC minimal alive coverage exists. +- Continue background saturation for multi-endpoint DCs. + +This keeps startup latency low while preserving eventual full capacity. + +## Interactions Between Concepts + +- `Generation` defines pool epochs. +- `Trio-State` defines per-writer role inside/around those epochs. +- `Adaptive Floor` defines how much capacity should be maintained right now. +- `Refill` is the actuator that closes the gap between desired and current capacity. +- `Registry` keeps per-session routing correctness while all of the above changes over time. + +## Architectural Approach + +### Layered Design +The runtime is intentionally split into two planes: +- `Control Plane`: decides desired topology and policy (`floor`, `generation swap`, `refill`, `fallback`). +- `Data Plane`: executes packet/session transport (`reader`, `writer`, routing, acks, close propagation). + +Architectural rule: +- Control Plane may change writer inventory and policy. +- Data Plane must remain stable and low-latency while those changes happen. + +### Ownership Model +Ownership is centered around explicit state domains: +- `MePool` owns writer lifecycle and policy state. +- `Registry` owns per-connection routing bindings. +- `Writer task` owns outbound ME socket send progression. +- `Reader task` owns inbound ME socket parsing and event dispatch. + +This prevents accidental cross-layer mutation and keeps invariants local. + +### Control Plane Responsibilities +Control Plane is event-driven and policy-driven: +- Startup initialization and readiness gates. +- Runtime reinit (periodic or config-triggered). +- Coverage checks per DC/family/endpoint group. +- Floor enforcement (static/adaptive). +- Refill scheduling and retry orchestration. +- Generation transition (`warm -> active`, previous `active -> draining`). + +Control Plane must prioritize determinism over short-term aggressiveness. + +### Data Plane Responsibilities +Data Plane is throughput-first and allocation-sensitive: +- Session bind to writer. +- Per-frame parsing/validation and dispatch. +- Ack and close signal propagation. +- Route drop behavior under missing connection or closed channel. +- Minimal critical logging in hot path. + +Data Plane should avoid waiting on operations that are not strictly required for frame correctness. + +## Concurrency and Synchronization + +### Concurrency Principles +- Per-writer isolation: each writer has independent send/read task loops. +- Per-connection isolation: client channel state is scoped by `conn_id`. +- Asynchronous recovery: refill/reconnect runs outside the packet hot path. + +### Synchronization Strategy +- Shared maps use fine-grained, short-lived locking. +- Read-mostly paths avoid broad write-lock windows. +- Backpressure decisions are localized at route/channel boundary. + +Design target: +- A slow consumer should degrade only itself (or its route), not global writer progress. + +### Cancellation and Shutdown +Writer and reader loops are cancellation-aware: +- explicit cancel token / close command support; +- safe unbind and cleanup via registry; +- deterministic order: stop admission -> drain/close -> release resources. + +## Consistency Model + +### Session Consistency +For one `conn_id`: +- exactly one active route target at a time; +- close and unbind must be idempotent; +- writer loss must not leave dangling bindings. + +### Generation Consistency +Generational consistency guarantees: +- New generation is not promoted before minimum coverage gate. +- Previous generation remains available in `draining` state during handover. +- Forced retirement is policy-bound (`drain ttl`, optional force-close), not immediate. + +### Policy Consistency +Policy changes (`adaptive/static floor`, fallback mode, retries) should apply without violating established active-session routing invariants. + +## Backpressure and Flow Control + +### Route-Level Backpressure +Route channels are bounded by design. +When pressure increases: +- short burst absorption is allowed; +- prolonged congestion triggers controlled drop semantics; +- drop accounting is explicit via metrics/counters. + +### Reader Non-Blocking Priority +Inbound ME reader path should never be serialized behind one congested client route. +Practical implication: +- prefer non-blocking route attempt in the parser loop; +- move heavy recovery to async side paths. + +## Failure Domain Strategy + +### Endpoint-Level Failure +Failure of one endpoint should trigger endpoint-scoped recovery first: +- same endpoint reconnect; +- endpoint replacement within same DC group if applicable. + +### DC-Level Degradation +If a DC group cannot satisfy floor: +- keep service via remaining coverage if policy allows; +- continue asynchronous refill saturation in background. + +### Whole-Pool Readiness Loss +If no sufficient ME coverage exists: +- admission gate can hold new accepts (conditional policy); +- existing sessions should continue when their path remains healthy. + +## Performance Architecture Notes + +### Hotpath Discipline +Allowed in hotpath: +- fixed-size parsing and cheap validation; +- bounded channel operations; +- precomputed or low-allocation access patterns. + +Avoid in hotpath: +- repeated expensive decoding; +- broad locks with awaits inside critical sections; +- verbose high-frequency logging. + +### Throughput Stability Over Peak Spikes +Architecture prefers stable throughput and predictable latency over short peak gains that increase churn or long-tail reconnect times. + +## Evolution and Extension Rules + +To evolve this model safely: +- Add new policy knobs in Control Plane first. +- Keep Data Plane contracts stable (`conn_id`, route semantics, close semantics). +- Validate generation and registry invariants before enabling by default. +- Introduce new retry/recovery strategies behind explicit config. + +## Failure and Recovery Notes + +- Single-endpoint DC failure is a normal degraded mode case; policy should prioritize fast reconnect and optional shadow/probing strategies. +- Idle close by peer should be treated as expected when upstream enforces idle timeout. +- Reconnect backoff must protect against synchronized churn while still allowing fast first retries. +- Fallback (`ME -> direct DC`) is a policy switch, not a transport bug by itself. + +## Terminology Summary +- `Coverage`: enough live writers to satisfy per-DC acceptance policy. +- `Floor`: target minimum writer count policy. +- `Churn`: frequent writer reconnect/remove cycles. +- `Hotpath`: per-packet/per-connection data path where extra waits/allocations are expensive. diff --git a/docs/model/MODEL.ru.md b/docs/model/MODEL.ru.md new file mode 100644 index 0000000..2f19efe --- /dev/null +++ b/docs/model/MODEL.ru.md @@ -0,0 +1,285 @@ +# Runtime-модель Telemt + +## Область описания +Документ фиксирует ключевые runtime-понятия пайплайна Middle-End (ME) и оркестрации вокруг него. + +Фокус: +- `ME Pool / Reader / Writer / Refill / Registry` +- `Adaptive Floor` +- `Trio-State` +- `Generation Lifecycle` + +## Базовые сущности + +### ME Pool +`ME Pool` — центральный оркестратор всех Middle-End writer-ов. + +Зона ответственности: +- хранит инвентарь writer-ов по DC/family/endpoint; +- управляет выбором writer-а и маршрутизацией; +- ведёт состояние поколений (`active`, `warm`, `draining` контекст); +- применяет runtime-политики (floor, refill, reconnect, reinit, fallback); +- отдаёт сигналы готовности для admission-логики (conditional accept/cast). + +Что не делает: +- не декодирует клиентский протокол; +- не реализует бизнес-политику пользователя (квоты/лимиты). + +### ME Writer +`ME Writer` — долгоживущий ME RPC-канал к конкретному endpoint (`ip:port`), у которого есть: +- канал команд на отправку; +- связанный reader loop для входящего потока; +- флаги состояния/деградации; +- метаданные contour/state и generation. + +Writer — это фактический data-plane носитель клиентских сессий после бинда. + +### ME Reader +`ME Reader` — входной parser/dispatcher одного writer-а: +- читает и расшифровывает ME RPC-фреймы; +- проверяет sequence/checksum; +- маршрутизирует payload в client-каналы через `Registry`; +- обрабатывает close/ack/data и обновляет телеметрию. + +Инженерный принцип: +- Reader должен оставаться неблокирующим. +- Backpressure одной клиентской сессии не должен останавливать весь поток writer-а. + +### Refill +`Refill` — механизм восстановления покрытия writer-ов при просадке: +- восстановление на том же endpoint в первую очередь; +- восстановление по DC до требуемого floor; +- опциональные outage/shadow-режимы для хрупких single-endpoint DC. + +Refill работает асинхронно и не должен блокировать hotpath. + +### Registry +`Registry` — маршрутизационный индекс между ME и клиентскими сессиями: +- `conn_id -> канал ответа клиенту`; +- map биндов `conn_id <-> writer_id`; +- снимки активности writer-ов и idle-трекинг. + +Ключевые инварианты: +- один `conn_id` маршрутизируется максимум в один активный канал ответа; +- потеря writer-а приводит к безопасному unbind/cleanup и отправке close; +- именно `Registry` является источником истины по активным ME-биндам. + +## Adaptive Floor + +### Что это +`Adaptive Floor` — runtime-политика, которая динамически меняет целевое число writer-ов на DC в зависимости от активности, а не держит всегда фиксированный статический floor. + +### Зачем +Цели: +- уменьшить churn на idle-трафике; +- сохранить достаточную прогретую ёмкость для быстрых всплесков; +- снизить лишние reconnect-штормы на нестабильных endpoint. + +### Модель поведения +- при активности floor стремится к статическому требованию; +- при длительном idle floor может снижаться до безопасного минимума; +- grace/recovery окна не дают системе "флапать" слишком резко. + +### Ограничения безопасности +- нельзя нарушать минимальный floor выживаемости DC-группы; +- refill обязан быстро нарастить покрытие по запросу; +- адаптация не должна принудительно ронять уже привязанные healthy-сессии. + +## Trio-State + +`Trio-State` — контурная роль writer-а: +- `Warm` +- `Active` +- `Draining` + +### Семантика состояний +- `Warm`: writer подключён и валиден, но не основной для новых биндов. +- `Active`: приоритетный для новых биндов и обычного трафика. +- `Draining`: новые обычные бинды не назначаются; текущие сессии живут до правил graceful-вывода. + +### Логика переходов +- `Warm -> Active`: когда достигнуты условия покрытия/готовности. +- `Active -> Draining`: при swap поколения, замене endpoint или контролируемом выводе. +- `Draining -> removed`: после drain TTL/force-close политики (или естественного опустошения). + +Такое разделение снижает SPOF-риски и делает cutover предсказуемым. + +## Generation Lifecycle + +Generation изолирует эпохи пула при reinit/reconfiguration. + +### Фазы жизненного цикла +1. `Bootstrap`: поднимается начальный набор writer-ов. +2. `Warmup`: создаётся и валидируется новое поколение. +3. `Activation`: новое поколение становится active после прохождения coverage-gate. +4. `Drain`: предыдущее поколение переводится в draining, текущим сессиям дают завершиться. +5. `Retire`: старое поколение удаляется по graceful-правилам. + +### Операционные гарантии +- нельзя активировать поколение частично без минимального покрытия; +- healthy-клиенты не должны теряться только из-за появления нового поколения; +- draining-поколение служит буфером для in-flight трафика во время swap. + +### Готовность и приём клиентов +Готовность пула не равна "все endpoint полностью насыщены". +Типичная стратегия: +- открыть admission при минимально достаточном alive-покрытии по DC; +- параллельно продолжать saturation для multi-endpoint DC. + +Это уменьшает startup latency и сохраняет выход на полную ёмкость. + +## Как понятия связаны между собой + +- `Generation` задаёт эпохи пула. +- `Trio-State` задаёт роль каждого writer-а внутри/между эпохами. +- `Adaptive Floor` задаёт, сколько ёмкости нужно сейчас. +- `Refill` — исполнитель, который закрывает разницу между desired и current capacity. +- `Registry` гарантирует корректную маршрутизацию сессий, пока всё выше меняется. + +## Архитектурный подход + +### Слоистая модель +Runtime специально разделён на две плоскости: +- `Control Plane`: принимает решения о целевой топологии и политиках (`floor`, `generation swap`, `refill`, `fallback`). +- `Data Plane`: исполняет транспорт сессий и пакетов (`reader`, `writer`, маршрутизация, ack, close). + +Ключевое правило: +- Control Plane может менять состав writer-ов и policy. +- Data Plane должен оставаться стабильным и низколатентным в момент этих изменений. + +### Модель владения состоянием +Владение разделено по доменам: +- `MePool` владеет жизненным циклом writer-ов и policy-state. +- `Registry` владеет routing-биндами клиентских сессий. +- `Writer task` владеет исходящей прогрессией ME-сокета. +- `Reader task` владеет входящим парсингом и dispatch-событиями. + +Это ограничивает побочные мутации и локализует инварианты. + +### Обязанности Control Plane +Control Plane работает событийно и policy-ориентированно: +- стартовая инициализация и readiness-gate; +- runtime reinit (периодический и/или по изменению конфигурации); +- проверки покрытия по DC/family/endpoint group; +- применение floor-политики (static/adaptive); +- планирование refill и orchestration retry; +- переходы поколений (`warm -> active`, прежний `active -> draining`). + +Для него важнее детерминизм, чем агрессивная краткосрочная реакция. + +### Обязанности Data Plane +Data Plane ориентирован на пропускную способность и предсказуемую задержку: +- bind клиентской сессии к writer-у; +- per-frame parsing/validation/dispatch; +- распространение ack/close; +- корректная реакция на missing conn/closed channel; +- минимальный лог-шум в hotpath. + +Data Plane не должен ждать операций, не критичных для корректности текущего фрейма. + +## Конкурентность и синхронизация + +### Принципы конкурентности +- Изоляция по writer-у: у каждого writer-а независимые send/read loop. +- Изоляция по сессии: состояние канала локально для `conn_id`. +- Асинхронное восстановление: refill/reconnect выполняются вне пакетного hotpath. + +### Стратегия синхронизации +- Для shared map используются короткие и узкие lock-секции. +- Read-heavy пути избегают длительных write-lock окон. +- Решения по backpressure локализованы на границе route/channel. + +Цель: +- медленный consumer должен деградировать локально, не останавливая глобальный прогресс writer-а. + +### Cancellation и shutdown +Reader/Writer loop должны быть cancellation-aware: +- явные cancel token / close command; +- безопасный unbind/cleanup через registry; +- детерминированный порядок: stop admission -> drain/close -> release resources. + +## Модель согласованности + +### Согласованность сессии +Для одного `conn_id`: +- одновременно ровно один активный route-target; +- close/unbind операции идемпотентны; +- потеря writer-а не оставляет dangling-бинды. + +### Согласованность поколения +Гарантии generation: +- новое поколение не активируется до прохождения минимального coverage-gate; +- предыдущее поколение остаётся в `draining` на время handover; +- принудительный вывод writer-ов ограничен policy (`drain ttl`, optional force-close), а не мгновенный. + +### Согласованность политик +Изменение policy (`adaptive/static floor`, fallback mode, retries) не должно ломать инварианты маршрутизации уже активных сессий. + +## Backpressure и управление потоком + +### Route-level backpressure +Route-каналы намеренно bounded. +При росте нагрузки: +- кратковременный burst поглощается; +- длительная перегрузка переходит в контролируемую drop-семантику; +- все drop-сценарии должны быть прозрачно видны в метриках. + +### Приоритет неблокирующего Reader +Входящий ME-reader path не должен сериализоваться из-за одной перегруженной клиентской сессии. +Практически это означает: +- использовать неблокирующую попытку route в parser loop; +- выносить тяжёлое восстановление в асинхронные side-path. + +## Стратегия доменов отказа + +### Отказ отдельного endpoint +Сначала применяется endpoint-local recovery: +- reconnect в тот же endpoint; +- затем замена endpoint внутри той же DC-группы (если доступно). + +### Деградация уровня DC +Если DC-группа не набирает floor: +- сервис сохраняется на остаточном покрытии (если policy разрешает); +- saturation refill продолжается асинхронно в фоне. + +### Потеря готовности всего пула +Если достаточного ME-покрытия нет: +- admission gate может временно закрыть приём новых подключений (conditional policy); +- уже активные сессии продолжают работать, пока их маршрут остаётся healthy. + +## Архитектурные заметки по производительности + +### Дисциплина hotpath +Допустимо в hotpath: +- фиксированный и дешёвый parsing/validation; +- bounded channel operations; +- precomputed/low-allocation доступ к данным. + +Нежелательно в hotpath: +- повторные дорогие decode; +- широкие lock-секции с `await` внутри; +- высокочастотный подробный logging. + +### Стабильность важнее пиков +Архитектура приоритетно выбирает стабильную пропускную способность и предсказуемую latency, а не краткосрочные пики ценой churn и long-tail reconnect. + +## Правила эволюции модели + +Чтобы расширять модель безопасно: +- новые policy knobs сначала внедрять в Control Plane; +- контракты Data Plane (`conn_id`, route/close семантика) держать стабильными; +- перед дефолтным включением проверять generation/registry инварианты; +- новые recovery/retry стратегии вводить через явный config-флаг. + +## Нюансы отказов и восстановления + +- падение single-endpoint DC — штатный деградированный сценарий; приоритет: быстрый reconnect и, при необходимости, shadow/probing; +- idle-close со стороны peer должен считаться нормальным событием при upstream idle-timeout; +- backoff reconnect-логики должен ограничивать синхронный churn, но сохранять быстрые первые попытки; +- fallback (`ME -> direct DC`) — это переключаемая policy-ветка, а не автоматический признак бага транспорта. + +## Краткий словарь +- `Coverage`: достаточное число живых writer-ов для политики приёма по DC. +- `Floor`: целевая минимальная ёмкость writer-ов. +- `Churn`: частые циклы reconnect/remove writer-ов. +- `Hotpath`: пер-пакетный/пер-коннектный путь, где любые лишние ожидания и аллокации особенно дороги.