Open-source Grok free registration CLI, xai_enroller auth pipeline, local auth service, tests and docs.
6.3 KiB
CSP 架构说明
本文档记录当前运行时架构。README 面向使用者;本文面向维护者,重点是并发边界、资源生命周期和测试不变量。
目标
运行时采用 CSP 风格的异步流水线:
S_Worker生产T。P_Worker发起外部请求,等待并生产Q。C_Worker原子获取一组T + Q并执行最终消费。
架构目标是资源所有权闭合、背压边界清晰、取消语义可测试。当前设计不包含中心调度器、动态角色选择或运行时动态并发分配。
核心组件
Physical_Sem 限制本地浏览器重操作并发。P_Worker 发出请求后,在等待 Q 返回期间不得持有该许可。
T_Slot_Sem 限制已入库 T 的容量。T 生成成功后才允许获取 slot。
Q_Slot_Sem 限制已返回并入库 Q 的容量。Q 真正返回前不得获取 slot。
Q_Pending_Sem 限制已发出但尚未终态的外部 Q 请求数量。
Inventory 是唯一库存门面。worker 不直接访问底层队列,只能调用:
put_t(env)
put_q(env)
claim_pair()
ResourceEnvelope 把资源实体和库存 slot 绑定在一起。slot 只能释放一次。
PairLease 是 claim_pair() 返回的异步上下文管理器。pair 一旦 claim 成功,两个 envelope 的所有权转移给 lease,直到 consumer 退出上下文。
AdmissionGate 是局部生产准入门控,只根据库存深度和静态水位决定是否允许继续生产。它不选择 worker 角色,不搬运资源,不调整并发容量。
Worker 流程
S_Worker
- 等待
AdmissionGate允许生产T。 - 获取
Physical_Sem。 - 生产
T。 - 释放
Physical_Sem。 - 调用
ResourceEnvelope.create_with_slot(...),创建 envelope 并获取T_Slot_Sem。 - 调用
Inventory.put_t(...),把所有权转移给Inventory。 - 所有权转移前如果异常或取消,释放已获取的 slot。
slot 获取和 envelope 创建必须绑定,避免取消落在“已获取 slot、尚未创建 envelope”的窗口。
P_Worker
- 等待
AdmissionGate允许生产Q。 - 获取
Q_Pending_Sem。 - 获取
Physical_Sem。 - 创建请求并发送。
- 释放
Physical_Sem。 - 在不持有
Physical_Sem的情况下等待Q返回。 Q返回后调用ResourceEnvelope.create_with_slot(...),创建 envelope 并获取Q_Slot_Sem。- 调用
Inventory.put_q(...),把所有权转移给Inventory。 - 请求进入终态后释放
Q_Pending_Sem。
Q_Pending_Sem 表达外部在途上限;Q_Slot_Sem 只表达已返回库存容量。两者不能合并。
C_Worker
- 进入
async with inventory.claim_pair() as pair。 - 获取
Physical_Sem。 - 消费 pair。
- 释放
Physical_Sem。 - 退出
PairLease,释放两个库存 slot。
C_Worker 不允许先取单边资源再等待另一边。对 consumer 来说,pair claim 必须是原子的。
Inventory 语义
第一版 Inventory 使用一把 lock 和一个 condition。lock 保护等待、复查、过期清理和弹出操作。
必须保持以下语义:
- 等待中的 consumer 不移除资源。
- claim 成功时同时移除一个有效
T和一个有效Q。 - 等待 pair 时被取消,不影响库存。
- claim 成功后被取消,由
PairLease核销两个 envelope。 - worker 永远不能直接操作底层
T/Q队列。
只要锁内逻辑保持很小,除 lazy expiry cleanup 外基本是 O(1),单锁在当前版本可以接受。只有 profile 证明锁竞争成为真实瓶颈时,才考虑拆锁或分片。
过期模型
ResourceEnvelope 可以携带 created_at 和 expires_at。Inventory 在配对前可以丢弃已过期资源。
当前采用 lazy cleanup:
put_t、put_q、claim_pair被触发时顺手清理。- 清理会释放它看到的过期 envelope 对应 slot。
- 系统完全静默时不会主动扫库。
- 单边长期故障和静默停摆由监控暴露,不靠后台 sweeper 修复。
当前不做 worker 级回队。消费失败后,已 claim 的 T 和 Q 都由 PairLease 核销。
容量策略
容量边界由 Semaphore 表达。启动期容量优先级:
显式 PHYSICAL_CAP > CAPACITY_PROFILE > CPU/内存自动派生
CAPACITY_PROFILE 只在启动时读取,是静态 profile,不是运行时调度器。
默认 worker 数量由容量派生:
S_WORKERS = Physical_Sem + 2
P_WORKERS = Q_Pending_Sem + 2
C_WORKERS = Physical_Sem + 2
worker 数量不是主要调参入口。主要并发边界是各类容量许可,而不是 coroutine 循环数量。
当前不做
当前版本明确不包含:
- 中心调度器;
- 运行时角色选择;
- 动态打分;
- 动态并发控制;
- worker 级回队;
- 高价值资源抢救策略;
- 后台过期清扫;
- 自动切换高风险浏览器模式。
这些方向可以单独实验,但不能混入基础所有权模型。
必须保持的不变量
实现和测试必须维持以下不变量:
- 每个已获取的库存 slot 最终释放一次且只释放一次。
- 每个已准入的 pending 请求最终释放一次
Q_Pending_Sem。 P_Worker等待Q返回时不持有Physical_Sem。Q_Slot_Sem只在Q返回后获取。C_Worker只能通过Inventory.claim_pair()获取T和Q。claim_pair()要么返回一个受PairLease保护的完整 pair,要么不返回资源。- 等待 pair 时取消,不移除库存资源。
- claim pair 后取消,两个 envelope 由
PairLease释放。 - 触发清理时,过期资源不能被配对。
- 监控只读,不修改 Semaphore、队列或 worker 状态。
测试
.venv/bin/pip install -r tests/requirements.txt
快速检查:
python3 -m unittest tests.test_admission_gate tests.test_register_runtime_unittest tests.test_inventory_unittest tests.test_runtime_log_analyzer -v
完整测试:
python3 -m pytest tests -q
重点测试文件:
tests.test_inventory_unittest:库存、过期和 lease 行为。tests.test_register_runtime_unittest:worker 运行语义和监控行。tests.test_admission_gate:局部门控水位。tests.test_runtime_log_analyzer:日志解析兼容性。tests/test_cancel.py:取消边界。tests/test_property.py:随机化不变量检查。tests/test_stress.py:更高并发 fake-service 压测。