VPS-78: CSG day 费用口径统一 + 断档自愈 (v5) + v3/v5 迁移脚本入库

- compose/pgdb/csg-snapshot-v3.sql: 补提交(此前只存在于工作区,未入 git)
- compose/pgdb/csg-snapshot-v5.sql: 新增
  · csg_ladder_cost_raw() 无舍入阶梯费用助手
  · csg_daily_snapshot() v5: day 费用改累积边际差分 + round(...,2);
    p_month 上移 + 跨月守卫;日期与用量同源 latest_day_kwh
  · 一次性归一历史 day cost(UPDATE 3 行,总差 -0.01)
  · csg_backfill_missing_days() + job 1011(insert-only 断档自愈)
- runbooks/pgdb-health.md: 新增 check 9 CSG 归档新鲜度
- hosts/pgdb.md: v5 事实 + Known issues 2026-09-22
This commit is contained in:
windyboy
2026-09-22 08:45:45 +08:00
parent 2c5d5e3d45
commit 906fb4a759
4 changed files with 571 additions and 4 deletions
+48
View File
@@ -43,6 +43,8 @@ From the agent sandbox, always use `-F /dev/null` (system ssh config is unreadab
- Latest daily backup older than today, not custom format, or `pg_restore -l` fails.
- Disk usage near full on `/srv/pgdata` or `/`.
- New `ERROR`/`FATAL` lines in the timescaledb log or backup failures in the pg-backup log.
- CSG 归档新鲜度断言失败(见 check 9):`csg_history` 最新 day 行落后超过 2 天、
`period` 上出现缺口、或任一 CSG job 非 `Success` / `total_failures > 0`
## Pass criteria
@@ -54,6 +56,9 @@ From the agent sandbox, always use `-F /dev/null` (system ssh config is unreadab
- `daily/*-latest.dump` symlinks point to today's dumps; `file -L` reports `PostgreSQL custom database dump`.
- `/srv/pgdata` (`/dev/sdb1`, 32G) and `/` not near full; fstab mounts `/srv/pgdata` by `UUID=c9e12e79-1f66-404c-ab7f-b8809be81d86` with `defaults,noatime`.
- timescaledb log: no new `ERROR`/`FATAL`; pg-backup log: recent successful backup.
- `scribe.csg_history`:最新 day 行 `period >= current_date - 2``period` 上无缺口;
`csg_daily_snapshot`1010)与 `csg_backfill_missing_days`1011)均 `Success``total_failures = 0`
各月 `|sum(day cost) month cost| ≤ 0.05`(逐日舍入的累积,非缺陷)。
## Checks
@@ -179,3 +184,46 @@ ssh -F /dev/null -o BatchMode=yes windy@192.168.55.15 'cd /opt/database && docke
- pg-backup: recent successful run (`SQL backup created successfully` for each database, no restore/cleanup errors).
**Stop** if repeated `ERROR`/`FATAL` appear or a backup run failed.
### 9. CSG 长期归档新鲜度(`scribe.csg_history`
> 为什么必须单独看这张表:它承载南方电网的**永久**逐日/逐月归档,但写入它的
> job 曾经 **294 次运行 0 成功**、静默造成 23 天缺口而无人发现。更隐蔽的是:
> **上游停更时 job 会反复 upsert 同一行、`last_run_status` 仍是 `Success`、
> 日期上也不会出现"缺口"** —— 只有本节的"最新日期"断言能发现它。
```bash
# a) 最新 day 行日期(期望 >= 今天 - 2;CSG 数据本身滞后一天)
ssh -F /dev/null -o BatchMode=yes windy@192.168.55.15 'cd /opt/database && docker compose exec -T timescaledb psql -U postgres -d scribe -Atc "select max(period) as last_day, current_date - 2 as min_expected from csg_history where kind='\''day'\'';"'
# b) period 上是否有缺口(期望 0 行)
ssh -F /dev/null -o BatchMode=yes windy@192.168.55.15 'cd /opt/database && docker compose exec -T timescaledb psql -U postgres -d scribe -Atc "select period from generate_series('\''2026-07-01'\''::date, (select max(period) from csg_history where kind='\''day'\''), '\''1 day'\'') period except select period from csg_history where kind='\''day'\'';"'
# c) 两个 CSG job 的状态(期望均为 Success / 0 failures
ssh -F /dev/null -o BatchMode=yes windy@192.168.55.15 'cd /opt/database && docker compose exec -T timescaledb psql -U postgres -d scribe -Atc "select j.job_id, j.proc_name, s.last_run_status, s.total_runs, s.total_failures from timescaledb_information.jobs j join timescaledb_information.job_stats s on s.job_id = j.job_id where j.proc_name like '\''csg%'\'' order by j.job_id;"'
# d) 日费用与月费用的口径一致性(期望 |diff| <= 0.05
ssh -F /dev/null -o BatchMode=yes windy@192.168.55.15 'cd /opt/database && docker compose exec -T timescaledb psql -U postgres -d scribe -Atc "select m.period, m.usage_kwh, m.cost, round(d.s,2) as day_sum, round(d.s - m.cost,2) as diff from csg_history m left join (select date_trunc('\''month'\'',period)::date as mon, sum(cost) as s from csg_history where kind='\''day'\'' group by 1) d on d.mon = m.period where m.kind='\''month'\'' and m.period >= '\''2026-07-01'\'' order by m.period;"'
```
**Expected**
- a) `last_day >= current_date - 2`
- b) 0 行。
- c) `csg_daily_snapshot`job 101014:30 UTC = 22:30 CST)与
`csg_backfill_missing_days`job 101115:10 UTC = 23:10 CST)均 `Success``total_failures = 0`
- d) 每个月 `|diff| <= 0.05` 元。这是**逐日四舍五入到 2 位的累积**,不是缺陷;
月行(由 `csg_ladder_cost` 单次舍入)才是权威值。
**Stop** if
- `last_day` 落后超过 2 天 → 上游停更或 job 未运行。先看 c) 的 `last_run_started_at`
`ha core logs` 里的 `china_southern_power_grid_stat` 错误,再决定是否需要
重新加载集成 config entry(见 [home-assistant-maintenance](home-assistant-maintenance.md))。
- b) 出现缺口 → 等 `csg_backfill_missing_days` 下一轮(或手动 `CALL run_job(1011);`
后复查;仍缺则查 `timescaledb_information.job_errors`
- c) 任一 job 非 `Success``total_failures > 0` → 查 `job_errors``err_message`
历史上 job 1008 的失败原因是**函数签名与 TimescaleDB 自定义 job 约定不符**
(必须是 `(job_id integer, config jsonb)`),修复记录见
`compose/pgdb/csg-snapshot-v3.sql` / `-v5.sql`
- d) 某个 `|diff| > 0.05` → 说明有新写入路径绕过了 v5 的边际差分口径,停止并复核。