fa357a78b4
- обновить README с текущим Kafka, DB, outbox и scaling flow - расширить логи listener, ingestion, publisher и outbox worker - не логировать raw payload и outbox payload - закрыть test controller явным property - добавить Micrometer counters для ingestion и outbox outcomes - сохранить listener flow, payload contract и delivery semantics без изменений
93 lines
4.3 KiB
Markdown
93 lines
4.3 KiB
Markdown
# pcp-route-processing-service
|
||
|
||
## Назначение сервиса
|
||
|
||
`pcp-route-processing-service` принимает route passport из Kafka, парсит входной JSON, нормализует GeoJSON geometry в WKT, сохраняет паспорт маршрута в БД и создаёт downstream events в outbox.
|
||
|
||
Публикация downstream events выполняется не из Kafka listener, а через scheduled outbox worker:
|
||
|
||
1. `RoutePassportMessageListener` читает input Kafka record.
|
||
2. `RoutePassportJsonParserService` валидирует и парсит payload.
|
||
3. `RoutePassportIngestionService` в одной DB transaction сохраняет `route_passports` и создаёт `route_passport_outbox`.
|
||
4. Listener подтверждает offset только после успешного ingestion.
|
||
5. `RoutePassportOutboxWorker` claim'ит due outbox rows и публикует их через `RoutePassportKafkaPublisherService`.
|
||
|
||
## Kafka topics
|
||
|
||
Input topic:
|
||
- `pcp.request.survey-georeference.v1`
|
||
|
||
DLQ topic:
|
||
- `pcp.request.survey-georeference-dlq.v1`
|
||
|
||
Output topics:
|
||
- `pcp.route.in.v1` - событие для mission-planning flow.
|
||
- `pcp.route.georeference.v1` - событие для request-service matching flow.
|
||
|
||
## DB tables
|
||
|
||
- `route_passports` - сохранённые паспорта маршрутов и source metadata входного Kafka record.
|
||
- `route_passport_outbox` - pending/published/failed downstream events для Kafka publication.
|
||
|
||
## Horizontal scaling
|
||
|
||
Несколько pod'ов сервиса могут безопасно запускать outbox worker одновременно.
|
||
|
||
Outbox claim использует DB-level concurrency:
|
||
- `SELECT ... FOR UPDATE SKIP LOCKED` выбирает due `PENDING` rows.
|
||
- `lockedUntil` выставляется в той же transaction, что и claim.
|
||
- После commit другие instances не claim'ят row, пока `lockedUntil` в будущем.
|
||
- Если instance умер после claim, abandoned row снова станет доступна после истечения `lockedUntil`.
|
||
- In-memory locks, leader election и single-instance assumptions не используются.
|
||
|
||
## Delivery semantics
|
||
|
||
Delivery semantics: at-least-once.
|
||
|
||
Exactly-once не обещается. Kafka transactions не используются. Downstream consumers должны быть идемпотентны к повторной доставке одного и того же события.
|
||
|
||
## Error handling
|
||
|
||
Input Kafka flow:
|
||
- contract/parsing errors не подтверждают offset и обрабатываются configured retry/DLQ policy;
|
||
- после исчерпания retry record публикуется в `pcp.request.survey-georeference-dlq.v1`;
|
||
- временные ошибки ingestion retry'ятся bounded policy.
|
||
|
||
Output Kafka flow:
|
||
- downstream events сначала сохраняются в `route_passport_outbox`;
|
||
- output Kafka errors не теряются, а переводят row обратно в `PENDING` с bounded exponential backoff;
|
||
- после исчерпания `maxAttempts` из outbox row событие получает статус `FAILED`.
|
||
|
||
## Observability
|
||
|
||
Логи содержат operational identifiers без полного payload:
|
||
- source topic, partition, offset, key и payload size для input ingestion;
|
||
- `routeId`, `routeNameFull`, `traceId`, `correlationId`;
|
||
- outbox id, event type, target topic, message key, attempts, status, `nextAttemptAt`, `lockedUntil`, `publishedAt`;
|
||
- exception class/message для failure path.
|
||
|
||
Доступные Micrometer counters:
|
||
- `route_passport_ingest_success_total`
|
||
- `route_passport_ingest_failure_total`
|
||
- `route_passport_outbox_published_total`
|
||
- `route_passport_outbox_retry_total`
|
||
- `route_passport_outbox_failed_total`
|
||
|
||
## Operational notes
|
||
|
||
Test controller is disabled by default and must not be exposed in production unless explicitly enabled with:
|
||
|
||
```yaml
|
||
app:
|
||
route-processing:
|
||
test-controller:
|
||
enabled: true
|
||
```
|
||
|
||
## Current business limitations
|
||
|
||
- `routeStatus` сейчас остаётся `PROCESSED`.
|
||
- Сервис не делает request matching.
|
||
- Сервис не считает coverage.
|
||
- Сервис не вызывает request-service или mission-service по HTTP.
|