feat: 新增设备 API 拉取采集及配套处理能力

- 接入 NDRServer 告警和流量日志,支持凭据与任务管理
- 支持分页拉取、窗口拆分、调度优先级及 WAL 持久化后推进断点
- 增加迟到告警规则重放,优化多条泛化规则选择和事件时间处理
- 补充回归测试、数据库升级脚本及部署文档
This commit is contained in:
王绍宏
2026-09-06 16:46:21 +08:00
parent b61ec2e37c
commit 00c0a1fb7f
141 changed files with 11318 additions and 467 deletions
@@ -0,0 +1,35 @@
-- =============================================================================
-- 脚本:01-device-api-pull-precheck.sql
-- 用途:安装设备 API 拉取基础表之前,检查现有表结构是否满足升级条件。
-- 性质:只读脚本,不会修改表结构和业务数据,可以重复执行。
-- 执行时机:执行 02-device-api-pull-upgrade.sql 之前。
--
-- 重点检查:
-- 1. device_collect_task.method 当前使用了哪些值,避免新增 API 类型与旧值冲突;
-- 2. device_collect_task.id 和 device_device.id 是否都有主键;
-- 3. 两个主键字段的数据类型是否分别为 INTEGER/INT 和 BIGINT;
-- 4. 四张 DEVICE_COLLECT_API_* 表是否已经存在。
--
-- 判断方法:
-- - 任一 SQL 报错时,不要执行 02,先确认当前用户和 Schema 是否正确;
-- - 第四组查询返回 0 行,表示四张 API 拉取表尚未创建;
-- - 第四组查询有返回,表示可能执行过升级,应先运行 03 验证,不要直接重跑 02。
-- =============================================================================
-- 第 1 组:查看采集任务 method 的现有取值及任务数量。
SELECT method, COUNT(*) AS task_count FROM device_collect_task GROUP BY method ORDER BY method;
-- 第 2 组:确认两张被引用的旧表都有主键,每张表应各返回一条统计记录。
SELECT table_name, COUNT(*) AS pk_count FROM user_constraints
WHERE LOWER(table_name) IN ('device_collect_task','device_device') AND constraint_type='P'
GROUP BY table_name ORDER BY table_name;
-- 第 3 组:核对外键关联字段的数据类型、长度和是否允许为空。
SELECT table_name,column_name,data_type,data_length,data_precision,data_scale,nullable
FROM user_tab_columns
WHERE LOWER(table_name) IN ('device_collect_task','device_device') AND LOWER(column_name)='id'
ORDER BY table_name;
-- 第 4 组:检查目标表是否已存在。首次升级时应返回 0 行。
SELECT table_name FROM user_tables WHERE LOWER(table_name) IN
('device_collect_api_credential','device_collect_api_config','device_collect_api_checkpoint','device_collect_api_run');
@@ -0,0 +1,159 @@
-- =============================================================================
-- 脚本:02-device-api-pull-upgrade.sql
-- 用途:创建设备 API 拉取需要的四张基础表、约束和索引。
-- 性质:数据库结构变更脚本,会创建对象,不是只读脚本。
-- 执行时机:01 预检通过后,部署新版 syslog-serve 之前。
--
-- 前置条件:
-- 1. 已完成数据库备份;
-- 2. API 拉取任务和旧版服务已经停止;
-- 3. 已确认 device_collect_task.id 与 device_device.id 的真实类型;
-- 4. 四张 DEVICE_COLLECT_API_* 表均不存在。
--
-- 注意:本脚本不是重复执行脚本。执行成功后不要再次执行。
-- 如果中途报错,应保留现场并执行 03 验证实际创建情况,不要直接重跑。
-- =============================================================================
-- 凭据表:保存设备、适配器和 AppKey/AppSecret 的对应关系。
-- 第一阶段为明文保存,因此禁止把 app_secret 输出到日志或普通查询结果中。
CREATE TABLE device_collect_api_credential (
credential_ref VARCHAR2(128) NOT NULL,
device_id BIGINT NOT NULL,
adapter_type VARCHAR2(64) NOT NULL,
credential_version VARCHAR2(64) NOT NULL,
app_key VARCHAR2(512) NOT NULL,
app_secret VARCHAR2(2048) NOT NULL,
config_version BIGINT DEFAULT 1 NOT NULL,
created_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
updated_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
CONSTRAINT pk_dc_api_credential PRIMARY KEY(credential_ref),
CONSTRAINT fk_dc_api_credential_device FOREIGN KEY(device_id) REFERENCES device_device(id),
CONSTRAINT ck_dc_api_credential_ver CHECK(config_version >= 1),
CONSTRAINT uk_dc_api_credential_device UNIQUE(credential_ref, device_id, adapter_type)
);
-- 任务配置表:每个 API 拉取任务一条记录。
-- collect_task_id 关联现有 device_collect_task.id;任务默认关闭,验证通过后再开启。
CREATE TABLE device_collect_api_config (
collect_task_id INTEGER NOT NULL,
device_id BIGINT NOT NULL,
adapter_type VARCHAR2(64) NOT NULL,
adapter_version INTEGER DEFAULT 1 NOT NULL,
base_url VARCHAR2(1024) NOT NULL,
stream_key VARCHAR2(64) NOT NULL,
credential_ref VARCHAR2(128) NOT NULL,
enabled SMALLINT DEFAULT 0 NOT NULL,
next_run_at TIMESTAMP,
poll_interval_seconds INTEGER DEFAULT 10 NOT NULL,
page_size INTEGER DEFAULT 100 NOT NULL,
max_pages_per_run INTEGER DEFAULT 1000 NOT NULL,
connect_timeout_ms INTEGER DEFAULT 5000 NOT NULL,
request_timeout_ms INTEGER DEFAULT 30000 NOT NULL,
window_size_seconds INTEGER DEFAULT 60 NOT NULL,
safety_delay_seconds INTEGER DEFAULT 120 NOT NULL,
lookback_seconds INTEGER DEFAULT 300 NOT NULL,
max_response_bytes BIGINT DEFAULT 16777216 NOT NULL,
initial_start_time TIMESTAMP NOT NULL,
extra_config_json CLOB NOT NULL,
config_version BIGINT DEFAULT 1 NOT NULL,
created_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
updated_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
CONSTRAINT pk_dc_api_cfg PRIMARY KEY(collect_task_id),
CONSTRAINT fk_dc_api_cfg_task FOREIGN KEY(collect_task_id) REFERENCES device_collect_task(id),
CONSTRAINT fk_dc_api_cfg_credential FOREIGN KEY(credential_ref,device_id,adapter_type)
REFERENCES device_collect_api_credential(credential_ref,device_id,adapter_type),
CONSTRAINT uk_dc_api_cfg_stream UNIQUE(device_id,adapter_type,stream_key),
CONSTRAINT ck_dc_api_cfg_enabled CHECK(enabled IN (0,1)),
CONSTRAINT ck_dc_api_cfg_adapter_ver CHECK(adapter_version>=1),
CONSTRAINT ck_dc_api_cfg_poll CHECK(poll_interval_seconds BETWEEN 1 AND 86400),
CONSTRAINT ck_dc_api_cfg_page CHECK(page_size BETWEEN 1 AND 10000),
CONSTRAINT ck_dc_api_cfg_pages CHECK(max_pages_per_run BETWEEN 1 AND 100000),
CONSTRAINT ck_dc_api_cfg_conn_to CHECK(connect_timeout_ms BETWEEN 100 AND 120000),
CONSTRAINT ck_dc_api_cfg_req_to CHECK(request_timeout_ms BETWEEN 100 AND 600000),
CONSTRAINT ck_dc_api_cfg_window CHECK(window_size_seconds BETWEEN 1 AND 1209600),
CONSTRAINT ck_dc_api_cfg_delay CHECK(safety_delay_seconds BETWEEN 0 AND 604800),
CONSTRAINT ck_dc_api_cfg_lookback CHECK(lookback_seconds BETWEEN 0 AND 1209600),
CONSTRAINT ck_dc_api_cfg_resp CHECK(max_response_bytes BETWEEN 1024 AND 268435456),
CONSTRAINT ck_dc_api_cfg_ver CHECK(config_version>=1),
CONSTRAINT ck_dc_api_cfg_https CHECK(base_url LIKE 'https://%')
);
CREATE INDEX idx_dc_api_cfg_due ON device_collect_api_config(enabled,next_run_at,collect_task_id);
-- 进度表:记录任务已经安全写入 WAL 的时间位置,并保存跨实例租约信息。
-- committed_window_end 只有在当前窗口完整处理成功后才向后推进。
CREATE TABLE device_collect_api_checkpoint (
collect_task_id INTEGER NOT NULL,
checkpoint_type VARCHAR2(32) NOT NULL,
checkpoint_value CLOB NOT NULL,
committed_window_end TIMESTAMP,
adapter_type VARCHAR2(64) NOT NULL,
adapter_version INTEGER NOT NULL,
version BIGINT DEFAULT 1 NOT NULL,
lease_owner VARCHAR2(128),
lease_expires_at TIMESTAMP,
fencing_token BIGINT DEFAULT 0 NOT NULL,
last_event_time TIMESTAMP,
last_external_event_id VARCHAR2(512),
created_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
updated_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
CONSTRAINT pk_dc_api_cp PRIMARY KEY(collect_task_id),
CONSTRAINT fk_dc_api_cp_cfg FOREIGN KEY(collect_task_id) REFERENCES device_collect_api_config(collect_task_id),
CONSTRAINT ck_dc_api_cp_type CHECK(checkpoint_type IN ('TIME_WINDOW','TIME_ID','OPAQUE_CURSOR','EXTERNAL_ID')),
CONSTRAINT ck_dc_api_cp_adapter_ver CHECK(adapter_version>=1),
CONSTRAINT ck_dc_api_cp_ver CHECK(version>=1), CONSTRAINT ck_dc_api_cp_fence CHECK(fencing_token>=0),
CONSTRAINT ck_dc_api_cp_lease CHECK((lease_owner IS NULL AND lease_expires_at IS NULL)
OR (lease_owner IS NOT NULL AND lease_expires_at IS NOT NULL))
);
CREATE INDEX idx_dc_api_cp_lease ON device_collect_api_checkpoint(lease_expires_at,collect_task_id);
-- 运行记录表:每次调度、手工执行或验证都会留下运行结果,便于排查进度和错误。
-- 本表只保存脱敏错误摘要,不保存完整设备响应和认证信息。
CREATE TABLE device_collect_api_run (
run_id VARCHAR2(36) NOT NULL,
collect_task_id INTEGER NOT NULL,
device_id BIGINT NOT NULL,
adapter_type VARCHAR2(64) NOT NULL,
adapter_version INTEGER NOT NULL,
stream_key VARCHAR2(64) NOT NULL,
instance_id VARCHAR2(128) NOT NULL,
config_version BIGINT NOT NULL,
fencing_token BIGINT NOT NULL,
trigger_type VARCHAR2(16) NOT NULL,
status VARCHAR2(16) NOT NULL,
logical_window_start TIMESTAMP,
logical_window_end TIMESTAMP,
query_start TIMESTAMP,
query_end TIMESTAMP,
pages_fetched INTEGER DEFAULT 0 NOT NULL,
records_fetched BIGINT DEFAULT 0 NOT NULL,
records_durable BIGINT DEFAULT 0 NOT NULL,
records_quarantined BIGINT DEFAULT 0 NOT NULL,
duplicate_candidates BIGINT DEFAULT 0 NOT NULL,
last_http_status INTEGER,
error_code VARCHAR2(128),
error_summary VARCHAR2(2000),
scheduled_at TIMESTAMP,
started_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
finished_at TIMESTAMP,
created_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
CONSTRAINT pk_dc_api_run PRIMARY KEY(run_id),
CONSTRAINT fk_dc_api_run_cfg FOREIGN KEY(collect_task_id) REFERENCES device_collect_api_config(collect_task_id),
CONSTRAINT ck_dc_api_run_trigger CHECK(trigger_type IN ('SCHEDULED','MANUAL','VALIDATE')),
CONSTRAINT ck_dc_api_run_status CHECK(status IN ('RUNNING','SUCCESS','FAILED','SKIPPED')),
CONSTRAINT ck_dc_api_run_counts CHECK(pages_fetched>=0 AND records_fetched>=0 AND records_durable>=0
AND records_quarantined>=0 AND duplicate_candidates>=0),
CONSTRAINT ck_dc_api_run_fence CHECK(fencing_token>=0)
);
CREATE INDEX idx_dc_api_run_task_time ON device_collect_api_run(collect_task_id,started_at);
CREATE INDEX idx_dc_api_run_status ON device_collect_api_run(status,started_at);
-- 数据字典注释:方便 DBA 在数据库管理工具中直接查看各表用途。
COMMENT ON TABLE device_collect_api_config IS '设备 API 拉取任务非敏感配置,一任务一条';
COMMENT ON TABLE device_collect_api_credential IS '设备 API 厂商凭据;第一阶段 AppKey/AppSecret 明文保存,禁止日志和接口响应回显凭据正文';
COMMENT ON COLUMN device_collect_api_credential.credential_ref IS '任务引用的全局唯一凭据名称';
COMMENT ON COLUMN device_collect_api_credential.app_secret IS '第一阶段明文 AppSecret,后续迁移为密文字段';
COMMENT ON TABLE device_collect_api_checkpoint IS '设备 API 已安全进入 WAL 的业务水位及跨实例租约';
COMMENT ON TABLE device_collect_api_run IS '设备 API 每次调度、手工触发或验证的脱敏运行审计';
COMMENT ON COLUMN device_collect_api_run.error_summary IS '最长 2000 字符的脱敏摘要,禁止保存原始响应和认证信息';
-- 创建完成后,请立即执行 03-device-api-pull-verify.sql。
@@ -0,0 +1,57 @@
-- =============================================================================
-- 脚本:03-device-api-pull-verify.sql
-- 用途:确认设备 API 拉取基础表升级结果是否完整。
-- 性质:只读脚本,不会修改表结构和业务数据,可以重复执行。
-- 执行时机:02 执行完成后、启动新版 syslog-serve 之前。
--
-- 成功标准:
-- 1. 第 1 组返回四张表;
-- 2. 第 2 组所有约束的 STATUS 都是 ENABLED;
-- 3. 第 3 组所有索引的 STATUS 都是 VALID;
-- 4. 第 4 组能列出四张表的完整字段。
-- 任一对象缺失或状态异常时,不要启动 API 拉取任务。
-- =============================================================================
-- 第 1 组:确认四张基础表都已创建。
SELECT table_name
FROM user_tables
WHERE table_name IN (
'DEVICE_COLLECT_API_CREDENTIAL',
'DEVICE_COLLECT_API_CONFIG',
'DEVICE_COLLECT_API_CHECKPOINT',
'DEVICE_COLLECT_API_RUN'
)
ORDER BY table_name;
-- 第 2 组:确认主键、外键、唯一约束和检查约束均已启用。
SELECT table_name, constraint_name, constraint_type, status
FROM user_constraints
WHERE table_name IN (
'DEVICE_COLLECT_API_CREDENTIAL',
'DEVICE_COLLECT_API_CONFIG',
'DEVICE_COLLECT_API_CHECKPOINT',
'DEVICE_COLLECT_API_RUN'
)
ORDER BY table_name, constraint_name;
-- 第 3 组:确认四张表相关索引均有效。
SELECT table_name, index_name, uniqueness, status
FROM user_indexes
WHERE table_name IN (
'DEVICE_COLLECT_API_CREDENTIAL',
'DEVICE_COLLECT_API_CONFIG',
'DEVICE_COLLECT_API_CHECKPOINT',
'DEVICE_COLLECT_API_RUN'
)
ORDER BY table_name, index_name;
-- 第 4 组:核对字段名称、类型、长度和是否允许为空。
SELECT table_name, column_name, data_type, data_length, nullable
FROM user_tab_columns
WHERE table_name IN (
'DEVICE_COLLECT_API_CREDENTIAL',
'DEVICE_COLLECT_API_CONFIG',
'DEVICE_COLLECT_API_CHECKPOINT',
'DEVICE_COLLECT_API_RUN'
)
ORDER BY table_name, column_id;
@@ -0,0 +1,46 @@
-- =============================================================================
-- 脚本:04-fairness-late-replay-precheck.sql
-- 用途:升级公平调度和迟到告警重放功能之前,检查当前数据库结构。
-- 性质:只读脚本,不会修改表结构和业务数据,可以重复执行。
-- 执行时机:执行 05-fairness-late-replay-upgrade.sql 之前。
--
-- 首次升级的预期结果:
-- 1. 第 1 组返回 0 行,表示 SCHEDULER_PRIORITY 尚未添加;
-- 2. 第 2 组返回 0 行,表示两张重放表尚未创建;
-- 3. 第 3 组返回 ANALYSIS_CHECKPOINT,表示规则分析基础表存在;
-- 4. 第 4 组应返回 DEVICE_ID、LOG_TIME、CREATED_AT 三个字段;
-- 5. 第 5 组应能看到 NDR_ALERT 和 NDR_FLOW_LOG 任务。
--
-- 如果第 1 组或第 2 组已经有结果,说明可能升级过,不要直接重跑 05,
-- 应先执行 06-fairness-late-replay-verify.sql 判断结构是否完整。
-- =============================================================================
-- 第 1 组:检查任务配置表是否已经有调度优先级字段。
SELECT column_name
FROM user_tab_columns
WHERE table_name='DEVICE_COLLECT_API_CONFIG'
AND column_name='SCHEDULER_PRIORITY';
-- 第 2 组:检查迟到告警和规则重放表是否已经存在。
SELECT table_name
FROM user_tables
WHERE table_name IN ('ANALYSIS_LATE_ALARM_BUCKET','ANALYSIS_RULE_REPLAY_WINDOW')
ORDER BY table_name;
-- 第 3 组:确认现有规则分析进度表存在,应返回一行 ANALYSIS_CHECKPOINT。
SELECT table_name
FROM user_tables
WHERE table_name='ANALYSIS_CHECKPOINT';
-- 第 4 组:确认标准化告警表具备判断迟到数据所需的字段。
SELECT column_name,data_type,nullable
FROM user_tab_columns
WHERE table_name='SYSLOG_NORMAL_ALARM'
AND column_name IN ('DEVICE_ID','LOG_TIME','CREATED_AT')
ORDER BY column_name;
-- 第 5 组:查看现有 API 拉取任务类型及数量,为升级后的优先级赋值做准备。
SELECT stream_key,COUNT(*) AS task_count
FROM device_collect_api_config
GROUP BY stream_key
ORDER BY stream_key;
@@ -0,0 +1,142 @@
-- =============================================================================
-- 脚本:05-fairness-late-replay-upgrade.sql
-- 用途:增加 API 任务调度优先级,并创建迟到告警规则重放所需的两张表。
-- 性质:增量数据库结构变更脚本,会修改表结构和数据。
-- 执行时机:04 预检通过后,部署三个新版服务之前。
--
-- 前置条件:
-- 1. 已完成数据库备份;
-- 2. API 拉取任务已经暂停;
-- 3. syslog-serve、syslog-consumer、syslog-consumer-rule 旧版本已停止;
-- 4. SCHEDULER_PRIORITY 字段和两张 ANALYSIS_* 表尚不存在。
--
-- 注意:本脚本不是重复执行脚本。执行成功后不要再次执行。
-- 如果中途报错,应先执行 06 验证已经创建的对象,再决定如何处理。
-- =============================================================================
-- 第 1 步:增加任务调度优先级。
-- 数值越大,在到期时间相同的情况下越先执行;默认值 0 兼容旧任务。
ALTER TABLE device_collect_api_config
ADD scheduler_priority INTEGER DEFAULT 0 NOT NULL;
ALTER TABLE device_collect_api_config
ADD CONSTRAINT ck_dc_api_cfg_priority
CHECK(scheduler_priority BETWEEN -1000 AND 1000);
CREATE INDEX idx_dc_api_cfg_priority_due
ON device_collect_api_config(enabled,next_run_at,scheduler_priority,collect_task_id);
-- 第 2 步:设置现有 NDR 任务的默认优先级。
-- 同一 NDR 主机只有一个并发许可时,让低流量告警优先于高流量原始网络日志。
UPDATE device_collect_api_config
SET scheduler_priority=100,updated_at=SYSDATE
WHERE stream_key='NDR_ALERT';
UPDATE device_collect_api_config
SET scheduler_priority=10,updated_at=SYSDATE
WHERE stream_key='NDR_FLOW_LOG';
-- 第 3 步:创建迟到告警分钟桶表。
-- 标准化告警入库时,如果其事件分钟已经被实时分析处理过,就在此表登记补处理请求。
CREATE TABLE analysis_late_alarm_bucket (
id VARCHAR2(36) NOT NULL,
device_id BIGINT NOT NULL,
bucket_start TIMESTAMP NOT NULL,
bucket_end TIMESTAMP NOT NULL,
last_observed_at TIMESTAMP NOT NULL,
requested_generation BIGINT DEFAULT 1 NOT NULL,
claimed_generation BIGINT DEFAULT 0 NOT NULL,
processed_generation BIGINT DEFAULT 0 NOT NULL,
status VARCHAR2(16) DEFAULT 'PENDING' NOT NULL,
attempt_count INTEGER DEFAULT 0 NOT NULL,
fencing_token BIGINT DEFAULT 0 NOT NULL,
lease_owner VARCHAR2(128),
lease_expires_at TIMESTAMP,
last_error_code VARCHAR2(128),
last_error_summary VARCHAR2(2000),
created_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
updated_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
CONSTRAINT pk_analysis_late_alarm_bucket PRIMARY KEY(id),
CONSTRAINT uk_analysis_late_alarm_bucket UNIQUE(device_id,bucket_start),
CONSTRAINT ck_analysis_late_bucket_status
CHECK(status IN ('PENDING','RUNNING','COMPLETED','FAILED')),
CONSTRAINT ck_analysis_late_bucket_gen
CHECK(requested_generation>=claimed_generation
AND claimed_generation>=processed_generation
AND processed_generation>=0),
CONSTRAINT ck_analysis_late_bucket_count CHECK(attempt_count>=0 AND fencing_token>=0),
CONSTRAINT ck_analysis_late_bucket_window CHECK(bucket_end>bucket_start),
CONSTRAINT ck_analysis_late_bucket_lease
CHECK((lease_owner IS NULL AND lease_expires_at IS NULL)
OR (lease_owner IS NOT NULL AND lease_expires_at IS NOT NULL))
);
CREATE INDEX idx_analysis_late_bucket_due
ON analysis_late_alarm_bucket(status,lease_expires_at,bucket_start);
-- 第 4 步:创建规则重放窗口表。
-- 系统根据分钟桶和当时适用的规则生成重放窗口,处理结果与正常实时窗口分开记录。
CREATE TABLE analysis_rule_replay_window (
id VARCHAR2(36) NOT NULL,
source_bucket_id VARCHAR2(36) NOT NULL,
rule_id VARCHAR2(128) NOT NULL,
rule_version VARCHAR2(64) NOT NULL,
window_start TIMESTAMP NOT NULL,
window_end TIMESTAMP NOT NULL,
requested_generation BIGINT DEFAULT 1 NOT NULL,
claimed_generation BIGINT DEFAULT 0 NOT NULL,
processed_generation BIGINT DEFAULT 0 NOT NULL,
status VARCHAR2(16) DEFAULT 'PENDING' NOT NULL,
attempt_count INTEGER DEFAULT 0 NOT NULL,
input_count BIGINT DEFAULT 0 NOT NULL,
output_count BIGINT DEFAULT 0 NOT NULL,
fencing_token BIGINT DEFAULT 0 NOT NULL,
lease_owner VARCHAR2(128),
lease_expires_at TIMESTAMP,
next_retry_at TIMESTAMP,
last_error_code VARCHAR2(128),
last_error_summary VARCHAR2(2000),
started_at TIMESTAMP,
finished_at TIMESTAMP,
created_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
updated_at TIMESTAMP DEFAULT SYSDATE NOT NULL,
CONSTRAINT pk_analysis_rule_replay PRIMARY KEY(id),
CONSTRAINT fk_analysis_rule_replay_bucket FOREIGN KEY(source_bucket_id)
REFERENCES analysis_late_alarm_bucket(id),
CONSTRAINT uk_analysis_rule_replay
UNIQUE(rule_id,rule_version,window_start,window_end),
CONSTRAINT ck_analysis_rule_replay_status
CHECK(status IN ('PENDING','RUNNING','COMPLETED','FAILED','BLOCKED')),
CONSTRAINT ck_analysis_rule_replay_gen
CHECK(requested_generation>=claimed_generation
AND claimed_generation>=processed_generation
AND processed_generation>=0),
CONSTRAINT ck_analysis_rule_replay_count
CHECK(attempt_count>=0 AND input_count>=0 AND output_count>=0 AND fencing_token>=0),
CONSTRAINT ck_analysis_rule_replay_window CHECK(window_end>window_start),
CONSTRAINT ck_analysis_rule_replay_lease
CHECK((lease_owner IS NULL AND lease_expires_at IS NULL)
OR (lease_owner IS NOT NULL AND lease_expires_at IS NOT NULL))
);
CREATE INDEX idx_analysis_rule_replay_due
ON analysis_rule_replay_window(status,next_retry_at,lease_expires_at,window_start);
-- 第 5 步:写入数据字典注释,方便后续运维排查。
COMMENT ON COLUMN device_collect_api_config.scheduler_priority
IS '到期时间相同时的竞争优先级,数值越大越先调度;逾期更久的任务优先';
COMMENT ON TABLE analysis_late_alarm_bucket
IS '标准化告警同事务写入的事件分钟 outbox;代数防止运行期间新通知丢失';
COMMENT ON COLUMN analysis_late_alarm_bucket.last_observed_at
IS '本事件分钟最近一次事务内观察时间,仅供审计,不代表事务提交时间';
COMMENT ON TABLE analysis_rule_replay_window
IS '迟到告警影响的独立规则重放窗口;不修改正常 RULE checkpoint';
COMMENT ON COLUMN analysis_rule_replay_window.rule_version
IS '必须与正常 analysis_checkpoint 的规则语义版本完全一致';
COMMENT ON COLUMN analysis_rule_replay_window.processed_generation
IS '已完整重放的请求代数;小于 requested_generation 时必须再次执行';
-- 提交本脚本中的优先级更新。
COMMIT;
-- 执行完成后,请立即执行 06-fairness-late-replay-verify.sql。
@@ -0,0 +1,61 @@
-- =============================================================================
-- 脚本:06-fairness-late-replay-verify.sql
-- 用途:验证公平调度和迟到告警重放的数据库升级结果。
-- 性质:只读脚本,不会修改表结构和业务数据,可以重复执行。
-- 执行时机:05 执行完成后、启动三个新版服务之前。
--
-- 成功标准:
-- 1. 第 1 组包含 SCHEDULER_PRIORITY 字段和两张 ANALYSIS_* 表的字段;
-- 2. 第 2 组所有约束的 STATUS 都是 ENABLED;
-- 3. 第 3 组返回三个索引,STATUS 都是 VALID;
-- 4. 第 4 组中 NDR_ALERT 优先级为 100,NDR_FLOW_LOG 优先级为 10;
-- 5. 首次升级时,第 5、6 组可以返回 0 行,因为还没有产生迟到数据和重放任务。
-- =============================================================================
-- 第 1 组:检查新增字段和两张新表的字段结构。
SELECT table_name,column_name,data_type,nullable,data_default
FROM user_tab_columns
WHERE (table_name='DEVICE_COLLECT_API_CONFIG' AND column_name='SCHEDULER_PRIORITY')
OR table_name IN ('ANALYSIS_LATE_ALARM_BUCKET','ANALYSIS_RULE_REPLAY_WINDOW')
ORDER BY table_name,column_id;
-- 第 2 组:检查新增检查约束、主键、外键和唯一约束是否启用。
SELECT table_name,constraint_name,constraint_type,status
FROM user_constraints
WHERE table_name IN (
'DEVICE_COLLECT_API_CONFIG',
'ANALYSIS_LATE_ALARM_BUCKET',
'ANALYSIS_RULE_REPLAY_WINDOW'
)
AND (table_name<>'DEVICE_COLLECT_API_CONFIG'
OR constraint_name='CK_DC_API_CFG_PRIORITY')
ORDER BY table_name,constraint_name;
-- 第 3 组:检查三个新增索引是否有效。
SELECT table_name,index_name,uniqueness,status
FROM user_indexes
WHERE index_name IN (
'IDX_DC_API_CFG_PRIORITY_DUE',
'IDX_ANALYSIS_LATE_BUCKET_DUE',
'IDX_ANALYSIS_RULE_REPLAY_DUE'
)
ORDER BY index_name;
-- 第 4 组:核对现有 API 拉取任务的调度优先级和启停状态。
SELECT collect_task_id,stream_key,scheduler_priority,enabled,next_run_at
FROM device_collect_api_config
ORDER BY scheduler_priority DESC,collect_task_id;
-- 第 5 组:按状态统计迟到告警分钟桶。刚升级完成时返回 0 行属于正常情况。
SELECT status,COUNT(*) AS bucket_count
FROM analysis_late_alarm_bucket
GROUP BY status
ORDER BY status;
-- 第 6 组:按状态统计规则重放窗口及输入、输出数量。
-- 刚升级完成时返回 0 行属于正常情况;运行后可据此观察积压和处理结果。
SELECT status,COUNT(*) AS replay_count,
SUM(input_count) AS input_count,SUM(output_count) AS output_count
FROM analysis_rule_replay_window
GROUP BY status
ORDER BY status;
@@ -0,0 +1,61 @@
-- =============================================================================
-- 脚本:80-late-replay-backfill-template.sql
-- 用途:为升级前已经进入 SYSLOG_NORMAL_ALARM、但错过实时规则窗口的历史告警,
-- 补建迟到告警分钟桶,之后由新版规则服务进行重放。
-- 性质:可选的数据修复模板,会写入或更新 ANALYSIS_LATE_ALARM_BUCKET。
-- 执行时机:05、06 已成功执行且确实需要补处理历史告警时。
--
-- 重要:本文件包含占位符,不得原样执行。
-- 执行前必须完成:
-- 1. 将 DEVICE_ID 替换为一个明确的数字,例如 126;
-- 2. 将 START_TIME、END_TIME 替换为明确时间,采用左闭右开范围;
-- 3. 先执行下面 USING 括号中的 SELECT,核对设备、时间和分钟数量;
-- 4. 一次最多按一小时处理,大范围应分成多个小时依次执行;
-- 5. 确认没有选中其他设备的数据。
--
-- 示例范围:
-- log_time >= TIMESTAMP '2026-09-06 00:00:00'
-- log_time < TIMESTAMP '2026-09-06 01:00:00'
-- 表示只处理 00:00:00(含)到 01:00:00(不含)的数据。
--
-- 成功后,受影响分钟在 ANALYSIS_LATE_ALARM_BUCKET 中为 PENDING,或在原任务
-- 正在运行时保持 RUNNING 并增加 requested_generation。随后由规则服务处理。
-- =============================================================================
-- 按“设备 + 事件分钟”合并:已有分钟增加请求代数,新分钟插入一条待处理记录。
MERGE INTO analysis_late_alarm_bucket t
USING (
SELECT
device_id,
TO_TIMESTAMP(
TO_CHAR(log_time,'YYYY-MM-DD HH24:MI') || ':00',
'YYYY-MM-DD HH24:MI:SS'
) AS bucket_start
FROM syslog_normal_alarm
WHERE device_id = DEVICE_ID
AND log_time >= TIMESTAMP 'START_TIME'
AND log_time < TIMESTAMP 'END_TIME'
GROUP BY
device_id,
TO_TIMESTAMP(
TO_CHAR(log_time,'YYYY-MM-DD HH24:MI') || ':00',
'YYYY-MM-DD HH24:MI:SS'
)
) s
ON (t.device_id=s.device_id AND t.bucket_start=s.bucket_start)
WHEN MATCHED THEN UPDATE SET
t.requested_generation=t.requested_generation+1,
t.last_observed_at=SYSDATE,
t.status=CASE WHEN t.status='RUNNING' THEN 'RUNNING' ELSE 'PENDING' END,
t.updated_at=SYSDATE
WHEN NOT MATCHED THEN INSERT (
id,device_id,bucket_start,bucket_end,last_observed_at,
requested_generation,claimed_generation,processed_generation,
status,attempt_count,fencing_token,created_at,updated_at
) VALUES (
SYS_GUID(),s.device_id,s.bucket_start,s.bucket_start+(1.0/1440),SYSDATE,
1,0,0,'PENDING',0,0,SYSDATE,SYSDATE
);
-- 提交本次历史补数。
COMMIT;
@@ -0,0 +1,29 @@
-- =============================================================================
-- 脚本:90-device-api-pull-rollback-audit.sql
-- 用途:应用版本回退前,统计设备 API 拉取表中的数据和任务启停状态。
-- 性质:只读核验脚本,不会删除表或数据,可以重复执行。
--
-- 回退原则:
-- 1. 关闭全局 DEVICE_API_ENABLED,并暂停所有 API 拉取任务;
-- 2. 保留凭据、任务配置、checkpoint 和运行记录,便于恢复和审计;
-- 3. 不要为了回退旧应用而删除四张 DEVICE_COLLECT_API_* 表。
-- =============================================================================
-- 第 1 组:统计四张 API 拉取表的记录数量,确认现有数据规模。
SELECT 'DEVICE_COLLECT_API_CREDENTIAL' AS table_name, COUNT(*) AS row_count
FROM device_collect_api_credential
UNION ALL
SELECT 'DEVICE_COLLECT_API_CONFIG', COUNT(*)
FROM device_collect_api_config
UNION ALL
SELECT 'DEVICE_COLLECT_API_CHECKPOINT', COUNT(*)
FROM device_collect_api_checkpoint
UNION ALL
SELECT 'DEVICE_COLLECT_API_RUN', COUNT(*)
FROM device_collect_api_run;
-- 第 2 组:统计启用和停用的任务数量。回退前 enabled=1 应为 0 条。
SELECT enabled, COUNT(*) AS task_count
FROM device_collect_api_config
GROUP BY enabled
ORDER BY enabled;
@@ -0,0 +1,28 @@
-- =============================================================================
-- 脚本:91-fairness-late-replay-rollback-audit.sql
-- 用途:应用版本回退前,检查任务优先级、迟到告警分钟桶和规则重放状态。
-- 性质:只读核验脚本,不会删除表或数据,可以重复执行。
--
-- 回退原则:
-- 1. 先暂停全部 API 拉取任务,再停止新版服务;
-- 2. 保留 SCHEDULER_PRIORITY 字段和两张 ANALYSIS_* 表;
-- 3. 不要删除 PENDING、RUNNING 或 FAILED 记录,避免丢失待处理信息和审计记录;
-- 4. 旧版应用会忽略新增字段和新增表。
-- =============================================================================
-- 第 1 组:列出 API 拉取任务的启停状态和优先级。回退前 enabled 应全部为 0。
SELECT collect_task_id,stream_key,enabled,scheduler_priority
FROM device_collect_api_config
ORDER BY collect_task_id;
-- 第 2 组:统计迟到告警分钟桶状态,记录是否还有待处理或正在处理的任务。
SELECT status,COUNT(*) AS bucket_count
FROM analysis_late_alarm_bucket
GROUP BY status
ORDER BY status;
-- 第 3 组:统计规则重放窗口状态,记录是否还有待处理、失败或阻塞的任务。
SELECT status,COUNT(*) AS replay_count
FROM analysis_rule_replay_window
GROUP BY status
ORDER BY status;
@@ -0,0 +1,82 @@
# v2 数据库升级说明
本目录中的脚本供达梦数据库管理员手工执行。请使用 `syslog-serve` 实际连接的数据库用户执行,不要使用其他业务库或其他 Schema。
## 1. 脚本分组
### 第一组:设备 API 拉取基础表
首次安装设备 API 拉取功能时,依次执行:
1. `01-device-api-pull-precheck.sql`:检查旧表主键和新表是否已经存在,只读。
2. `02-device-api-pull-upgrade.sql`:创建凭据、任务配置、进度和运行记录表,会修改数据库。
3. `03-device-api-pull-verify.sql`:检查新表、约束、索引和字段,只读。
如果四张 `DEVICE_COLLECT_API_*` 表已经存在,不要再次执行 `02`。
### 第二组:公平调度和迟到告警重放
升级公平调度及迟到告警处理功能时,依次执行:
1. `04-fairness-late-replay-precheck.sql`:检查升级前结构,只读。
2. `05-fairness-late-replay-upgrade.sql`:增加调度优先级,并创建迟到告警处理表,会修改数据库。
3. `06-fairness-late-replay-verify.sql`:检查新增字段、表、约束和索引,只读。
如果 `SCHEDULER_PRIORITY` 字段和两张 `ANALYSIS_*` 表已经存在,不要再次执行 `05`。
### 辅助脚本
| 脚本 | 用途 | 是否修改数据 |
| --- | --- | --- |
| `80-late-replay-backfill-template.sql` | 为升级前已经入库的历史告警补建重放任务 | 是,必须先替换参数 |
| `90-device-api-pull-rollback-audit.sql` | 回退应用前检查 API 拉取数据 | 否 |
| `91-fairness-late-replay-rollback-audit.sql` | 回退应用前检查调度和重放状态 | 否 |
## 2. 执行前准备
执行修改数据库的脚本前,先完成以下事项:
1. 确认已经完成数据库备份。
2. 确认连接的是正确的测试环境或生产环境。
3. 确认当前用户可以创建表、索引、约束以及修改表结构。
4. 暂停所有 API 拉取任务,避免升级过程中还有任务写入。
5. 停止待升级的旧版本服务,避免新旧程序同时运行。
可以先执行下面的语句确认当前用户:
```sql
SELECT USER AS current_user;
```
## 3. 如何执行
可以使用达梦管理工具或现场常用的数据库客户端打开脚本执行。每个脚本应当单独执行,执行完成并核对结果后,再执行下一个脚本。
不要一次选择整个目录中的全部脚本执行,因为预检、升级、验证和历史补数的用途不同。
## 4. 如何判断执行成功
- 预检脚本:SQL 能正常返回,并且结果符合脚本开头写明的预期。
- 升级脚本:所有 DDL 和 DML 均执行成功,没有报错。
- 验证脚本:新增对象都能查到,约束状态为 `ENABLED`,索引状态为 `VALID`。
- 历史补数脚本:必须先查询并确认时间范围,然后才允许执行 `MERGE`。
如果出现“对象已经存在”“列已经存在”之类的错误,不要继续重复执行升级脚本。先运行对应的验证脚本,判断之前是否已经升级成功。
## 5. 推荐执行顺序
全新升级时:
```text
01(检查)→ 02(升级)→ 03(验证)
↓
04(检查)→ 05(升级)→ 06(验证)
```
已经安装过设备 API 拉取基础表,只升级本次新增功能时:
```text
04(检查)→ 05(升级)→ 06(验证)
```
`80`、`90` 和 `91` 不属于正常安装步骤,没有明确需要时不要执行。