Files
ai-ops/tech/migrations/000001_init_schema.up.sql
2026-05-12 17:48:22 +08:00

181 lines
7.2 KiB
PL/PgSQL
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.
-- AI-Ops 初始化 schema
-- 表前缀 ai_ops_避免与桥项目表名冲突
-- 1. 告警规则
CREATE TABLE IF NOT EXISTS ai_ops_rules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(128) NOT NULL,
metric_source VARCHAR(64) NOT NULL,
metric_name VARCHAR(128) NOT NULL,
threshold_type VARCHAR(16) NOT NULL CHECK (threshold_type IN ('>', '<', '=', 'regex')),
threshold_value TEXT NOT NULL,
duration_min INT NOT NULL DEFAULT 1 CHECK (duration_min >= 1),
level VARCHAR(8) NOT NULL CHECK (level IN ('P0', 'P1', 'P2', 'P3')),
channel_ids UUID[] NOT NULL DEFAULT '{}',
healing_action VARCHAR(32) DEFAULT NULL,
healing_config JSONB DEFAULT NULL,
is_sandboxed BOOLEAN NOT NULL DEFAULT FALSE,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_by VARCHAR(64) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
version INT NOT NULL DEFAULT 1,
CONSTRAINT uq_rules_name UNIQUE (name)
);
CREATE INDEX IF NOT EXISTS idx_rules_enabled ON ai_ops_rules(enabled);
-- 2. 告警事件
CREATE TABLE IF NOT EXISTS ai_ops_alerts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
rule_id UUID NOT NULL REFERENCES ai_ops_rules(id) ON DELETE CASCADE,
level VARCHAR(8) NOT NULL,
resource_type VARCHAR(64) NOT NULL,
resource_id VARCHAR(128) NOT NULL,
current_value TEXT NOT NULL,
threshold_value TEXT NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'triggered'
CHECK (status IN ('triggered', 'notified', 'healing', 'resolved', 'escalated', 'acknowledged')),
is_aggregated BOOLEAN NOT NULL DEFAULT FALSE,
aggregated_count INT DEFAULT 0,
parent_alert_id UUID NULL REFERENCES ai_ops_alerts(id) ON DELETE SET NULL,
started_at TIMESTAMPTZ NOT NULL,
resolved_at TIMESTAMPTZ NULL,
acknowledged_by VARCHAR(64) NULL,
acknowledged_at TIMESTAMPTZ NULL
);
CREATE INDEX IF NOT EXISTS idx_alerts_status ON ai_ops_alerts(status);
CREATE INDEX IF NOT EXISTS idx_alerts_started_at ON ai_ops_alerts(started_at DESC);
CREATE INDEX IF NOT EXISTS idx_alerts_resource ON ai_ops_alerts(resource_type, resource_id);
-- 3. 自愈执行记录
CREATE TABLE IF NOT EXISTS ai_ops_healings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
alert_id UUID NOT NULL REFERENCES ai_ops_alerts(id) ON DELETE CASCADE,
action_type VARCHAR(32) NOT NULL
CHECK (action_type IN ('switch_route', 'throttle', 'restart_instance', 'invoke_script', 'isolate_node')),
config JSONB NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'succeeded', 'failed', 'rolled_back')),
dry_run BOOLEAN NOT NULL DEFAULT FALSE,
result_detail JSONB NULL,
error_code VARCHAR(16) NULL,
started_at TIMESTAMPTZ NOT NULL,
completed_at TIMESTAMPTZ NULL
);
CREATE INDEX IF NOT EXISTS idx_healings_alert ON ai_ops_healings(alert_id);
-- 4. 通知渠道
CREATE TABLE IF NOT EXISTS ai_ops_channels (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(128) NOT NULL,
channel_type VARCHAR(32) NOT NULL
CHECK (channel_type IN ('webhook', 'email', 'feishu', 'wechat', 'sms')),
config JSONB NOT NULL,
priority INT NOT NULL DEFAULT 1,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 5. 审计日志append-only禁止更新和删除
CREATE TABLE IF NOT EXISTS ai_ops_audits (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id VARCHAR(64) NOT NULL,
object_type VARCHAR(64) NOT NULL,
object_id VARCHAR(128) NOT NULL,
action VARCHAR(32) NOT NULL
CHECK (action IN ('create', 'update', 'delete', 'rollback')),
before_state JSONB NULL,
after_state JSONB NULL,
request_id VARCHAR(64) NOT NULL,
result_code VARCHAR(16) NOT NULL,
source_ip VARCHAR(45) NOT NULL,
actor_id VARCHAR(64) NOT NULL,
risk_level VARCHAR(8) NOT NULL DEFAULT 'normal'
CHECK (risk_level IN ('normal', 'high', 'critical')),
parent_audit_id UUID NULL REFERENCES ai_ops_audits(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_audits_tenant_created ON ai_ops_audits(tenant_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_audits_object ON ai_ops_audits(object_type, object_id);
CREATE INDEX IF NOT EXISTS idx_audits_actor ON ai_ops_audits(actor_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_audits_request ON ai_ops_audits(request_id);
-- 审计日志防篡改触发器(仅允许插入,禁止更新和删除)
CREATE OR REPLACE FUNCTION ai_ops_audit_readonly()
RETURNS TRIGGER AS $$
BEGIN
RAISE EXCEPTION 'ai_ops_audits is append-only. Updates and deletes are not allowed.';
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_ai_ops_audits_no_update ON ai_ops_audits;
CREATE TRIGGER trg_ai_ops_audits_no_update
BEFORE UPDATE ON ai_ops_audits
FOR EACH ROW
EXECUTE FUNCTION ai_ops_audit_readonly();
DROP TRIGGER IF EXISTS trg_ai_ops_audits_no_delete ON ai_ops_audits;
CREATE TRIGGER trg_ai_ops_audits_no_delete
BEFORE DELETE ON ai_ops_audits
FOR EACH ROW
EXECUTE FUNCTION ai_ops_audit_readonly();
-- 6. 时序指标缓存(降级方案,主存储推荐 Prometheus / VictoriaMetrics
CREATE TABLE IF NOT EXISTS ai_ops_metrics (
id BIGSERIAL PRIMARY KEY,
metric_name VARCHAR(128) NOT NULL,
labels JSONB NOT NULL DEFAULT '{}',
value DOUBLE PRECISION NOT NULL,
recorded_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_metrics_name_time ON ai_ops_metrics(metric_name, recorded_at DESC);
-- 分区表(按天分区,自动清理 > 7 天的分区)
CREATE TABLE IF NOT EXISTS ai_ops_metrics_p (
id BIGSERIAL NOT NULL,
metric_name VARCHAR(128) NOT NULL,
labels JSONB NOT NULL DEFAULT '{}',
value DOUBLE PRECISION NOT NULL,
recorded_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (id, recorded_at)
) PARTITION BY RANGE (recorded_at);
-- 创建当前日分区
CREATE TABLE IF NOT EXISTS ai_ops_metrics_p_default PARTITION OF ai_ops_metrics_p
DEFAULT;
-- 启用 pg_partman 扩展后可自动管理分区(建议生产环境使用)
-- SELECT partman.create_parent('public.ai_ops_metrics_p', 'recorded_at', 'native', 'daily');
-- 7. 配置版本管理(支持审计回滚)
CREATE TABLE IF NOT EXISTS ai_ops_configs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
object_type VARCHAR(64) NOT NULL,
object_id VARCHAR(128) NOT NULL,
config_data JSONB NOT NULL,
version INT NOT NULL DEFAULT 1,
created_by VARCHAR(64) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_configs_object_version UNIQUE (object_type, object_id, version)
);
CREATE INDEX IF NOT EXISTS idx_configs_object ON ai_ops_configs(object_type, object_id, created_at DESC);
-- 8. 状态快照(自愈回滚用)
CREATE TABLE IF NOT EXISTS ai_ops_snapshots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
healing_id UUID NOT NULL REFERENCES ai_ops_healings(id) ON DELETE CASCADE,
snapshot_type VARCHAR(32) NOT NULL
CHECK (snapshot_type IN ('route', 'rate_limit', 'instance', 'script', 'node')),
before_state JSONB NOT NULL,
after_state JSONB NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_snapshots_healing ON ai_ops_snapshots(healing_id);