Подготовить skeleton лабораторной 6 по мессенджеру
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# Семантика API
|
||||
|
||||
Этот текст дополняет [openapi.json](openapi.json). Обязательный API соответствует уровню 3 текущей лабораторной. Факультативный crop или rate limiting не становятся обязательными только из-за наличия допустимого имени варианта или кода ошибки в схеме.
|
||||
|
||||
## Формат и ошибки
|
||||
|
||||
- JSON — UTF-8, `Content-Type: application/json`. UUID — строка; время — RFC 3339 UTC с `Z`, назначается сервером. Часы не считаются уникальным ID.
|
||||
- Каждый ответ API, включая ошибки и health, содержит непустой `X-Request-Id`. Тело ошибки: `{"error":{"code":"invalid_request","message":"...","request_id":"..."}}`; `request_id` совпадает с заголовком. Сообщение ошибки не раскрывает stack trace, SQL, credentials или наличие чужого объекта.
|
||||
- Обязательные строки не состоят только из пробельных символов. `username` строго `[a-z0-9_]{3,32}`, уникален; `display_name` — 1–100 Unicode code points. JSON с лишними полями или неверными типами — `400`; malformed JSON также `400`.
|
||||
- `400 invalid_request` — неверные поля, UUID, cursor, limit; `401 unauthorized` — нет действительной идентификации/сессии; `404 not_found` — объекта нет или он чужой; `409 conflict` — username занят, повторный ключ с другим сообщением, объект ещё не готов; `413 payload_too_large`; `415 unsupported_media_type`; `429 rate_limited`; `503 unavailable`. Неожиданное исключение — `500 internal_error` без внутренних деталей.
|
||||
- Сначала аутентификация, затем доступ к конкретному ресурсу. Любой аутентифицированный участник чата может читать его историю и вложения и писать в него; посторонний получает `404`. Списки содержат только доступные объекты. Не используйте `403` для раскрытия существования чужого чата.
|
||||
- `GET /health/live` проверяет только жизнь процесса, возвращает `200 {"status":"ok"}`. `GET /health/ready` возвращает 200, если приложение может безопасно обслуживать обязательный синхронный путь; иначе 503 в общем формате ошибки. Какие зависимости критичны, зафиксируйте в отчёте; недоступность Grafana не должна останавливать API.
|
||||
|
||||
## Пользователи, чаты и сообщения
|
||||
|
||||
- `POST /users` публичен; возвращает `201 User`. Пароль/его hash и сессионные данные никогда не входят в User.
|
||||
- `POST /chats` принимает title и явный список существующих `member_ids`, включая вызывающего. Дубликаты, неизвестные участники и отсутствие вызывающего — 400. Чат создаётся атомарно. GET списка чатов сортируется по `(created_at, id)` по возрастанию.
|
||||
- Отправитель сообщения выводится из текущей идентификации, поле `sender_id` в запросе не принимается. Текст — до 4000 Unicode code points. Нельзя отправить пустой/пробельный текст без вложений.
|
||||
- История использует keyset cursor: `(created_at, id)`, по возрастанию; `limit` — целое 1–100, по умолчанию 50. Ответ `{"items":[...],"next_cursor":null}`. Непустой cursor — непрозрачная для клиента строка; последний элемент предыдущей страницы не повторяется. Cursor привязан к ресурсу/запросу: от другого чата — 400. Изменять `limit` при продолжении можно.
|
||||
- `next_cursor` равен null, если на момент чтения следующей записи нет. Гарантируется отсутствие повторов при обходе неизменной истории; snapshot изоляция между несколькими HTTP-запросами не требуется. При конкурентных вставках опишите выбранную семантику и ограничения часов.
|
||||
- Подтверждённое сообщение видно следующему чтению через любую API-реплику. Допускается кэш, но он не должен возвращать устаревшую историю после успешной записи. Удаление и изменение участников в обязательный API не входят.
|
||||
|
||||
## Идентификация и сессии
|
||||
|
||||
**Lab1:** защищённые endpoint требуют `X-User-Id: <UUID существующего пользователя>`; отсутствие/неизвестный ID — 401. Это учебный выбор пользователя: любой клиент может выдать себя за другого.
|
||||
|
||||
**Lab2–7:** `POST /auth/sessions` принимает только `Authorization: Basic <base64(username:password)>`, без JSON body. Username ограничен ASCII; пароль кодируется UTF-8, 12–128 code points, двоеточие в пароле допустимо. Неверные credentials — одинаковый 401 и `WWW-Authenticate: Basic realm="messenger", charset="UTF-8"`.
|
||||
|
||||
Вход возвращает `201 {"token":"...","token_type":"Bearer","expires_at":"...Z","user":{...}}`. Токен — непрозрачное случайное значение, не username/UUID и не обязательный JWT; энтропия не менее 256 бит. Храните серверную сессию с expiry; TTL задаётся `SESSION_TTL_SECONDS`, по умолчанию 3600 (в тесте истечения можно 2). В обычном smoke TTL должен быть не менее 300.
|
||||
|
||||
Все защищённые endpoint принимают только `Authorization: Bearer <token>`. Истёкший/отозванный/неизвестный токен — 401 с `WWW-Authenticate: Bearer realm="messenger"`. Basic на бизнес-endpoint — 401. `X-User-Id` с lab2 игнорируется: он не меняет владельца сессии и сам по себе не даёт доступ. `DELETE /auth/sessions/current` отзывает текущий токен и возвращает 204 без тела; повтор с отозванным токеном — 401.
|
||||
|
||||
Пользователи, чаты, сообщения и ещё действительные сессии переживают перезапуск приложения и используемых хранилищ с сохранёнными volumes. TTL не продлевается из-за рестарта. Пароли уже с lab2 хешируются библиотечным password KDF (Argon2id, scrypt или bcrypt с учётом ограничений библиотеки), не SHA-256 и не plaintext. Если bcrypt не поддерживает весь диапазон UTF-8 паролей без усечения, выберите другую библиотечную схему. В lab7 обязателен Argon2id.
|
||||
|
||||
Basic — это кодирование, не шифрование. HTTP lab1–6 допускается только для локального изолированного стенда с синтетическими данными; для удалённой демонстрации нужен TLS. Lab7 использует HTTPS обязательно. См. [RFC 7617](https://www.rfc-editor.org/info/rfc7617/).
|
||||
|
||||
## Балансировка и повторная отправка (с lab3)
|
||||
|
||||
- Каждый ответ, созданный API, содержит `X-Instance-Id`; значение стабильно для жизни процесса. Ошибки соединения, созданные самим LB, могут не иметь этого заголовка. В lab7 используйте случайный alias реплики без раскрытия hostname/IP.
|
||||
- Для POST сообщения обязателен `Idempotency-Key` (ASCII `[A-Za-z0-9._:-]{1,128}`). Нет/неверный ключ — 400. Область уникальности: текущий пользователь + HTTP-метод + путь чата + ключ.
|
||||
- При повторе того же валидного JSON в течение 24 часов сервер возвращает тот же код 201 и то же Message. Порядок ключей JSON и отсутствующий `attachment_ids` вместо `[]` семантически равны. Другой body при том же ключе — 409. Разные пользователи/чаты могут использовать одинаковый ключ независимо.
|
||||
- Одновременные повторы через разные реплики создают ровно одно сообщение. Повтор снова проверяет действительность сессии и доступ. Транзиентные 5xx не фиксируются как окончательный результат; побочный эффект и запись результата должны быть атомарны.
|
||||
- Ключ обязателен только для сообщений. Самостоятельный retry загрузки файла или создания чата может создать ещё один объект; не добавляйте прозрачные повторы этих POST без расширения контракта.
|
||||
|
||||
## Вложения (с lab4)
|
||||
|
||||
- `POST /chats/{chat_id}/attachments?filename=photo.png`: сырые bytes одного PNG/JPEG; `Content-Type: image/png` или `image/jpeg`. Не multipart. Максимум **10 MiB = 10 485 760 байт**. MIME проверяется по содержимому; заведомо повреждённые/чужие форматы — 415, превышение bytes — 413. Не более 40 миллионов декодированных пикселей (превышение — 413). Устанавливайте лимит до полной распаковки изображения.
|
||||
- `filename` — отображаемое имя (1–255 code points). Не используйте его как путь/ключ S3. Имя с `/`, `\`, NUL или управляющими символами — 400. Bucket приватен; object key генерирует сервер. Оригинал возвращается побайтно, его lowercase SHA-256 и размер соответствуют исходному файлу.
|
||||
- Lab4 возвращает `201 Attachment` со `status=ready`, `variants=[]`, `error_code=null` только после сохранения оригинала и метаданных. Lab5–7 возвращают `202 Attachment` со снимком `status=queued`; готовность определяется последующим GET метаданных. Быстрый worker может закончить до первого GET — это нормально.
|
||||
- Файл заранее привязан к чату. В POST сообщения добавляется `attachment_ids` (0–10 уникальных ID, по умолчанию []). Можно отправить `text=""` с хотя бы одним вложением. Все вложения должны принадлежать этому чату; чужое/неизвестное — 404. Любой участник может ссылаться на доступное вложение чата. Failed-вложение прикреплять нельзя (409); queued/processing с lab5 можно.
|
||||
- `GET /attachments/{id}` и `/content` доступны только участникам чата. Download возвращает байты с истинным Content-Type, через авторизованный API, **без redirect** в обязательном контракте. Приватный bucket сам по себе не заменяет эту проверку. `variant` по умолчанию original. Для queued/processing content — 409; для failed — 409. Отсутствующий вариант ready-объекта — 404.
|
||||
- В ready-метаданных `error_code=null`. В failed — краткий машинный код (например `invalid_image`, `processing_failed`, `retry_exhausted`), без stack trace. `size_bytes`, `sha256`, `content_type` всегда описывают оригинал; у вариантов отдельные поля. Публичный API не раскрывает ключи бакета, внутренние адреса и credentials.
|
||||
- Прямой upload/download по временной подписи — факультативное расширение. Его дополняют ограничение срока/размера, уникальный ключ и подтверждение реально загруженного объекта; он не заменяет стандартный endpoint. Особенности подписанных URL: [S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html).
|
||||
|
||||
## Фоновая обработка (с lab5)
|
||||
|
||||
До 202 все байты приняты в **устойчивый staging**, а намерение обработать файл надёжно сохранено. HTTP-передача клиента не исчезает и не становится мгновенной. Staging — приватный S3-prefix либо общий persistent volume, доступный worker; локальная память/API ephemeral disk не подходит. Worker асинхронно переносит/публикует оригинал в конечный S3-prefix и создаёт варианты; если staging уже в S3, опишите эту границу честно.
|
||||
|
||||
Обязательный thumbnail: JPEG, изображение вписано в 256×256 с сохранением пропорций, без увеличения маленьких исходников, EXIF orientation применяется; alpha компонуется на белый. Размеры округляются вниз, минимум 1 px. Fixture 320×200 должен дать 256×160. Статус ready ставится после сохранения оригинала и thumbnail. Crop на 4: JPEG, центрированный квадрат 128×128 (для маленьких исходников допускается увеличение). Optimized на 4: WebP, вписан в 1280×1280 без увеличения, параметры качества и удаления EXIF документируются; не обещайте уменьшение каждого маленького файла.
|
||||
|
||||
Допустимые состояния и события описаны в `jobs.schema.json` и `attachment-states.md`. Доставка — at least once; обработка идемпотентна, повторы не размножают варианты. Финальные ошибки наблюдаемы, бесконечный немой retry недопустим. Обязательный smoke ждёт ready до 60 секунд; это бюджет локального smoke для одного файла при свободном worker, не универсальный production SLA. В очередь идут ссылки/ID, не бинарное содержимое.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Состояния вложения и договор между producer/worker
|
||||
|
||||
Сообщение broker соответствует [jobs.schema.json](jobs.schema.json). Можно применять native headers брокера для trace context, но семантика carrier сохраняется; для проверки покажите отображение полей. `job_id` идентифицирует одну логическую работу и не меняется при redelivery; `attempt` начинается с 1 и отражает управляемую попытку обработки (broker redelivery сам по себе не создаёт новый job_id). Ключ эффекта — `(attachment_id, pipeline_version, variant)`.
|
||||
|
||||
| Переход | Условие | Что устойчиво сохранено |
|
||||
| --- | --- | --- |
|
||||
| нет → queued | Принимаем upload; только после durable handoff отвечаем 202 | Полный проверенный оригинал в staging, метаданные, намерение обработки |
|
||||
| queued → processing | Worker захватил/арендовал работу | Попытка, lease/deadline или эквивалентная защита |
|
||||
| processing → queued | Временная ошибка и остались попытки | Причина без секретов, время следующей попытки |
|
||||
| processing → ready | Все обязательные объекты записаны и результат атомарно опубликован | Оригинал, thumbnail (на 4 ещё crop/optimized), метаданные/checksums |
|
||||
| processing → failed | Ошибка постоянная или попытки исчерпаны | error_code и запись, доступная оператору |
|
||||
| processing → queued | Истёк lease после смерти worker | Восстановленная работа, без дублирования эффектов |
|
||||
| failed → queued | Явный операторский replay, доступен на 4 | Audit, новая попытка, прежний логический эффект защищён от дубля |
|
||||
|
||||
Повторная доставка уже ready-задачи — no-op с ack после проверки завершённого эффекта. Конкурентные попытки не перезаписывают более новый pipeline_version. Основной курс использует pipeline_version=1; версионирование нескольких pipeline — трек 5. Заявляйте готовность только когда все обещанные уровнем варианты доступны. Нельзя откатывать ready в processing из-за старого дубля.
|
||||
|
||||
**Окна отказа для защиты:** после staging до записи задания; после COMMIT до publish; после publish до confirm; после записи S3 до COMMIT результата; после COMMIT результата до ack. На 3 покажите сохранность успешно принятой работы и повторную доставку; на 4 автоматическое восстановление разрыва DB/broker (outbox/эквивалент) и зависшего processing.
|
||||
|
||||
Staging не удаляется до безопасной публикации результатов. Неуспешные/непривязанные исходники имеют документированный retention и cleanup; он не удаляет текущую работу. Broker принимает только ID/служебные данные, не произвольный URL для скачивания и не путь из клиентского filename. Worker берёт доверенные storage metadata из БД.
|
||||
|
||||
Ack подтверждает устойчивую обработку, publisher confirm — приём брокером; это разные границы. См. [RabbitMQ reliability](https://www.rabbitmq.com/docs/reliability). At-least-once + идемпотентный эффект не означает exactly-once доставку.
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"CreateUser": {
|
||||
"username": "alice",
|
||||
"display_name": "Алиса",
|
||||
"password": "Example-only-not-a-real-secret-123"
|
||||
},
|
||||
"User": {
|
||||
"id": "11111111-1111-4111-8111-111111111111",
|
||||
"username": "alice",
|
||||
"display_name": "Алиса",
|
||||
"created_at": "2026-09-01T12:00:00Z"
|
||||
},
|
||||
"CreateChat": {
|
||||
"title": "Проект по ВНП",
|
||||
"member_ids": [
|
||||
"11111111-1111-4111-8111-111111111111",
|
||||
"22222222-2222-4222-8222-222222222222"
|
||||
]
|
||||
},
|
||||
"Chat": {
|
||||
"title": "Проект по ВНП",
|
||||
"member_ids": [
|
||||
"11111111-1111-4111-8111-111111111111",
|
||||
"22222222-2222-4222-8222-222222222222"
|
||||
],
|
||||
"id": "33333333-3333-4333-8333-333333333333",
|
||||
"created_at": "2026-09-01T12:00:00Z"
|
||||
},
|
||||
"CreateMessage": {
|
||||
"text": "Привет, Боб!"
|
||||
},
|
||||
"Message": {
|
||||
"id": "44444444-4444-4444-8444-444444444444",
|
||||
"chat_id": "33333333-3333-4333-8333-333333333333",
|
||||
"sender_id": "11111111-1111-4111-8111-111111111111",
|
||||
"text": "Привет, Боб!",
|
||||
"created_at": "2026-09-01T12:00:00Z",
|
||||
"attachment_ids": []
|
||||
},
|
||||
"MessagePage": {
|
||||
"items": [
|
||||
{
|
||||
"id": "44444444-4444-4444-8444-444444444444",
|
||||
"chat_id": "33333333-3333-4333-8333-333333333333",
|
||||
"sender_id": "11111111-1111-4111-8111-111111111111",
|
||||
"text": "Привет, Боб!",
|
||||
"created_at": "2026-09-01T12:00:00Z",
|
||||
"attachment_ids": []
|
||||
}
|
||||
],
|
||||
"next_cursor": null
|
||||
},
|
||||
"Error": {
|
||||
"error": {
|
||||
"code": "not_found",
|
||||
"message": "Объект не найден",
|
||||
"request_id": "example-request-id"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"lab": 6,
|
||||
"kind": "requirements, not runnable deployment",
|
||||
"required_for_grade_3": [
|
||||
{
|
||||
"role": "api",
|
||||
"count_min": 2,
|
||||
"student_implements": true,
|
||||
"persistent_local_state": false
|
||||
},
|
||||
{
|
||||
"role": "postgres",
|
||||
"purpose": "users/chats/membership/messages/metadata",
|
||||
"persistent_volume": true
|
||||
},
|
||||
{
|
||||
"role": "load_balancer",
|
||||
"public_entrypoint": true,
|
||||
"backend_readiness": true
|
||||
},
|
||||
{
|
||||
"role": "s3",
|
||||
"choose_one": [
|
||||
"MinIO",
|
||||
"SeaweedFS",
|
||||
"RustFS"
|
||||
],
|
||||
"private_bucket": true,
|
||||
"persistent_volume": true
|
||||
},
|
||||
{
|
||||
"role": "broker",
|
||||
"choose_one": [
|
||||
"RabbitMQ",
|
||||
"Redis Streams",
|
||||
"Valkey Streams",
|
||||
"NATS JetStream",
|
||||
"documented durable equivalent"
|
||||
],
|
||||
"persistent_volume": true,
|
||||
"delivery": "at-least-once"
|
||||
},
|
||||
{
|
||||
"role": "worker",
|
||||
"separate_process": true
|
||||
},
|
||||
{
|
||||
"role": "otel_collector",
|
||||
"signals": [
|
||||
"metrics",
|
||||
"logs",
|
||||
"traces"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "telemetry_storage",
|
||||
"signals": [
|
||||
"metrics",
|
||||
"logs",
|
||||
"traces"
|
||||
],
|
||||
"possible_combination": [
|
||||
"Prometheus",
|
||||
"Loki",
|
||||
"Tempo"
|
||||
],
|
||||
"persistent_volume": true
|
||||
},
|
||||
{
|
||||
"role": "grafana",
|
||||
"provisioning_in_git": true
|
||||
}
|
||||
],
|
||||
"optional_components": [
|
||||
{
|
||||
"role": "redis_or_valkey",
|
||||
"required_for_lab2_grade4": true,
|
||||
"note": "Choose one; if only store of sessions, configure durable persistence."
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
"Choose Compose or Swarm for lab3+; do not need both.",
|
||||
"Never publish internal API replica ports in bypass of LB from lab3.",
|
||||
"Versions/digests must be fixed in implementation.",
|
||||
"Do not remove volumes in restart/failover checks.",
|
||||
"Document service names, healthchecks, bootstrap/migrations and credentials."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "urn:mtusi:messenger:attachment-job:v1",
|
||||
"title": "Durable attachment processing job v1",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"job_id",
|
||||
"type",
|
||||
"attachment_id",
|
||||
"pipeline_version",
|
||||
"attempt",
|
||||
"created_at"
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"const": 1
|
||||
},
|
||||
"job_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"type": {
|
||||
"const": "attachment.process.v1"
|
||||
},
|
||||
"attachment_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"pipeline_version": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"attempt": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"traceparent": {
|
||||
"type": "string",
|
||||
"pattern": "^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$"
|
||||
},
|
||||
"tracestate": {
|
||||
"type": "string",
|
||||
"maxLength": 512
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"schema_version": 1,
|
||||
"job_id": "11111111-1111-4111-8111-111111111111",
|
||||
"type": "attachment.process.v1",
|
||||
"attachment_id": "22222222-2222-4222-8222-222222222222",
|
||||
"pipeline_version": 1,
|
||||
"attempt": 1,
|
||||
"created_at": "2026-09-01T12:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# Контракт наблюдаемости
|
||||
|
||||
Ниже логические сигналы. Допустимы стандартные semantic conventions выбранного SDK и их native metric names: зафиксируйте соответствие в `REPORT.md`, dashboard обязан ссылаться на реально существующие имена/единицы. Переименование SDK не повод дублировать одну метрику двумя инструментами. Версии SDK, semantic conventions, Collector distribution и backend закрепляются в решении.
|
||||
|
||||
| Сигнал | Минимальные поля/измерения | Уровень |
|
||||
| --- | --- | --- |
|
||||
| HTTP requests | method, route template, status class; counter | 3 |
|
||||
| HTTP duration | histogram в секундах или документированное преобразование из ms | 3 |
|
||||
| Jobs processed / failed | job type, outcome, counter | 3 |
|
||||
| Job processing duration | job type, outcome, histogram | 3 |
|
||||
| API и worker logs | timestamp, severity, service.name, event, trace_id, span_id при активном span | 3 |
|
||||
| API и worker traces | реальные server/consumer spans, duration, outcome; не искусственный статический trace | 3 |
|
||||
| Queue depth / oldest job age | queue name, количество / секунды | 4 |
|
||||
| Pool/saturation | DB pool used/waiting, worker busy/capacity, process/container CPU/RAM | 4 |
|
||||
| Сквозная связь upload → publish → consume → S3/DB | W3C traceparent/tracestate; parent-child или link по выбранной семантике | 4 |
|
||||
| End-to-end time to ready | от durable acceptance до публикации ready, histogram | 4 |
|
||||
|
||||
Resource attributes: `service.name` (messenger-api / messenger-worker), `service.version`, `service.instance.id`, `deployment.environment.name=lab`. Список имён может отличаться, если явно сопоставлен. `X-Request-Id` коррелирует HTTP-ошибку с логом; trace_id — не замена всех request IDs.
|
||||
|
||||
**Не labels метрик:** user_id, chat_id, message_id, attachment_id, request_id, raw URL, filename, текст. Для маршрута используйте `/api/v1/chats/{chat_id}/messages`, не реальный UUID. В логах/трассах допустимы ограниченные технические ID для учебных синтетических данных, если обоснована необходимость; пароли, Authorization, session token, body сообщения, картинка, private key и signed URL запрещены.
|
||||
|
||||
Для HTTP error ratio заранее определите numerator/denominator: например 5xx/все бизнес-запросы, исключая health; не смешивайте неверный пароль клиента с отказом БД. p95 агрегируется из histogram buckets всех нужных реплик, а не средних p95. Границы buckets должны соответствовать ожидаемым задержкам. Backlog — не queue processing latency.
|
||||
|
||||
В Git находятся Collector config, backend configs, datasources/dashboard provisioning, dashboard JSON и на 4 alert rules. У alerts есть условие, окно, порог, `for` при необходимости, описание и локальный runbook. Отправка сообщений в почту/мессенджер не требуется.
|
||||
|
||||
Минимальные контролируемые инциденты для 4: (1) остановить worker и увидеть рост возраста задач; (2) сделать DB медленной/недоступной и отличить её от медленного HTTP handler по trace/метрикам. После восстановления backlog уменьшается, alerts resolved. Третий сценарий — недоступность backend telemetry: SDK/Collector ограничивают RAM/очередь, API сохраняет работу, потери telemetry считаются и объясняются.
|
||||
|
||||
Collector — получатель/обработчик/экспортёр, не долговременная БД. Grafana — интерфейс и запросы к источникам. Логи можно отправлять OTLP в совместимый backend; не закладывайте удалённый/устаревший exporter без проверки текущей сборки. См. [Collector configuration](https://opentelemetry.io/docs/collector/configuration/), [Trace Context](https://www.w3.org/TR/trace-context/) и [histograms](https://prometheus.io/docs/practices/histograms/).
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user