"""Seed the ``cmdb_cost_facts.raw`` retention cron. Revision ID: 584 Revises: 583 2026-09-01 存储核查:``cmdb_cost_facts`` 生产 1000 万行、逻辑 10.5 GB,是全库 (11.19 GB / 345 张表)的 **94%**。逐列实测 8 月那一个月:``raw`` 平均 311 字节/行 × 106 万行 = 0.33 GB/月,11 个月约 3.6 GB。 ``raw`` 是厂商账单行的原样快照,**全仓没有任何读者** —— 唯一引用是 ``ingest_cost_facts`` 的那次写入(2026-09-01 grep 确认)。它的价值只有事后 人工排查「这个数为什么是这样」,而任何厂商的重新计价窗口最宽也就 ~45 天。 本迁移只建 cron 行,不动数据:清理由 ``cmdb.cost_facts_raw_purge`` 按天分批 执行(见 ``cost_service.purge_cost_fact_raw``)。**没有 DDL** —— 这张表上 任何 ALTER 都要在生产跑几十秒,而这里一次都不需要。 保留期读 ``platform_settings['cost_facts.raw_retention_days']``(默认 90, 下限 45),admin 可改、下一跳生效。 """ from __future__ import annotations import json import uuid import sqlalchemy as sa from alembic import op revision = "584" down_revision = "583" branch_labels = None depends_on = None _JOB = "cmdb.cost_facts_raw_purge" def upgrade() -> None: bind = op.get_bind() exists = bind.execute( sa.text("SELECT 1 FROM scheduled_jobs WHERE name = :name LIMIT 1"), {"name": _JOB}, ).scalar() if exists: return bind.execute( sa.text( """ INSERT INTO scheduled_jobs (id, name, display_name, description, handler_key, trigger_type, interval_seconds, cron_expr, timezone, enabled, config, run_count, failure_count, created_at, updated_at) VALUES (:id, :name, :display_name, :description, :name, 'cron', NULL, '35 3 * * *', 'Asia/Shanghai', 1, :config, 0, 0, UTC_TIMESTAMP(), UTC_TIMESTAMP()) """ ), { "id": str(uuid.uuid4()), "name": _JOB, "display_name": "云成本明细 raw 保留期清理", "description": ( "每日凌晨把超出保留期的 cmdb_cost_facts.raw 置空;" "只清 raw 一列,不删行、不动金额与维度列。" ), # max_days_per_run 30:首轮补历史时每晚推进 30 天,~11 个月的 # 积压约 10 个晚上走完,之后稳态每晚只推进 1 天(一天的索引 # 区间 ~3 万行)。一次性全推会把一个事务/一轮 cron 拉得太长。 "config": json.dumps({ "batch_size": 2000, "max_days_per_run": 30, }), }, ) def downgrade() -> None: op.get_bind().execute( sa.text("DELETE FROM scheduled_jobs WHERE name = :name"), {"name": _JOB}, )