gendesign/backend/app/workers/tasks/refresh_quarter_price_index.py
bot-backend 6982255fb3
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m54s
CI / backend-tests (pull_request) Successful in 17m0s
fix(workers): убрать max_retries, который ничего не делает (#2464)
11 тасок объявляли `max_retries=2`, но ретраи не реализовывали: ни
`autoretry_for` в декораторе, ни вызова `self.retry()` в теле. Celery в
таком виде параметр не применяет — при исключении таска падает с первой
попытки. Читающий код видит «до 3 попыток», а их одна.

Убран `max_retries` у: cbr_macro_sync, rosstat_macro_sync,
developer_registry_refresh, location_refresh, mv_sales_tracker_refresh,
refresh_analytics, refresh_layout_velocity, refresh_quarter_price_index,
scrape_objective.sync_objective_group, supply_layers_refresh,
scrape_kn.scrape_kn_region. Заодно убран `bind=True` там, где `self` не
использовался вовсе; в `scrape_kn_region` он оставлен — `self.request.id`
пишется в kn_scrape_log.

Не тронуты и не должны быть: `resume_kn_run` (max_retries=12 +
настоящий self.retry()), `nspd_sync`/`scrape_cadastre` (autoretry_for),
`nspd_geo`/`objective_etl` (max_retries=0 — честное «ретраев нет»).

Гейт `test_2464_retry_config_is_real.py` разбирает AST всех модулей
`app/workers/tasks/` и требует: если декоратор объявляет ненулевой
max_retries, в нём есть autoretry_for либо в теле функции есть
self.retry(). Три таски из одиннадцати гейт нашёл сверх списка эпика.

Проверка гейта: с фиксом зелено, при возврате `max_retries=2` в
supply_layers_refresh — красно с указанием на эту таску. Плюс два
контроля: гейт видит ≥20 тасок (не молчит из-за пустой выборки) и
признаёт обе законные формы ретраев.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 17:17:38 +05:00

49 lines
2.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Celery task: refresh mv_quarter_price_per_m2 → mv_quarter_price_index chain.
Scheduled via hardcoded beat entry in workers/beat_schedule.py:
'refresh-quarter-price-index' — monthly on the 5th at 05:00 MSK (02:00 UTC).
Runs after 'refresh-ekb-districts-medians' (04:00 MSK same day) so that
deals data is settled before the price index is recomputed.
Issue: #762.
"""
from __future__ import annotations
import logging
from typing import Any
from app.core.db import SessionLocal
from app.services.site_finder.quarter_price_index_refresh import refresh_quarter_price_index
from app.workers.celery_app import celery_app
logger = logging.getLogger(__name__)
# Ретраев здесь нет, и параметров, обещающих их, быть не должно (#2464). Стояло
# `bind=True, max_retries=2`, но self не использовался, self.retry() не вызывался и
# autoretry_for задан не был — конфигурация не имела эффекта. Где ретраи нужны, они
# задаются явно: autoretry_for (nspd_sync, scrape_cadastre) или self.retry()
# (scrape_kn.resume_kn_run). Где их сознательно нет — пишется max_retries=0 с
# пояснением (nspd_geo, objective_etl.import_anton_objective).
@celery_app.task(
name="tasks.refresh_quarter_price_index.refresh_quarter_price_index_chain",
)
def refresh_quarter_price_index_chain() -> dict[str, Any]:
"""Refresh mv_quarter_price_per_m2 then mv_quarter_price_index in sequence.
Both MVs are refreshed CONCURRENTLY (non-blocking). Falls back to
non-concurrent if the MV is found unpopulated (edge case on first run).
Returns result dict for Celery task result store / logging.
"""
db = SessionLocal()
try:
count = refresh_quarter_price_index(db, concurrently=True)
logger.info("refresh_quarter_price_index_chain: completed, index rows=%d", count)
return {"status": "ok", "mv_quarter_price_index_rows": count}
except Exception as e:
logger.exception("refresh_quarter_price_index_chain failed: %s", e)
raise
finally:
db.close()