108 lines
11 KiB
Markdown
108 lines
11 KiB
Markdown
# ADR: Gelios adapter в External Provider Data Plane
|
||
|
||
Статус: **superseded**.
|
||
Дата: 2026-07-13.
|
||
Владелец решения: NODE.DC Platform.
|
||
|
||
> Заменено 2026-07-14 документом
|
||
> [`ADR_L2_OWNED_EXTERNAL_CONNECTORS.md`](ADR_L2_OWNED_EXTERNAL_CONNECTORS.md).
|
||
> Gelios остаётся domain/ontology примером, но не получает отдельный
|
||
> provider-owned gateway: fetch, mapping и collection policy живут в его L2
|
||
> connector instance; Platform предоставляет нейтральный Data Plane.
|
||
|
||
## Контекст
|
||
|
||
Gelios Pro поставляет непрерывную телеметрию, конфигурацию устройств и пространственные данные. В текущем доступе подтверждены 107 видимых units, из них 105 с `lastMsg`; окончательный connection scope должен быть зафиксирован allowlist-ом владельца. Это не данные Ontology Core и не данные Tasker. Они требуют отдельного контура для текущего состояния, истории, геозапросов, аналитики и будущих прогнозов.
|
||
|
||
Gelios является первым provider adapter, который подчиняется общему
|
||
`docs/ADR_EXTERNAL_PROVIDER_DATA_PLANE.md`: connection instance хранит
|
||
connection scope/policy, Engine Credentials владеет provider secret, gateway
|
||
владеет storage/read-моделью, а ontology описывает значения, но не runtime
|
||
data.
|
||
|
||
Физические trike-команды существуют в домене, но **не входят в ingestion или тестирование**. Для них позднее потребуется отдельный, ручной и аудируемый контур.
|
||
|
||
## Решение
|
||
|
||
1. Создать отдельный сервис `platform/services/gelios-gateway` как storage/read gateway:
|
||
- не хранит и не получает provider token;
|
||
- принимает только аутентифицированный safe-read intake от защищённого Engine L2 Collector workflow;
|
||
- применяет allowlist и field policy до записи;
|
||
- нормализует ответ в сущности пакета `gelios`;
|
||
- публикует read-модель для Engine L2 workflow и Map View.
|
||
2. В Engine появляется отдельный Gelios Credential и NDC Agent L2 collection profile:
|
||
- credential скрывает access/refresh pair и его lifecycle;
|
||
- credential привязан только к намеренно пошаренному Collector workflow;
|
||
- Collector содержит allowlist read-capabilities и не имеет command transport;
|
||
- ни секрет, ни provider response без нормализации не передаются в ontology, UI, Gateway или другие workflow.
|
||
3. Выделить сервису собственную БД `gelios-postgres`, не деля её с Tasker, Authentik, Notification Core или Ontology Core. В будущей multi-tenant форме эта БД обслуживает несколько Gelios connection instances, но все records разделены `tenant_id` и `connection_id`.
|
||
4. Базовый движок: PostgreSQL 16 с расширениями TimescaleDB и PostGIS.
|
||
- Timescale hypertable хранит временные ряды и автоматически делит их по времени.
|
||
- PostGIS хранит нормализованные точки и геометрии зон, а не renderer-объекты Cesium.
|
||
- Данный выбор покрывает транзакционную конфигурацию, realtime upsert, исторические запросы, SQL-аналитику и географию одним контуром.
|
||
5. **Не** использовать RabbitMQ Tasker как общую шину Gelios. Если измерения покажут, что прямой writer или число независимых потребителей не справляются, добавить в Gelios-контур NATS JetStream с durable pull consumers. Он даст replay, acknowledgement и контролируемое удержание сообщений.
|
||
6. Engine level-2 Collector — единственная точка provider access; остальные workflow являются потребителями read-модели. Ontology Core остаётся только словарём и контрактами.
|
||
|
||
## Целевой поток
|
||
|
||
```text
|
||
Gelios REST (safe read only)
|
||
-> NDC Agent L2 collection profile + protected credential
|
||
-> authenticated normalized intake
|
||
-> Gelios Gateway: scope -> field policy -> normalizer
|
||
-> gelios-postgres: current state + immutable telemetry history
|
||
-> [при необходимости] NATS JetStream
|
||
-> `fleet.positions.current.v1` read API / realtime subscription
|
||
-> NDC workflow level 2
|
||
-> Map View semantic binding
|
||
-> Cesium renderer adapter
|
||
```
|
||
|
||
Ни один шаг не получает права отправить команду устройству. Красный command-domain находится вне этого потока; metadata каталога команд сохраняется, но send transport не создаётся.
|
||
|
||
## Модель хранения v0
|
||
|
||
| Слой | Назначение | Минимальные записи |
|
||
| --- | --- | --- |
|
||
| Контроль | граница и повторяемость сбора | `access_scope`, `collection_run`, `ingestion_cursor`, endpoint/response metrics |
|
||
| Каталог | стабильные сущности и их конфигурация | `unit`, `unit_group`, `tracker_device`, sensor/maintenance/custom-field definitions |
|
||
| Current state | одна актуальная запись на unit для карты и интерфейса | `unit_current`, `position_fix`, approved operational status |
|
||
| History | неизменяемые события с временем наблюдения и получения | `telemetry_snapshot`, selective `sensor_reading`, restricted raw payload reference |
|
||
| Spatial | геометрии для запросов, не Cesium graphics | point/track/geozone with SRID 4326 |
|
||
| Analytics | роллапы и признаки, не запросы по всему raw | hourly/daily aggregates, feature sets, model runs |
|
||
| Audit | попытки, ошибки, политика, будущие команды | collection audit; separate command audit later |
|
||
|
||
`unit_current` обновляется idempotently по `unitSubjectId`. История записывается append-only с ключом дедупликации, включающим provider unit id, observed time и fingerprint сообщения. Все временные таблицы имеют `observed_at` и `received_at`: задержка поставщика не должна переписывать фактическое время на карте.
|
||
|
||
## Индексы и жизненный цикл
|
||
|
||
- Основной путь истории: `(unit_subject_id, observed_at DESC)`.
|
||
- Пространственный индекс только для нормализованной geography/geometry; рендер-кэши в БД не храним.
|
||
- Сырые `params`/raw messages — restricted, отдельно от публичной Studio read-model.
|
||
- Политики retention, downsampling и резервного копирования должны быть утверждены до запуска history backfill. Они зависят от фактических msg/s, размера payload, нужной глубины истории, RPO/RTO и стоимости хранения.
|
||
- Для предиктива держать recent/raw слой и отдельные часовые/дневные агрегаты. Timescale continuous aggregates позволяют сохранять длительную агрегированную историю после сокращения raw при корректно согласованных refresh и retention политиках.
|
||
|
||
## Нулевая итерация без лишней инфраструктуры
|
||
|
||
1. Зафиксировать owner-approved allowlist и видимые поля.
|
||
2. Сделать только safe-read Engine Collector с ограничением по scope, rate limit, paging и cursor.
|
||
3. В течение согласованного окна измерить: сообщений/сек, размер ответа, lag, дубликаты, задержку записи и нагрузку запросов карты.
|
||
4. На фактах включить Timescale hypertables, PostGIS и retention policy; после этого решить, нужен ли JetStream сразу.
|
||
5. Подать только `fleet.positions.current.v1`/approved current position в Map View. Историю и raw не отдавать в renderer напрямую.
|
||
|
||
## Что не решено этим ADR
|
||
|
||
- окончательное правило connection scope;
|
||
- частота polling/возможность provider push;
|
||
- сроки хранения raw, нормализованной истории и агрегатов;
|
||
- RPO/RTO, репликация и production backup plan;
|
||
- допуск к ручному command gateway. До отдельного решения отправка команд запрещена.
|
||
|
||
## Обоснование и источники
|
||
|
||
- [Timescale hypertables](https://docs.timescale.com/use-timescale/latest/hypertables/) — временные таблицы PostgreSQL автоматически партиционируются по времени.
|
||
- [Timescale self-hosted installation](https://docs.timescale.com/self-hosted/latest/install/) — расширение разворачивается как self-hosted PostgreSQL-контур; production требует backup/PITR и HA-плана.
|
||
- [Retention и continuous aggregates](https://docs.timescale.com/use-timescale/latest/data-retention/data-retention-with-continuous-aggregates/) — raw и агрегаты требуют согласованных lifecycle-политик.
|
||
- [PostGIS](https://postgis.net/docs/en/) — PostgreSQL-расширение для spatial types и GiST R-tree индексов.
|
||
- [NATS JetStream consumers](https://docs.nats.io/nats-concepts/jetstream/consumers) — durable consumers дают acknowledgement, повторную доставку и recovery; рекомендуются pull consumers для новых масштабируемых обработчиков.
|