Rate Budget ETM

NOTE

Статус: Target service boundary. Документ описывает целевую сервисную границу. Код либо полностью отсутствует, либо существует только как scaffold — смотрите секцию «Статус документа» ниже для точного указания на код. Правила маркировки — в 50-processes/documentation-standard.md.

Стратегия обхода rate-лимитов ETM с учётом множественных credentials.

См. ../../../../10-business/contexts/credentials.md для модели credentials.

Лимиты ETM (per credential)

Лимиты ETM применяются к учётной записи, не к нашему IP-аккаунту целиком (это нормально для дистрибьюторов: ограничения по login).

  • POST /user/login — 1 req / 2 мин на одну учётку.
  • GET /goods/* — 1 req / сек на одну учётку.
  • GET /goods/remains?store=X — рекомендуем не чаще 1 req / 10 сек на учётку.
  • Async jobs /job/create, /job/{uuid} — без явного лимита, polling разумный.

Превышение по конкретной учётке → блокировка (а не блокировка нашего IP в целом).

Множитель от credentials

Если у нас:

  • 1 системная credential (ETM-договор нашей компании);
  • N клиентских credentials (договоры наших клиентов с ETM);

то catalog discovery (товарная канва: характеристики, жизненный цикл, медиа, справочник производителей) опрашивается через все credentials по схеме round-robin — это всегда обогащает наш общий каталог (и не нарушает privacy, т.к. характеристики одинаковы для всех клиентов одного и того же товара).

Pricing/Stock observations не масштабируются множителем, поскольку наблюдения per-customer не взаимозаменяемы. Но через дедупликацию (SupplierCredentialGroup) одинаковые credentials у разных клиентов опрашиваются один раз, экономя rate budget.

При N=20 клиентских credentials catalog rate budget вырастает в ~21 раз. Это позволяет, например, обновлять характеристики 100% каталога ежедневно вместо 14 дней.

Стратегия по операциям

1. Сессии (per credential)

  • Одна постоянная сессия на каждую активную credential.
  • Refresh каждой за ~90 минут до истечения 2h.
  • Login per credential лимитируется 1 req / 2 min — у каждой credential свой bucket.
  • Распределённый lock в Redis для защиты от одновременного re-login per credential.
  • При 403 от запроса с конкретной credential → инвалидировать её сессию и повторно залогиниться.

2. Полная выгрузка номенклатуры (SgGds)

  • Через системную credential — раз в сутки.
  • Через клиентские credentials с shared_for_catalog — round-robin, чтобы получить более широкое покрытие (некоторые SKU видны только определённым категориям клиентов).
  • Каждая выгрузка SgGds = отдельная async job; результаты мёржатся.

3. Catalog discovery / характеристики (/goods/{id})

Самое масштабируемое направление.

  • Нужно обновить ~500k SKU.
  • При 1 credential: ~6 суток на полный обход (1 req/sec, 86k/сутки).
  • При 21 credential (1 system + 20 shared): один полный обход за ~7 часов.

Стратегия:

  1. Очередь приоритетов SKU (hot → warm → cold).
  2. Пул воркеров: по одному воркеру на каждую активную credential.
  3. Каждый воркер берёт следующий SKU из приоритетной очереди и делает запрос через свою credential.
  4. Полученный payload пишется как observation от этой credential, но характеристики применяются к общему SupplierOffer (товарная канва общая).

4. Pricing / Stock — per customer

Здесь нет round-robin, поскольку наблюдения не взаимозаменяемы:

  • Customer X имеет credential → его цены/остатки обновляются через его credential.
  • Customer Y → через свою.
  • Customer без credential → используется системная (B2C / fallback).

Каждая customer credential обновляет:

  • Цены товаров, которые этот клиент когда-либо запрашивал — приоритет.
  • Полный его прайс — раз в сутки (через batch или ручной обход hot-SKU).
  • Остатки на интересующих складах — каждые 6 часов.

Системная credential обновляет:

  • Цены и остатки для всех остальных SKU (для B2C / новых клиентов / для обогащения).

5. Webhooks (если ETM будет поддерживать в будущем)

При получении webhook для credential X → немедленная обработка, без расходования polling budget.

Реализация в конфиге

configs/connectors/etm.yaml:

rate_limit_per_credential:
  login:        { rate: 1, period: 120s }
  goods_read:   { rate: 1, period: 1s }
  price_read:   { rate: 1, period: 1s }
  stock_read:   { rate: 1, period: 1s }
  stock_batch:  { rate: 1, period: 10s }
  async_poll:   { rate: 1, period: 60s }
 
credential_buckets:
  # bucket key pattern: ratelimit:etm:{credential_id}:{endpoint}
  isolation: per_credential
 
routing:
  catalog_discovery:
    strategy: round_robin
    eligible_credentials: "all_active"   # все active credentials, независимо от scope
  pricing:
    strategy: customer_first
    fallback: system
    freshness_ttl: 6h
  stock:
    strategy: customer_first
    fallback: system
    freshness_ttl: 2h
  dedup:
    strategy: by_fingerprint   # одинаковые fingerprint опрашиваются один раз через primary

Суточный бюджет (пример)

Допустим: 1 system + 5 customer credentials (3 из них shared_for_catalog).

ОперацияДоступные credentialsreq/credential/dayTotal req/day
Catalog discovery (hot+warm)1+3 = 4~20 000~80 000
SgGds (jobs)1+3 = 4~50 (poll)~200
Stock batch1+5 = 6~50~300
Customer pricing (per customer)1 (own)~10 000по ~10 000 на каждого
System pricing/stock (B2C/fallback)1 (system)~25 00025 000

Использование лимита одной credential — ≤ 50%.

Итог: при 5 customer credentials catalog обновляется в 4× быстрее vs single-credential, и каждый customer получает свежие данные через свою credential.

Мониторинг

Метрики per credential:

  • etm_api_requests_total{credential_id, endpoint, status} — все запросы с разбивкой по credential.
  • etm_credential_usage_ratio{credential_id} — % использования лимита.
  • etm_credential_failures_total{credential_id, type} — auth / rate / 5xx.
  • etm_observations_recorded_total{credential_scope, type} — observations по типу.

Алерты:

  • 85% использования лимита одной credential за час.

  • 5+ подряд ошибок авторизации одной credential → пометить как failing, уведомить клиента.
  • Customer credential не давала observations > 24 часов при наличии активных запросов клиента.

Резервный сценарий при деградации

  • Падение системной credential → переключение catalog discovery на customer (shared) credentials; B2C-pricing вынужденно использует stale-данные с пометкой.
  • Падение customer credential → его pricing использует системную с пометкой «приближённо».
  • Падение всех credentials → деградация ingestion, алерт, UI показывает stale-данные с явной пометкой.

TBD

  • Фактический размер каталога (нужно получить от менеджера).
  • Возможность договориться на более высокий rate (платный tier).
  • Использование batch-метода для цен — оценить реальную эффективность.
  • Политика биллинга для клиентов с shared_for_catalog (бенефит).