Перейти к содержимому

F-06 real-data verification — partial success (rate-limit ban) + observability gap

Outcome

Partial success: code path verified до WB API, но real data pull blocked sustained rate-limit ban (token quota exhausted после многократных smoke попыток в течение дня).

Что подтверждено working:

  • init_engine + get_session + RLS organization_context — без ошибок открывают session под razmakh_app.
  • _create_run — INSERT into etl.run (running) выполняется, run_id присваивается (UUID v7).
  • ProxyPool.from_env() — pool initializes 60 proxies (proxy_pool_size=60 в logs).
  • WB statistics-api request формируется корректно (URL, token fingerprint, dateFrom param).
  • WBClient._handle_429 правильно parses X-Ratelimit-Retry / Retry-After header (видим wait_sec=3988.0 для statistics-api, wait_sec=83045.0 для common-api в structured logs).
  • Rate limiter get_global_limiter().acquire(token, url) per-(token, endpoint) bucket срабатывает без exceptions.

Что не подтверждено реальными данными:

  • parse_response на live WB JSON (только smoke с empty payload симулировался ранее).
  • upsert в raw.payload с реальным sales array.
  • compute_next_cursor advancing dateFrom на основе real lastChangeDate.
  • End-to-end commit transaction (см. observability gap ниже).

WB endpoint rate-limit reality (verified)

EndpointRate ban observed
https://common-api.wildberries.ru/api/v1/seller-info23+ часа (83045s)
https://statistics-api.wildberries.ru/api/v1/supplier/sales66 мин (3988s)

Ключевой вывод: WB rate-limit per-token (НЕ per-IP) — proxy pool не обходит. Когда token переходит порог 429 → sustained cooldown сильно > 60s. Это исследовано в research/C-wb-api-inventory.md “Rate limits & задержки данных”, но обнаружено эмпирически при попытке smoke сегодня. Implication для production: повторные failed runs на одном token => spiral lockout. Нужен circuit breaker (после 3 подряд 429 — wait min 1h, не resched).

Observability gap (P20 finding)

etl.run table осталась пустой после нескольких killed runs, хотя в logs видели etl.run.started с присвоенным run_id.

Причина: run_collector в apps/api/src/razmakh_api/etl/runner.py:193 оборачивает весь lifecycle в одну transaction (async with get_session() as session, organization_context(...)), COMMIT происходит только после finalize_run. При SIGKILL (timeout / pkill) — rollback автоматический, etl.run INSERT откатывается.

Fix: _create_run нужно делать в отдельной transaction (commit сразу), чтобы running runs были видимы в DB для observability / dashboards. Запиcaть в backlog как F-06.1.

Что осталось deferred

  • F-06.1 (new): split etl.run start commit в отдельную transaction. P20 issue для GitHub.
  • F-06.2 (new): circuit breaker для WB token — после N подряд 429 wait min 1h перед следующей попыткой (защита от spiral lockout).
  • Block 3 (orders+sales section): parse sales JSON в core.sale table — пока upsert пишет только в raw.payload. Это by-design для Phase 1 simple version.
  • Live retry: дождаться ≥1ч после последнего 429 и повторить sudo bash /tmp/run_sales_full.sh sales на razmakh-vps для full end-to-end verification.

Metrics

  • Code path completion: 8/12 checkpoints (init_engine ✓, RLS context ✓, etl.run.start ✓, cursor load ✓, smoke ✓, fetch URL build ✓, rate limit acquire ✓, 429 handler ✓ — но: actual fetch ✗, parse_response ✗, upsert ✗, finalize commit ✗).
  • Total time invested today: ~30 min (multiple retry attempts + diagnosis + lesson).
  • Files touched: 0 (verification only, no code changes per task spec).

Lesson learned

В контексте verification против rate-limited APIs (WB statistics, advert, finance) — НЕ запускать smoke runs повторно в коротком окне. Один failed run + 429 = ban на 60-1500 мин. Сначала dry-run с локальным mock сервером, потом ровно одна попытка против real API.

Observability invariant: для long-running orchestrators (ETL, jobs, sagas) — entity start должен коммититься в отдельной transaction, иначе killed processes становятся invisible. Это критический gap для on-call (нельзя ответить “что сейчас выполняется?” из DB).

Verification

  • Real WB API HTTP requests sent (visible в structlog: wb.api.get events with token_fp, url, params).
  • 429 response parsed correctly (X-Ratelimit-Retry and Retry-After headers both honored).
  • DB schema deployed (alembic migration 0002_etl_schema_and_seed.py applied — confirmed via \dt etl.* and \dt raw.* showing all 5 expected tables + partition payload_y2026m05/06).
  • Seed данные присутствуют: organization_id=019e3657-e3ef-7cd0-850e-445d9021c9d4, marketplace_account_id=019e3657-e3f0-7993-a6c0-5dba12f727c7 (refs в plan/00 F-06).

Дополнительные ссылки

  • lessons/f06-skipped-research-halturность-2026-05-17.md — почему research prereqs важно.
  • research/C-wb-api-inventory.md §Rate limits — WB rate limit per-token spec.
  • research/H-wb-api-deep.md §X-Ratelimit-Retry — header handling per spec.

Notes

  • Lesson также сохраняется в netnik-state/lessons/razmakh-f06-real-data-verified-2026-05-17.md per P16 dual-storage.