Files
grok-free-register-oss/docs/architecture.md
chaos d10009d639 Initial commit: grok-free-register-oss
Open-source Grok free registration CLI, xai_enroller auth pipeline,
local auth service, tests and docs.
2026-07-16 21:04:05 +08:00

6.3 KiB
Raw Blame History

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 只能释放一次。

PairLeaseclaim_pair() 返回的异步上下文管理器。pair 一旦 claim 成功,两个 envelope 的所有权转移给 lease直到 consumer 退出上下文。

AdmissionGate 是局部生产准入门控,只根据库存深度和静态水位决定是否允许继续生产。它不选择 worker 角色,不搬运资源,不调整并发容量。

Worker 流程

S_Worker

  1. 等待 AdmissionGate 允许生产 T
  2. 获取 Physical_Sem
  3. 生产 T
  4. 释放 Physical_Sem
  5. 调用 ResourceEnvelope.create_with_slot(...),创建 envelope 并获取 T_Slot_Sem
  6. 调用 Inventory.put_t(...),把所有权转移给 Inventory
  7. 所有权转移前如果异常或取消,释放已获取的 slot。

slot 获取和 envelope 创建必须绑定,避免取消落在“已获取 slot、尚未创建 envelope”的窗口。

P_Worker

  1. 等待 AdmissionGate 允许生产 Q
  2. 获取 Q_Pending_Sem
  3. 获取 Physical_Sem
  4. 创建请求并发送。
  5. 释放 Physical_Sem
  6. 在不持有 Physical_Sem 的情况下等待 Q 返回。
  7. Q 返回后调用 ResourceEnvelope.create_with_slot(...),创建 envelope 并获取 Q_Slot_Sem
  8. 调用 Inventory.put_q(...),把所有权转移给 Inventory
  9. 请求进入终态后释放 Q_Pending_Sem

Q_Pending_Sem 表达外部在途上限;Q_Slot_Sem 只表达已返回库存容量。两者不能合并。

C_Worker

  1. 进入 async with inventory.claim_pair() as pair
  2. 获取 Physical_Sem
  3. 消费 pair。
  4. 释放 Physical_Sem
  5. 退出 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_atexpires_atInventory 在配对前可以丢弃已过期资源。

当前采用 lazy cleanup

  • put_tput_qclaim_pair 被触发时顺手清理。
  • 清理会释放它看到的过期 envelope 对应 slot。
  • 系统完全静默时不会主动扫库。
  • 单边长期故障和静默停摆由监控暴露,不靠后台 sweeper 修复。

当前不做 worker 级回队。消费失败后,已 claim 的 TQ 都由 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() 获取 TQ
  • 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_unittestworker 运行语义和监控行。
  • tests.test_admission_gate:局部门控水位。
  • tests.test_runtime_log_analyzer:日志解析兼容性。
  • tests/test_cancel.py:取消边界。
  • tests/test_property.py:随机化不变量检查。
  • tests/test_stress.py:更高并发 fake-service 压测。