运行中配置变更契约
首版支持运行中增加、删除来源和修改配置。宿主通过 Client.apply_config 提交完整的冻结配置;不需要关闭整个 Client。实现与验收跟踪见 Issue #3。进程内入口已实现,真实签名见源码参考;下文的持久化和外部传播要求随相应模块交付。配合公共 API、生命周期及配置使用。
入口和提交回执
async def apply_config(
self,
config: ClientConfig,
*,
expected_revision: ConfigRevision,
transports: Mapping[str, TransportFactory] | None = None,
readers: Mapping[str, JsonReader] | None = None,
) -> ConfigReceipt: ...
只允许在 running 状态调用,由 Client 的拥有事件循环执行。ConfigRevision(store_id, epoch, revision) 标识该发布者的配置版本,初始 revision=1;不是配置文件 schema_version,也不随普通数据刷新增加。get_status().config_revision 返回当前版本。宿主保留自己提交的配置对象,使用 dataclasses.replace 等方式构造候选;状态、日志和回执不返回凭据。
同时最多准备一个配置候选;已有候选占用该额度时,新的调用报 ResourceLimitError,不创建无界配置队列。expected_revision 必须匹配提交时的当前配置版本;不匹配抛 ConfigurationConflictError(code="configuration_conflict"),不覆盖他人的修改。配置及注入映射均相同时为 no-op,返回 changed=False,不增加配置版本、快照 generation 或事件。注入对象按实例身份比较,不调用用户自定义相等方法来判断信任等价。
ConfigReceipt 包含 changed、config_revision、snapshot_id,以及确定排序的 added_source_ids、removed_source_ids、restarted_source_ids。成功表示配置和当前快照已共同提交,不表示新增来源已经同步、数据库已经落盘或旧工作线程已经退出。调用方分别使用 wait_ready、flush 和 get_status 检查这些状态。wait_ready 判断整个生效视图;检查某个新增来源是否就绪须观察该来源状态。
transports/readers 为 None 时,沿用仍适用于新配置的既有 ID 绑定,删除来源的绑定同时移除;显式映射是这一类绑定的完整替换,空映射恢复全部内置实现。映射在入口复制并校验,不允许调用方以后修改字典就暗中改变运行配置。绑定类型、配置 format 和 extras 必须在提交前校验。不得将同一有状态、非线程安全的 reader/adapter 实例交给可能并发运行的来源;实现对同一实例串行调用,适配器不得依靠修改全局注册表工作。
ConnectedTransport 只能归属一个来源实例。替换使用既有流的来源时须同时提供新的绑定,不能重新使用已交付或已关闭的包装器;普通可重连工厂仍可显式共享。此检查在准备候选前执行,失败候选不能关闭仍由旧配置使用的流。
原子性和任务替换
- 校验完整候选、资源限制、依赖、显式注入与运行模式。准备候选视图时保持现有来源工作,不为未提交的新配置发起网络连接,不运行用户回调作为提交的一部分。
- 拥有者重新核对配置版本、来源数据版本和当前时间。在短临界区一起发布配置版本、完整来源状态、活动组、能力和快照;准备期间的数据更新不能被旧候选覆盖。需要重建时在临界区外重新准备。
- 同一串行发布入口写入
reason=configuration_changed的 SnapshotEvent,携带 before/after 配置版本,必要时标记整类重验;订阅注册不能落入空窗。非 no-op 的配置变更即使载荷没变也产生新快照。 - 在宿主管理的任务树内停止移除或替换的来源,启动新来源;未变来源继续工作。提交后的连接或读取失败只使对应来源失败,不回滚已经生效的整份配置。
每次创建来源任务分配不可复用的内部 incarnation 标识。来源更新、状态报告、构建结果和重连定时器均携带该标识;删除后以相同 source_id 重建也不能接纳旧任务的迟到 EOD、JSON 或错误报告。未改身份、仅改调度参数的来源可继续使用当前会话;提交时的组归属以新配置为准。
取消若发生在提交前,不发布候选并清理准备资源;提交之后即使调用方未收到回执也不自动回滚。调用方通过 config_revision 和事件确认状态后再决定重试。aclose 与提交使用相同串行入口:closing 开始后拒绝新变更;已提交的变更归关闭流程清理。旧来源的不可中断工作仍受 Client 管理、计入资源预算,不能因为逻辑删除就遗弃线程或释放其正在使用的连接。
字段的变更行为
| 配置变化 | 首版行为 |
|---|---|
| 增加来源或组 | 原子加入,来源起始为未加载;按新的组调度规则启动,不假装已有完整空数据 |
| 删除来源或组 | 从新视图移除相关原始支持并重新选择活动组;其他有效来源的支持保留 |
| 删除最后一个来源 | 允许 groups=() 或空组;Client 继续运行,无有效载荷,wait_ready 可以超时;以后仍可增加来源 |
| 改变组 priority、成员或 failback_delay | 保留身份未变的来源原始状态,原子重算选择;新优先级显式立即生效于已就绪组,未就绪组不取代有效组;后续恢复回切遵守新延迟 |
| 改变 kind、端点/文件路径/URL、协议范围、format、reader_profile_id、trust_profile_id,替换 TLS/SSH 认证、SSH 用户名/密钥/known_hosts 路径或 provider 实例、transport 或 reader 实例 | 作为来源替换,撤销旧来源在新视图的支持和增量恢复资格,新任务从未加载开始;不能沿用旧 session/serial 或旧期限 |
| 改变 JSON freshness | 作为来源替换;再次读取相同旧输入仍以原生成时间、原始期限评价,不能用提交时间续期 |
| connect/query/request_timeout、poll_interval、check_order | 新操作使用新值;进行中的事务使用启动时的值和校验规则,时限不重新起算;调度更新不延长数据期限 |
| required、startup_timeout | 更新默认就绪/健康要求和后续组准备等待窗口;已开始的显式 wait_ready 保留其 required 与总截止时间 |
| cleanup_timeout、持久化 flush_timeout | 后续清理/flush 使用新值;已开始的清理或等待不重置期限,安全关闭优先 |
| Limits | 允许有条件原子修改;新值不得小于当前已占用、已预留或不可撤销在途工作预算,否则整体拒绝;不能静默删除有效记录腾空间 |
| 持久化 backend/path/mode、注入后端实例,clock_tolerance,事件循环/时钟实现 | 当前 Client 不热切换,报 ConfigurationError(code="restart_required");显式关闭后重建,不对一半字段提交成功 |
| 服务监听、认证边界、worker 数;CLI 入口自身配置 | 属于外层应用生命周期,不由 Client.apply_config 修改;宿主明确重建相应应用资源 |
Limits 中改变现有订阅队列形状时,提交后通过独立信号要求这些订阅 ResyncRequired;新订阅使用新值。降低服务端保留数量或保留时间可按既有淘汰规则释放内部引用、使 token 返回 SnapshotGone,仍须计入外部持有及清理中引用;不能声称引用释放必然立即归还 RSS。发布前必须证明这些操作符合新的总预算,否则拒绝变更。协议硬限制不允许放宽。
只有组为空时允许等待以后注入来源;非空配置仍检查 required 与明确已知能力的矛盾。删除全部来源后的无数据是不可用状态,不是 ready_empty,不能返回正常 notfound/unknown。
快照、持久化和外部接口
快照增加 config_revision;共享读取的配置版本保留 writer 身份,与 reader 本地快照身份分开。来源状态保留可持久化的非敏感配置身份;配置变更与快照一同进入最新完整持久化状态。磁盘失败不撤销内存变更,status 分别报告当前和持久化版本。启动仍以宿主显式给出的配置为准,不能从旧数据库重新启用已删除或信任条件不匹配的来源。
已交给调用方的不可变快照、已开始的批次和远程分页仍绑定取得时的配置版本,并按原期限检查;热更新不把它们改写成新配置。删除或撤信作用于提交后的当前视图,不承诺追溯撤回已经交付的数据。需要以新信任条件作判断的宿主必须停止使用旧版本、取得回执所指版本或更新快照并重验。普通查询结果可通过 snapshot_id 对应到配置版本;导出、SnapshotRef 和状态直接携带 config_revision。
SQLite 共享读取者在轮询到新完整状态后原子接纳配置身份与数据;跨进程存在轮询/读取延迟,不承诺立即撤回其他进程持有的旧快照。HTTP 新请求默认取新配置的当前快照,已有显式 token 继续按固定版本和保留期工作。
首版库提供进程内配置入口,SharedSnapshotClient 和 RemoteClient 不提供写配置方法。查询 HTTP 不增加远程管理端点,CLI 不自动监视配置文件、依赖 SIGHUP 或加载任意 Python 插件;宿主可显式读取配置后调用同一公共入口。此边界不限制宿主自行实现跨平台的配置通知。