Skip to content

数据与错误契约

本文细化总体设计的模型和结果语义。已实现类型及字段见源码生成参考,其余在线、事件和持久化类型随对应阶段实施;本页同时保留完整首版契约。Python 模型使用标准库 dataclass、Enum、ipaddress 和 datetime,字段说明在实现时进入 docstring。

通用表示

  • 公共记录采用冻结的数据类,构造参数以关键字为主。嵌套集合也不可变:序列用 tuple,集合用 frozenset,映射提供只读视图并切断调用方持有的可变底层对象。
  • ASN 是整数,范围为 0–4294967295,拒绝 bool、浮点数和隐式字符串转换。不同输入位置对 AS0 的限制见输入契约;JSON 生产者适配器负责解析其明确支持的 ASN 字符串。
  • Network 表示 IPv4Network | IPv6Network。公共便捷入口接受 CIDR 字符串,采用严格网络地址检查;不静默清除主机位,不接受 %eth0 等接口 zone。模型字段统一保存 Network。IPv4 映射 IPv6 前缀仍是 IPv6 授权,不转换为 IPv4;公共 JSON 和状态摘要使用固定的 ::ffff:a.b.c.d/length 文本,不能依赖当前解释器的 str(Network)。
  • 时间用带时区的 datetime,内部统一 UTC,拒绝 naive datetime。持续时间用有限非负秒数;超时和周期要求大于零。外部 JSON 时间用 RFC 3339 UTC 字符串,协议整数秒在适配边界转换。
  • 源 ID 是调用方指定的稳定非空字符串,不能包含凭据。它和经过规范化的来源身份不同:同一个 ID 指向不同端点或不同信任配置时必须使恢复匹配失败。
  • 不承诺 dataclass 的内存布局、pickle 格式或内部索引可序列化;跨版本交换使用版本化格式。

载荷与来源

类型 公共字段与含义
Vrp prefix: Network、max_length: int、asn: int;前缀长度 ≤ max_length ≤ 地址族位数
Aspa customer: int、providers: frozenset[int]、afi(Afi 或 None,默认 None);None 明确表示双族相同授权,显式 Afi 仅授权该族
RecordSupport source_id、expires_at、可选有效起点 valid_from、原始生成时间与上游来源标签;不将上游标签当成独立可信来源
VrpMatch vrp、supports: tuple[RecordSupport, ...];验证解释另列 ASN 与长度是否匹配
AspaProviders customer、present: bool、providers: Mapping[int, tuple[RecordSupport, ...]]、afi,provider 到支持来源的只读映射;无记录和已知记录分开表达
SourceInfo ID、非敏感身份指纹、来源类型、格式或协议版本、连接状态、各载荷能力、最后完成同步时间、期限、session ID、serial、最近错误
ParsedDataset 规范化载荷、字段是否存在、导出时间、逐条期限、生产者格式 ID、完整性诊断;解析结果自身不授予在线可信性
SourceUpdate 单个来源的完整提交状态、提交原因、会话与时效元数据;不含半个 RTR 事务

Router Key 在首版内部保留 ASN、20 字节 SKI、SPKI 原始字节及来源;不开放独立 Router Key 查询 API。内部载荷仍须完整进入持久化和恢复。通用导出可以保留这些原始状态,不因此承诺专用查询接口。

Afi 是 IANA 地址族整数枚举:IPV4=1、IPV6=2,不使用 IP 网络版本号 4/6,也不接受 bool、裸整数或字符串冒充枚举。所有 ASPA 记录、查询和结果中的 afi=None 都表示明确的双族共同范围,不是未知范围或任意族。不同 AFI 的授权、覆盖、期限和安全组分别保留;原始双族记录可支持两族,但单族记录不能自动授权另一族。

同来源 JSON 完全相同的载荷可去重,同时保留必要的支持期限;同来源同 customer、同有效 AFI 的 ASPA 记录按输入契约合并。不同来源的支持必须分别保留。一个 provider 的支持到期不应删除其他有效来源对该 provider 的支持。

provider 的来源至少精确到本库配置的输入来源;上游对象级关联只有输入明确提供时才保留。Routinator JSONExt 已合并 ASPA 的 source 元数据不能拆成虚构的逐 provider 对象支持,该条目按输入期限规则整体保守失效。

程序化完整数据集

ParsedDataset 可由宿主直接构造。present: frozenset[PayloadKind] 明确声明字段存在;空记录与缺能力分开。vrps/aspas 分别是 VrpRecord(vrp=...)/AspaRecord(aspa=...) 的 tuple;各记录可附 valid_from、expires_at 和 upstream_label。源 ID 在接纳时绑定,不由记录伪造。Router Key 仅随内部读取、协议及完整导出保留,不新增专用查询入口。

format_id="programmatic" 表示宿主已有规范化记录;generated_at 保存原始时间;complete=True 是宿主对所需验证范围完整性的声明,不能证明全网覆盖或代替认证。diagnostics 保存解析观察,metadata 保存冻结的未验证生产者信息(包括 CCR);complete=False 只可研究,不能接纳。索引不暴露可写容器。程序化入口与 reader 输出执行相同的时间、能力及资源检查。

aspa_families: frozenset[Afi] | None = None 声明 ASPA 已覆盖的地址族,包括明确同步为空的族。构造时 None 在 present 含 ASPA 时展开为两族,否则为空;显式非空范围要求 present 含 ASPA,显式空范围要求其不含 ASPA。每条原始记录的范围必须包含在声明中,afi=None 的双族记录不能放进单族覆盖。缺少另一族覆盖代表不支持该族,不能视为已经同步为空。该字段在构造后总是不可变 frozenset。

metadata 的键必须是字符串,值限可交换的有限 JSON 类型;列表冻结为 tuple。集合、任意 Python 对象、非有限浮点数和非法 Unicode 不属于元数据格式,构造时明确失败。Diagnostic 的 code、message 及可选 field 使用字符串,避免接纳成功后才发现无法完整导出或加载。

能力与状态

PayloadKind 的首版值为 vrp、aspa、router_key,其中最后一项只用于能力、内部状态和恢复诊断。

Availability 使用以下值;连接状态单独记录,不把两个维度塞进一个枚举。

值 含义 能否验证
unsupported 该协议或来源明确不提供这种载荷 否
not_loaded 可能支持,尚无完整成功提交 否
ready 已完整同步且存在有效记录 是
ready_empty 已完整同步、具有该能力且有效集合为空 是
expired 曾完整同步,但没有仍有效的载荷视图 否
unavailable 因致命协议错误、来源移除或明确失效而撤销可用性 否

ConnectionState 为 idle / connecting / connected / retrying / stopped。例如 retrying + ready 可以继续验证。状态同时提供 reason_code,不要求调用方通过日志文本推断原因。

ASPA 就绪且集合为空时,查询任意 customer 得到 present=False,算法可以产生 unknown。ASPA 未加载时,相同查询必须报告不可用。

SourceInfo.aspa_capabilities 保存 Afi.IPV4/IPV6 两个 Availability;省略时由聚合 ASPA 能力分别展开,聚合字段也缺失时为两族 unsupported。显式映射必须恰好具有两枚举键,聚合 capabilities[ASPA] 从两族推导:两者均 ready/ready_empty 才就绪,任一 ready 则聚合 ready,否则 ready_empty;未就绪按 unavailable、expired、not_loaded、unsupported 的顺序选取。单族 ready 不能使要求双族的旧 wait_ready 成功。显式 family 映射优先于传入的聚合值;dataclasses.replace 改变来源能力时必须一并提供更新后的 family 映射。交换/数据库读取还须独立验证传输的聚合值,不能利用构造器推导掩盖损坏。

快照与时间

SnapshotId(store_id: str, epoch: str, generation: int) 是结构化标识。generation 从 1 开始,仅在 store_id 和 epoch 都相同时有序;禁止仅用一个整数作为跨进程游标。

Snapshot 对外提供:

字段 约定
id、upstream_id 当前发布身份;共享读取者可另保留数据库发布身份
config_revision ConfigRevision(store_id, epoch, revision),定义视图的配置拥有者版本;共享读取保留 writer 配置版本,可与本地快照发布身份不同;静态 MemoryStore/导入具有自身初始版本,不充当热更新入口
published_at、context 发布时刻和在线或离线评价上下文
sources、active_groups 来源状态与每类载荷所用组,同一提交内一致
capabilities、usable_until 各载荷可用性,以及该载荷视图最早需要重新计算的期限
aspa_capabilities、aspa_usable_until、aspa_active_groups 分别按 Afi 保留 ASPA 能力、边界和选中的安全组;不得跨族复制或跨安全组合并
algorithm_versions、policy_id 固定规范版本;首版未启用策略时使用明确的无策略标识

索引和原始记录存储属于内部实现,禁止暴露可写字典。快照只代表发布时固定的来源及合并视图,持有快照不会保活数据。运行中配置变更不回写已交付的快照;原配置版本与期限继续固定,需要新信任条件的调用方重新获取并重验,见运行中配置变更。

EvaluationContext.online() 使用当前时钟。EvaluationContext.offline(at=...) 固定显式参考时间并标注离线;同一 MemoryStore 不混合在线和离线上下文。没有默认“忽略时效”开关。

在线查询开始时检查相应载荷的 usable_until。它是可能改变该载荷有效视图的最早来源期限、记录到期或未来记录开始生效的时间;到达边界后,旧快照拒绝该载荷查询并抛 SnapshotExpiredError。存储或管理器重建当前有效视图后可提供新快照,其他来源仍支持的数据继续可用。这一保守规则避免旧索引含有已到期支持而给出错误结果。

来源生成时间略晚于当前有效时间、但在允许偏差内时,可接纳等待激活;该来源在生成时间到来前为 not_loaded,不能以 ready_empty 产生正常 notfound/unknown。逐条未来生效窗口仍属于已完成来源的有效记录筛选。在线结果 evaluated_at 使用不倒退的有效评价时间,不把容忍范围内的墙钟回拨写成更早的评价时刻。

批量操作固定一个快照,但每条输入开始评价时重新检查期限。结果记录各自 evaluated_at;到期后的条目报告错误,不能冻结批次开始时间继续作在线结论。离线批次始终使用显式参考时间。

时钟、期限和恢复的运行规则见生命周期与持久化。

输入和结果类型

OriginInput(prefix, asn, input_id=None) 表示一条路由;prefix 接受 Network 或严格 CIDR,asn 为 int 或 None,None 对应 RFC 6811 的 NONE。AsPath 包含有序 PathSegment(kind, asns);kind 使用 sequence / set / confed_sequence / confed_set。AsPath.sequence(asns) 为普通路径的便捷构造器。方向固定为邻居端在前、起源端在后。

AspaInput(path, context, input_id=None, afi=None) 使用 AspaContext(local_asn, neighbor_asn, relationship)。relationship 从接收方视角取 customer / peer / provider / route_server / route_server_client。AspaProviders、AspaResult 和 BmpAnalysis 也携带实际评价的 afi;省略族只允许在双族授权和可用性相同的共同视图上执行,否则查询/验证明确报输入错误,不能静默选择一族。完整路径规则见输入与验证。

公开枚举命名为 EvaluationMode、PayloadKind、Availability、ConnectionState、OriginStatus、AspaStatus、BmpAssessment、PathSegmentKind 和 NeighborRelationship。Python 成员名使用大写下划线形式,value 为本文小写字符串,例如 NeighborRelationship.ROUTE_SERVER_CLIENT.value 为 route_server_client。线上结果 enum 的未知值不能被调用方当作 valid。

结果 状态及附加字段
OriginResult status: valid / invalid / notfound;规范化输入、reason_codes、快照 ID、evaluated_at、mode、可选 evidence
AspaResult status: valid / invalid / unknown;另有草案版本、输入上下文、压缩路径及可选坡段和授权证据
BmpAnalysis assessment: invalid / no_invalid_evidence / indeterminate;缺失上下文、已检查的候选场景及证据;不含冒充标准验证的 valid
BatchItem[T] index、input_id、可空 result 与可空 ErrorInfo;后二者恰有一个非空
BatchResult[T] snapshot_id、按输入顺序的 items、complete、success_count、error_count;complete 表示所有输入均已处理,不表示都有效

这些泛型按 Python 3.11 的 Generic / TypeVar 写法实现。简要模式保留结论、原因、快照、时间和模式;explain=True 才构建详细证据。证据有上限,使用 truncated 和固定快照下可重复查询的定位条件表示截断。

SourceDiff 包含 snapshot_id、left/right 的 SourceInfo、按载荷类型的 only_left/only_right/common 支持及有效期差异;载荷相同而支持不同不能省略。差异超过结果预算时明确 ResourceLimitError,不返回无标记截断集合。BmpContext 的字段为 observation_stage、path_reconstructed、path_direction、可选 local_asn/neighbor_asn/relationship,未知字段值不自动补全。

批量接口接受有界 Sequence,默认逐条返回领域输入错误或数据不可用错误;容器格式错误、超出批量上限和不可恢复的内部失败使整次调用失败。取消传播给调用方,不作为普通条目错误吞掉。异步批次因取消或连接中断而未返回时,不承诺已经交付部分结果。

异常与错误码

可恢复的网络故障由运行中的管理器记录并按策略重试;直接操作失败则抛异常。验证的 invalid、notfound、unknown 是正常领域结果,不是异常。

全部库异常继承 RpkiparrotError,携带 code、source_id、retryable、可选 retry_after、安全的 details;保留底层异常链。序列化使用 ErrorInfo,不发送 Python traceback 或凭据。

异常 稳定 code 示例 处理方式
ConfigurationError invalid_configuration、unsupported_backend、restart_required 修改配置
ConfigurationConflictError configuration_conflict 当前配置版本与 expected_revision 不同,重新获取状态并构造候选;ConfigurationError 子类
MissingExtraError missing_extra 安装所选功能的 extra
InputError invalid_input、unsupported_format、unsupported_path_context 修正输入,保留字段路径和记录位置
DataUnavailableError not_loaded、unsupported_capability、data_expired、source_unavailable 等待或更换来源
SnapshotExpiredError snapshot_expired DataUnavailableError 子类,重新取得快照
ReadyTimeoutError ready_timeout 检查所需能力与来源
TransportError / ProtocolError transport_error、protocol_error 查询来源状态;RTR wire_error_code 单独保存
ResourceLimitError resource_limit 明确 limit、observed 和受影响操作
PersistenceError persistence_failed、persistence_schema、persistence_owner、persistence_corrupt 区分读取/提交运行期失败、schema 不支持、owner 冲突和损坏;已识别暂时性读取故障标记 retryable,内存状态保持独立
ResyncRequiredError resync_required 结束旧订阅,从新初始快照开始
SnapshotGoneError snapshot_gone 远程固定快照已淘汰,重启整次分页或查询
ClosedError closed 重新创建有状态对象

具体 message 可以改善,不作为匹配接口。稳定 code 新增时同步文档;未知远程 code 由 SDK 保留为普通 RpkiparrotError,不能错误映射为验证结果。