Из код-ревью #1234 (находка #13).
Суть
Дневной cost cap — check-then-record без резервирования: _lock сериализует только проверку, а расход бронируется record_cost после завершения платного вызова (10-60с) — N конкурентных генераций все проходят проверку. Остаток бюджета = 1 изображение; N генераций стартуют конкурентно (pipeline + agent-tool + веб) → каждая проходит check_cost_cap по пре-спенд итогу → все N платных вызовов уходят → record_cost бронирует всё постфактум → cap превышен на (N-1)×стоимость. «Жёсткая» гарантия #814 не выполняется.
Что сделать
- Атомарный check-and-reserve под единым
_lock: резервировать оценочную стоимость ДО платного вызова, корректировать по факту в record_cost (release разницы).
- Тест: N конкурентных генераций при остатке на 1 не превышают cap.
Файлы
src/services/production_limits_service.py:344-349 (check), record_cost
- потребитель:
src/services/image_generation_service.py:~142
Оценка
- LOC: ~110–240 (src ~90 / tests ~120). Claim-гонка + concurrency-тесты + дизайн резерва → +20% и +25%.
- Размер: L
- Время (агент): ~55–90 мин
Из код-ревью #1234 (находка #13).
Суть
Дневной cost cap — check-then-record без резервирования:
_lockсериализует только проверку, а расход бронируетсяrecord_costпосле завершения платного вызова (10-60с) — N конкурентных генераций все проходят проверку. Остаток бюджета = 1 изображение; N генераций стартуют конкурентно (pipeline + agent-tool + веб) → каждая проходитcheck_cost_capпо пре-спенд итогу → все N платных вызовов уходят →record_costбронирует всё постфактум → cap превышен на (N-1)×стоимость. «Жёсткая» гарантия #814 не выполняется.Что сделать
_lock: резервировать оценочную стоимость ДО платного вызова, корректировать по факту вrecord_cost(release разницы).Файлы
src/services/production_limits_service.py:344-349(check),record_costsrc/services/image_generation_service.py:~142Оценка