Квоты и retention
Перевод в процессе / Translation in progress. See русская версия.
Тариф организации напрямую транслируется в два численных контракта: верхний предел ingestion (сколько логов и метрик в единицу времени принимает бэкенд) и срок хранения данных в ClickHouse и Postgres. Оба параметра вычисляются по PlanLimits домена и применяются отдельно для логов и метрик, без общего бюджета между ними. Когда клиент превышает лимит или данные выходят за окно хранения, система реагирует предсказуемо: ingestion возвращает HTTP 429 quota_exceeded, а ежедневный воркер вычищает просроченные строки.
Семантика 429 quota_exceeded
При превышении лимита ingestion возвращает HTTP 429 с телом, содержащим error: quota_exceeded и reset_at — UTC timestamp момента, когда квота снова станет доступной. В том же ответе бэкенд устанавливает заголовок Retry-After со значением в секундах. При одновременном наличии обоих источников backoff Retry-After имеет приоритет как authoritative backoff: агент ждёт ровно столько секунд, сколько указано в заголовке, и только после этого сверяется с reset_at в теле. Такая комбинация закрывает два сценария — короткое мгновенное окно (заголовок) и точное абсолютное время разблокировки (поле).
Где энфорсятся квоты
Квоты проверяются только в Flaree.Ingestion через абстракцию IIngestQuotaPolicy. Никакой другой модуль не проверяет квоты: ни Modules.Logs, ни Modules.Metrics, ни контроллеры Api — поздние проверки тратят storage I/O впустую, потому что к моменту записи в Redis Streams решение о приёме уже должно быть принято. Это политика, явно зафиксированная в AGENTS.md разделом про Logs / OTLP / Metrics и поддержанная архитектурным тестом ObservabilityModuleIsolationTests.
Per-org retention
Retention применяется per-org, а не через единый глобальный TTL на таблице: у каждой организации — своё окно хранения, вычисленное из её тарифа. Вычистку выполняет ежедневный Hangfire-джоб RetentionWorker, живущий в Flaree.Worker. Рекуррентные Hangfire-jobs регистрируются ТОЛЬКО в Flaree.Worker/Program.cs — Hangfire dashboard на Api read-only. Конкретное значение retention для каждой организации вычисляется отдельно, поэтому удаление всегда кастомно на каждую организацию.
Поведение агента на 429
Получив 429 quota_exceeded, агент не роняет пайплайн: неотправленные батчи уходят в buffer на диске — локальный SQLite spool. Повторные попытки идут с экспоненциальным backoff и jitter, чтобы равномерно размазать нагрузку при восстановлении. Заголовок Retry-After уважается как authoritative — агент не шлёт запросы раньше указанного срока. Каждое событие quota_exceeded логируется как structured event и surface-ится в heartbeat, чтобы оператор видел состояние прямо в дашборде.