Skip to content

路由观测

agent 直连 BIRD 控制 socket 采集的路由全表观测(约 5 min 一份),与 reconcile 健康完全分开。模型在 app/db/models/routing.py

四张表是一套:

角色实测
node_routingL2摘要与预聚合10 行 / 344 kB
node_route_entriesL2逐路由明细178 303 行 / 144 MB
node_route_prefix_hashesL2差分写的门控基准10 行 / 1288 kB
node_routing_eventsL2规模与 churn 时间线37 265 行 / 7488 kB

这是全库最大的子系统(合计约 153 MB,占控制库 38%),其写路径也是全库约束最严的一条:整表内容哈希与 per-prefix 差分双重门控,机制见写入路径,实测效果是每节点每轮从约 17 800 行降到约 1 500 行。

读侧的聚合结果有一层 L5 缓存(routing:*,TTL 30 秒),见 KV 与缓存键索引

明细数量远多于前缀数量:实测每节点 2 496 个唯一前缀对应 1.78 万行明细,即同一前缀存在来自不同对端的多条路径,平均约 7 条。该比例是理解差分写为何无法降至「几十行」量级的前提。


node_routing

每节点最新路由全表加预聚合,随上报 upsert。

类型约束 / 默认说明
node_idString(64)PK,FK→nodes.node_id(CASCADE)主键即外键
observationString(16)NOT NULL,默认 not-observed采集状态(observed / unavailable / not-observed
captured_atDateTime(tz)可空采集时刻
route_countIntegerNOT NULL,默认 0唯一前缀数(按最优路径去重)
route_count_v4 / route_count_v6IntegerNOT NULL,默认 0分族计数
rpki_valid / rpki_invalid / rpki_not_foundIntegerNOT NULL,默认 0RPKI 分布计数
aggregatesJSON可空预聚合:origins、prefix_lengths、as_path_lengths、peers、routes_hash
updated_atDateTime(tz)server_default now,带 onupdate更新时刻

aggregates.routes_hash 是差分写第一级门控用的整表内容哈希。


node_route_entries

逐路由明细,每路由一行。前缀检索走 SQL WHERE 加索引加 LIMIT

类型约束 / 默认说明
idIntegerPK,autoincrement主键
node_idString(64)FK→nodes.node_id(CASCADE),NOT NULL所属节点(不单独建索引,由复合索引最左前缀覆盖)
prefixString(64)NOT NULL路由前缀
is_v6BooleanNOT NULL,默认 False地址族
localBooleanNOT NULL,默认 False本地起源(static / direct / device),供 scope 过滤
primaryBooleanNOT NULL,默认 False是否最优路径
origin_asnInteger可空起源 AS
protocolString(128)可空协议来源
rpkiString(16)可空RPKI 状态
next_hopString(128)可空下一跳
as_path / communities / large_communitiesJSON可空路径与团体属性

复合索引(均以 node_id 打头):(node_id, is_v6)(node_id, local)(node_id, prefix)

注意:四个索引合计 64 MB,接近表体的一半,但仅一个承担实际检索——(node_id, prefix) 被使用 312 万次,自增主键仅 15 次,另两个在三位数量级。清理判断见索引清单


node_route_prefix_hashes

每节点 per-prefix 规范化哈希基准,是差分写的第二级门控。哈希算法为 FNV-1a64 over 定序数组 JSON(services/routing.py);基准缺失时自动退回一次无害的整表重写

类型约束 / 默认说明
node_idString(64)PK,FK→nodes.node_id(CASCADE)主键即外键
hashesJSONNOT NULL,默认 {}prefix → 规范化哈希 映射(整节点一行一份基准)
updated_atDateTime(tz)server_default now,带 onupdate更新时刻

10 行占 1288 kB:单行 JSON 装着该节点的全部前缀哈希(千级键),是全库单行最大的活跃列。

本表尚未纳入迁移链,缺表时路由明细退化为整表重写。见已知偏差


node_routing_events

append-only 路由表时间序列,每次全表快照一条并附 churn。按节点保留最近 17 280 条_HISTORY_KEEP)——采集约 300 s 一次即约 60 天,与 L3 存档同窗口。

类型约束 / 默认说明
idIntegerPK,autoincrement主键
node_idString(64)FK→nodes.node_id(CASCADE),NOT NULL,index所属节点
captured_atDateTime(tz)可空采集时刻
route_count / route_count_v4 / route_count_v6IntegerNOT NULL,默认 0表规模计数
rpki_valid / rpki_invalid / rpki_not_foundIntegerNOT NULL,默认 0RPKI 计数
announced / withdrawnIntegerNOT NULL,默认 0相对上次快照的 churn(前缀集合的增减,不含路径属性变化)
created_atDateTime(tz)server_default now,index写入时刻

本表虽是时间序列,却不在分区化目标之列:主键是自增 id,转换要动主键,而它只有约 3.7 万行。裁剪按条数而非时间,也不适用桶模型。