lifespan 加 _fail_stale_ingest_jobs():UPDATE ingest_jobs SET status=failed
WHERE status IN ('pending','running');异常仅记 warning 不阻断启动。
顺手修复 0021(op.text→字符串)/0023(索引名 leason→league)/0024(type_unique→type_)
三个待执行迁移 bug。
测试 test_p1_e_stale_ingest_jobs(2/2 mock);全量 315 通过。
42 lines
1.4 KiB
Python
42 lines
1.4 KiB
Python
"""P0-03: Prediction 幂等指纹——移除旧唯一约束,改为 partial unique on input_hash
|
|
|
|
input_hash 非空时唯一(同指纹返回已有行,不 UPDATE/INSERT);
|
|
兼容旧数据 NULL input_hash(不强制回填)。
|
|
|
|
Revision ID: 0024_prediction_idempotent_fingerprint
|
|
Revises: 0023_standings_append_only
|
|
Create Date: 2026-09-22
|
|
"""
|
|
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
revision: str = '0024_prediction_idempotent_fingerprint'
|
|
down_revision: Union[str, None] = '0023_standings_append_only'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
# 移除旧唯一约束(match, provider, model, mode, run_type)
|
|
op.drop_constraint(
|
|
'uq_predictions_match_provider_model_mode_run_type',
|
|
'predictions', type_='unique',
|
|
)
|
|
# P0-03: partial unique on input_hash(非空时唯一)
|
|
op.create_index(
|
|
'ix_predictions_input_hash_unique', 'predictions', ['input_hash'], unique=True,
|
|
postgresql_where=sa.text('input_hash IS NOT NULL'),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index('ix_predictions_input_hash_unique', table_name='predictions')
|
|
op.create_unique_constraint(
|
|
'uq_predictions_match_provider_model_mode_run_type',
|
|
'predictions',
|
|
['match_id', 'provider', 'model', 'mode', 'run_type'],
|
|
)
|