Files
sub2api/backend/migrations/126_add_channel_monitor_aggregation.sql
erio 8cf83c984e feat(channel-monitor): aggregate history to daily rollups + soft delete
明细只保留 1 天,超过 1 天聚合到新表 channel_monitor_daily_rollups(按
monitor_id/model/bucket_date 维度),聚合保留 30 天。两张表都用 SoftDeleteMixin
软删除(DELETE 自动改为 UPDATE deleted_at = NOW())。

聚合 + 清理任务由 OpsCleanupService 的 cron 统一调度,与运维监控的清理共享
schedule(默认 0 2 * * *)和 leader lock。ChannelMonitorRunner 的 cleanupLoop
被移除,只保留 dueCheckLoop。

读取路径 ComputeAvailability* 改为 UNION 明细(今天 deleted_at IS NULL)+
聚合(过去 windowDays 天 deleted_at IS NULL),SUM(ok)/SUM(total) 自然加权
计算可用率,AVG latency 用 SUM(sum_latency_ms)/SUM(count_latency)。

watermark 表 channel_monitor_aggregation_watermark 单行(id=1),记录
last_aggregated_date,重启后从该日期 +1 继续聚合,首次为 nil 则从
today - 30d 开始回填,单次最多 35 天上限避免长事务。

raw SQL 的 ListLatestPerModel / ListLatestForMonitorIDs / ListRecentHistoryForMonitors
都补上 deleted_at IS NULL 过滤(SoftDeleteMixin interceptor 只对 ent query 生效)。

bump version to 0.1.114.28

GroupBadge 在 MonitorKeyPickerDialog 中复用平台主题色 + 倍率/专属倍率
(顺手优化)。
2026-04-21 10:10:56 +08:00

61 lines
3.1 KiB
SQL
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.

-- Migration: 126_add_channel_monitor_aggregation
-- 渠道监控日聚合:把 channel_monitor_histories 的明细按天聚合,明细只保留 1 天,
-- 聚合保留 30 天。明细和聚合表都用软删除deleted_at由 ops cleanup 任务每天
-- 凌晨随运维监控清理一起跑(共享 cron
--
-- 设计要点:
-- - channel_monitor_histories 加 deleted_at 软删除字段SoftDeleteMixin 全局
-- Hook 会把 DELETE 自动改写成 UPDATE deleted_at = NOW())。
-- - channel_monitor_daily_rollups 按 (monitor_id, model, bucket_date) 唯一,
-- 用 ON CONFLICT DO UPDATE 实现幂等回填,状态分布和延迟分子分母都保留,
-- 方便后续按窗口任意求加权可用率和均值。
-- - watermark 表只有一行id=1记录最近一次聚合到达的日期避免重启后重复
-- 扫全表。
-- - rollup 上 (bucket_date) 索引服务清理任务的 DELETE WHERE bucket_date < cutoff。
-- 1) 给历史明细表加软删除字段
ALTER TABLE channel_monitor_histories
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
CREATE INDEX IF NOT EXISTS idx_channel_monitor_histories_deleted_at
ON channel_monitor_histories (deleted_at);
-- 2) 创建日聚合表
CREATE TABLE IF NOT EXISTS channel_monitor_daily_rollups (
id BIGSERIAL PRIMARY KEY,
monitor_id BIGINT NOT NULL REFERENCES channel_monitors(id) ON DELETE CASCADE,
model VARCHAR(200) NOT NULL,
bucket_date DATE NOT NULL,
total_checks INT NOT NULL DEFAULT 0,
ok_count INT NOT NULL DEFAULT 0,
operational_count INT NOT NULL DEFAULT 0,
degraded_count INT NOT NULL DEFAULT 0,
failed_count INT NOT NULL DEFAULT 0,
error_count INT NOT NULL DEFAULT 0,
sum_latency_ms BIGINT NOT NULL DEFAULT 0,
count_latency INT NOT NULL DEFAULT 0,
sum_ping_latency_ms BIGINT NOT NULL DEFAULT 0,
count_ping_latency INT NOT NULL DEFAULT 0,
computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_monitor_daily_rollups_unique
ON channel_monitor_daily_rollups (monitor_id, model, bucket_date);
CREATE INDEX IF NOT EXISTS idx_channel_monitor_daily_rollups_bucket
ON channel_monitor_daily_rollups (bucket_date);
CREATE INDEX IF NOT EXISTS idx_channel_monitor_daily_rollups_deleted_at
ON channel_monitor_daily_rollups (deleted_at);
-- 3) 创建 watermark 表单行id=1
CREATE TABLE IF NOT EXISTS channel_monitor_aggregation_watermark (
id INT PRIMARY KEY DEFAULT 1,
last_aggregated_date DATE,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT channel_monitor_aggregation_watermark_singleton CHECK (id = 1)
);
INSERT INTO channel_monitor_aggregation_watermark (id, last_aggregated_date, updated_at)
VALUES (1, NULL, NOW())
ON CONFLICT (id) DO NOTHING;