mirror of https://github.com/telemt/telemt.git
Runtime Model
This commit is contained in:
parent
ef7dc2b80f
commit
99edcbe818
|
|
@ -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.
|
||||||
|
|
@ -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`: пер-пакетный/пер-коннектный путь, где любые лишние ожидания и аллокации особенно дороги.
|
||||||
Loading…
Reference in New Issue