From d648ba37e07b045e3cae1f37d5f893f980baaa38 Mon Sep 17 00:00:00 2001 From: youfak Date: Thu, 30 Jul 2026 15:46:27 +0800 Subject: [PATCH] docs: record worker runtime capacity delivery --- docs/adr/005-redis-activity-pool.md | 47 +++++++++++++++++++++++-- docs/api/control-plane.md | 12 +++++++ docs/design/architecture.md | 16 +++++++++ docs/development/implementation-plan.md | 11 ++++-- docs/requirements/traceability.md | 2 +- docs/testing/strategy.md | 9 ++--- progress.md | 20 +++++++++++ task_plan.md | 8 +++-- 8 files changed, 113 insertions(+), 12 deletions(-) diff --git a/docs/adr/005-redis-activity-pool.md b/docs/adr/005-redis-activity-pool.md index 7d9089f..51a24b4 100644 --- a/docs/adr/005-redis-activity-pool.md +++ b/docs/adr/005-redis-activity-pool.md @@ -63,10 +63,14 @@ var _ activitypool.HealthStore = (*Adapter)(nil) var _ activitypool.InventoryReader = (*Adapter)(nil) var _ extraction.Store = (*Adapter)(nil) var _ ownership.Repository = (*Adapter)(nil) +var _ workerruntime.SessionWriter = (*Adapter)(nil) +var _ workerruntime.ReportWriter = (*Adapter)(nil) +var _ workerruntime.RuntimeReader = (*Adapter)(nil) +var _ pool.InventoryReader = (*Adapter)(nil) ``` -Provider、Checker、Distribution 和 Ownership 只依赖各自需要的端口,不直接 -依赖 Redis 客户端、键名、Lua 返回格式或清理策略。 +Provider、Checker、Distribution、Ownership 和 Worker Runtime 只依赖各自需要 +的端口,不直接依赖 Redis 客户端、键名、Lua 返回格式或清理策略。 `ownership.Repository` 改为适合远程存储的上下文感知接口: @@ -97,6 +101,11 @@ pp:{activity}:owners HASH proxyID -> ownership assignment pp:{activity}:owner-expiry ZSET proxyID -> ownership expiry milliseconds pp:{activity}:epoch STRING ownership 全局递增代次 pp:{activity}:inventory HASH upstreamID -> 当前未提取库存 +pp:{activity}:worker-sessions HASH workerID -> 当前 Worker session +pp:{activity}:worker-session-expiry ZSET workerID -> session expiry milliseconds +pp:{activity}:worker-runtime HASH workerID -> 完整稀疏运行态报告 +pp:{activity}:worker-runtime-expiry ZSET workerID -> report expiry milliseconds +pp:{activity}:owned: ZSET 单 Upstream 已分配 AVAILABLE Proxy pp:{activity}:idem: STRING 带 TTL 的提取幂等结果 pp:{activity}:op: STRING 带 TTL 的内部操作结果 ``` @@ -167,6 +176,38 @@ Assign、Renew、BeginDrain 和 AcknowledgeDrain 分别使用有界小脚本, - Assign 与 Extract 并发竞争同一 Proxy 时,只允许一个操作成功。 - Expire 使用 `limit` 分批回收过期 assignment,禁止无界返回。 +### Worker 运行态与容量汇总 + +Gateway 的 `Capacity` 使用一次打包原子读取取得同一时刻的 Active/Reserved, +`snapshot.Store` 周期生成完整稀疏报告。当前 Snapshot 已移除但仍有活动连接的 +Proxy 继续以 `draining=true` 上报,直到 Active/Reserved 同时归零。 + +Redis Adapter 在单个 `{activity}` 原子边界内维护 Worker session 和运行态报告: + +1. 新 Worker session 替换旧 session,并隔离旧实例后续写入。 +2. session 保存 Controller 已 ACK 的 snapshot version 与 ownership epoch;报告 + 必须与 ACK 上界完全一致,不能通过自报超前 epoch 绕过所有权校验。 +3. `report_sequence` 严格递增;同序号、同内容可幂等重放,冲突或倒序拒绝。 +4. 非零计数必须匹配当前 Proxy owner、Worker ID 和 ownership epoch。 +5. session/report TTL 使用 Redis 服务端时间;过期、缺失或损坏时容量 fail-closed。 +6. 空报告清除该 Worker 的全部旧计数,稀疏报告中缺失的 Proxy 计数视为零。 + +`pool.InventoryReader` 低频返回单个 Upstream 的 `Managed` 和 +`AvailableSlots`。Managed 统计 FETCHED/CHECKING/AVAILABLE/SUSPECT/DRAINING; +Available Slots 只统计超过 safety margin、状态为 AVAILABLE 且所有权与新鲜 +Worker 运行态一致的 `max - active - reserved`。未分配 Proxy 可直接贡献 Max; +已分配但运行态未知的 Proxy 贡献零槽位。PostgreSQL 不保存这些短效报告或容量 +明细,Gateway 每次请求也不访问 Redis。 + +Managed 直接读取现有 Upstream 权威计数;Available Slots 只扫描目标 Upstream +的未分配可用索引与已分配可用索引,不扫描全局 Proxy,也不受其他供应商活记录 +数量影响。索引成员数超过单次扫描预算时直接返回不可用,不降级为近似容量。 +生产规模验收仍需验证脚本 p95/p99、CPU、内存和过期风暴下的有界行为。 + +Gateway `snapshot.Store` 扫描有界的当前 Snapshot,并使用分片索引补充已移除但 +仍非零的 runtime,不扫描全部历史 Proxy;历史 Capacity 注册表设硬上限,达到 +上限时拒绝新 Snapshot 并保持旧视图,避免长期轮换造成无界内存增长。 + ### 短 TTL 清理 Redis Hash 字段没有独立 TTL,因此使用三层有界清理: @@ -226,6 +267,8 @@ Redis `inventory` 是当前未提取 Proxy 数量的运行时真值: - Assign 与 Extract 并发互斥,以及 renew/drain/ACK/expire。 - 提交后连接断开、脚本缓存丢失、上下文取消和 Redis 不可用。 - 30 秒 TTL 持续写入下的有界清理与库存一致性。 +- Worker session 替换、运行态序号幂等/冲突、报告过期和 ownership epoch 隔离。 +- 权威 Managed/Available Slots 聚合及扫描预算耗尽时的 fail-closed 行为。 Lua 语义必须使用真实 Redis 8.2 集成测试验证。单元测试最长 60 秒,并执行 gofmt、go vet、全量测试、构建和 diff whitespace 检查。100,000 QPS 只能由 diff --git a/docs/api/control-plane.md b/docs/api/control-plane.md index 3a98c4f..1008c73 100644 --- a/docs/api/control-plane.md +++ b/docs/api/control-plane.md @@ -34,6 +34,18 @@ sequenceDiagram `worker_id` 是逻辑节点,`instance_id` 区分进程重启,`session_id` 防止旧进程 继续上报。所有权 `epoch` 小于 Controller 当前值的数据必须拒绝。 +`ReportRuntimeRequest.report_sequence` 在当前 `session_id` 内严格单调递增。 +相同序号只允许内容完全相同的幂等重放;较小序号或相同序号的不同内容必须 +拒绝。`observed_at` 只用于观测,不作为乱序判定依据,运行态 TTL 统一使用 +Controller 侧 Redis 服务端时间。session 同时保存 Controller 已接受的 +`snapshot_version/ownership_epoch`;运行态报告必须与该 ACK 上界完全一致, +Worker 自报的超前版本或 epoch 也必须拒绝。 + +运行态上报是完整稀疏替换:只携带 Active/Reserved 非零的 Proxy,空列表表示 +当前会话全部归零。Controller 只有在 Worker session、报告 TTL、Proxy ownership +和 ownership epoch 同时有效时才使用计数;报告缺失、过期或不一致时按零可用 +容量 fail-closed,不能把未知计数解释成空闲容量。 + ## 3. Snapshot 与 Delta 完整 Snapshot 包含: diff --git a/docs/design/architecture.md b/docs/design/architecture.md index 30b83c3..5eff276 100644 --- a/docs/design/architecture.md +++ b/docs/design/architecture.md @@ -143,6 +143,15 @@ type Proxy struct { 运行态 `active` 与 `reserved` 存在 Worker 本地、按 Proxy ID 分片,不写入 不可变 Snapshot。 +Worker 以有界周期批量上报运行态,而不是在每个 Gateway 请求上写 Redis。 +报告通过公用 `workerruntime` seam 表达为完整稀疏替换:Gateway 从打包原子计数 +读取同一时刻的 Active/Reserved;已从当前 Snapshot 移除但仍有连接的 Proxy +继续以 draining 状态上报。Controller 使用 session、单调 report sequence、 +ownership epoch、Controller 已 ACK 的 snapshot/epoch 上界和 Redis 服务端 TTL +共同校验;缺失、过期或超前报告按零可用容量 fail-closed。运行态报告扫描有界 +当前 Snapshot,并以分片索引补充已移除但仍非零的 runtime;历史 Capacity +注册表有硬上限,避免短 TTL Proxy 持续轮换导致心跳扫描与内存无界增长。 + ## 6. Proxy 状态机 ```mermaid @@ -244,6 +253,13 @@ flowchart TD fail-closed,不回退为本地 Leader。补池使用 minimum/target 双水位迟滞,库存 复核期间若仍有 pending Fetch,则等待下一轮再同步 Managed,避免重复计数。 +补池读取 `pool.InventoryReader` 返回的权威 `Managed` 与 `AvailableSlots`,不使用 +AVAILABLE Proxy 数量乘固定并发的近似值。Available Slots 同时计入 Proxy 状态、 +TTL safety margin、MaxConcurrency、Worker Active/Reserved 和当前 ownership; +未知 Worker 运行态贡献零槽位。Redis 只扫描目标 Upstream 的未分配/已分配 +AVAILABLE 索引,Managed 读取已有权威计数;该读取属于 Controller 冷路径, +Gateway 热路径仍只访问本地 Snapshot 和本地原子计数。 + ### 8.1 Empty、Duplicate 与 Error - **Empty**:HTTP/认证成功、模板执行成功,解析后合法 Proxy 数为 0。 diff --git a/docs/development/implementation-plan.md b/docs/development/implementation-plan.md index 230aced..d06c108 100644 --- a/docs/development/implementation-plan.md +++ b/docs/development/implementation-plan.md @@ -219,8 +219,15 @@ generation + epoch fence、全局 requestInterval、全局 maxInFlight Permit、 回收及 Redis 状态丢失后的新 generation 自动重建;Redis 异常期间不发放请求。 补池配置新增必填 `refill` 双水位和 `fetch.estimatedIPsPerCall`,Pool Reconciler 已实现迟滞与 pending 槽位折算,FetchBudget 仅在无 pending 时同步 Redis 权威 -Managed。Provider Fleet、Worker Active/Reserved 汇总和 bootstrap 接线仍待完成, -因此本轮不勾选 Task 10 的组合验收项。 +Managed。Gateway 已增加打包原子 Active/Reserved 读取与完整稀疏运行态快照; +公用 `workerruntime` session/report/read seam 同时提供并发安全内存参考实现和 +生产 Redis Adapter。Redis 以服务端时间、Worker session、已 ACK snapshot/epoch、 +单调 report sequence 和报告 TTL 原子隔离旧实例,并由 `pool.InventoryReader` 汇总 +权威 Managed/Available Slots;真实 Redis 8.2 已覆盖空报告、幂等重放、倒序、 +冲突、超前 epoch、过期、单 Upstream 扫描隔离和预算耗尽的 fail-closed 行为。 +WorkerControlPlane gRPC 接收端、 +session 签发/心跳、Snapshot ACK 账本、Provider Fleet/bootstrap、全局 +`fetch.maxTotal` 和健康执行链仍待完成,因此本轮不勾选 Task 10 的组合验收项。 ## Task 11: Checker and Health Reducer diff --git a/docs/requirements/traceability.md b/docs/requirements/traceability.md index 4b86e00..32a16c1 100644 --- a/docs/requirements/traceability.md +++ b/docs/requirements/traceability.md @@ -46,7 +46,7 @@ | PROXY-002 | 唯一键包含 scheme、host、port、username、credentialVersion | 6655-6727, 8605-8678 | 去重单测 | | PROXY-003 | TTL 来源优先级明确并统一 UTC | 681-747, 8655-8678 | TTL 表驱动测试 | | CAP-001 | Gateway 分配使用 Reserved -> Active 原子转换 | 1203-1467, 8530-8597 | 固定 Max 下打包 CAS 与 1,000 并发不超卖已完成;动态降容和完整生命周期证据待完成 | -| CAP-002 | 补池依据 Available Slots,不只看 Proxy 数量 | 1203-1402, 8530-8597 | `AvailableSlots`、显式 minimum/target 水位、pending 槽位和迟滞 Reconciler 已测试;Worker Active/Reserved、ownership、目标健康及 Gateway reserve 运行时聚合待完成 | +| CAP-002 | 补池依据 Available Slots,不只看 Proxy 数量 | 1203-1402, 8530-8597 | `AvailableSlots`、显式 minimum/target 水位、pending 槽位和迟滞 Reconciler 已测试;Gateway 打包 Active/Reserved 报告、Worker session/ACK/sequence/TTL/ownership fence、单 Upstream 索引及 Redis 权威 Managed/Slots 汇总已通过内存与真实 Redis 测试;WorkerControlPlane 接线、目标健康和 Gateway reserve 策略仍待完成 | | CAP-003 | pool.maxSize 包括 FETCHED/CHECKING/AVAILABLE/SUSPECT/DRAINING 与 pending expected | 3001-3533, 6642-6680 | `FetchBudget` 100 并发额度预占测试 | | CAP-004 | TTL safety margin 内禁止新分配 | 173-220, 6728-6741 | 时钟测试 | | CAP-005 | 多 Worker 不在热路径访问 Redis 计数 | 1403-1467 | Gateway 包依赖审计、Snapshot/Dispatch 测试 | diff --git a/docs/testing/strategy.md b/docs/testing/strategy.md index 2da5c23..e15c500 100644 --- a/docs/testing/strategy.md +++ b/docs/testing/strategy.md @@ -74,16 +74,17 @@ soak 测试单独标记,不混入快速单测。 ## 5. 当前本地微基准 -2026-07-28,Windows/amd64、Intel Core Ultra 7 155H: +2026-07-30,Windows/amd64、Intel Core Ultra 7 155H: ```text -BenchmarkAcquire100kIndexed-22 3553592 640.0 ns/op 256 B/op 2 allocs/op -BenchmarkStoreApply100k-22 1 472.7 ms/op 654 MB/op 2700642 allocs/op +BenchmarkAcquire100kIndexed-22 1000000-1867125 893.9-1047 ns/op 256 B/op 2 allocs/op +BenchmarkStoreApply100k-22 1 518.7 ms/op 540 MB/op 3000887 allocs/op ``` `Acquire` 已使用 scheme/upstream/tag 索引,结果只代表本地选择和容量预留。 `Store.Apply` 属于冷路径且当前内存开销较高;运行态为防止旧快照在途连接超配, -暂不自动回收曾出现过的 Proxy ID。后续需要基于 RCU/引用计数定义安全回收点。 +暂不自动回收曾出现过的 Proxy ID,但注册表有 1,000,000 项硬上限,达到上限时 +拒绝新 Snapshot 并保留旧视图。后续需要基于 RCU/引用计数定义安全回收点。 这些数据不包含网络、认证、Provider、存储或多 Worker 协调,不能作为 100k QPS 端到端验收结论。 diff --git a/progress.md b/progress.md index 15ccb09..206fcf3 100644 --- a/progress.md +++ b/progress.md @@ -2,6 +2,26 @@ ## 2026-07-30 +- Gateway `Capacity` 新增一次打包原子读取,`snapshot.Store` 可生成完整稀疏 + Active/Reserved 运行态报告;当前快照已移除但仍有连接的 Proxy 会持续以 + draining 上报,归零后从后续报告消失。 +- 新增公用 `workerruntime` session/report/read seam 与并发安全 MemoryStore; + 完整替换、空报告清零、session fencing、单调 sequence、同内容幂等重放、 + 冲突/倒序拒绝和 TTL fail-closed 均已有单测。 +- 生产 `redisactivity` Adapter 新增 Worker session/运行态 Lua 和权威 + `pool.InventoryReader`;Managed/Available Slots 原子计入状态、TTL safety、 + MaxConcurrency、ownership 及 Active/Reserved,未知或过期运行态贡献零容量。 +- 真实 Redis 8.2 已覆盖 ACK snapshot/epoch、旧 session、空报告、超前 epoch、 + 报告过期、扫描预算耗尽及容量聚合。Managed 使用已有权威计数,Available + Slots 只扫描目标 Upstream 的未分配/已分配可用索引;Gateway 报告扫描当前 + Snapshot,并用分片索引补充已移除但仍非零的 runtime,历史注册表设置硬上限。 + WorkerControlPlane 接收端、Provider + Fleet/bootstrap、全局 `fetch.maxTotal` 和健康执行链仍待完成,总验收计数保持 + 51/73。 +- Gateway 当前 Proxy 禁用活跃索引回调,只有移出 Snapshot 后才开启分片追踪; + 本机 100k Proxy `Acquire` 三轮 1 秒基准为 893.9-1047 ns/op、256 B/op、 + 2 allocs/op。该数据只证明本地调度微基准,不代表 100k QPS 集群验收。 + - 新增公用 Provider `Coordinator.RunLeader` / `LeaderSession` 深 seam 和独立 `redisprovider` Adapter;Redis Lua 原子维护 generation、epoch、Leader 租约、 全局 requestInterval 与带 TTL 的 maxInFlight Permit,异常时 fail-closed。 diff --git a/task_plan.md b/task_plan.md index 934355c..c3864a1 100644 --- a/task_plan.md +++ b/task_plan.md @@ -35,7 +35,8 @@ 机器契约和文档类滞后勾选已按仓库证据校正 13. [进行中] 落地 `proxy-controller` 进程装配;配置单次加载、PostgreSQL 迁移、 Redis 活动池、低基数状态聚合、Distribution/Admin/Metrics 启动与关闭已完成, - 双存储 bootstrap 和探针集成已通过,Provider、业务指标与完整容器进程链仍待实现 + 双存储 bootstrap 和探针集成已通过;Worker 运行态存储与权威容量读取原语已 + 完成,WorkerControlPlane、Provider、业务指标与完整容器进程链仍待实现 ## 串并行关系 @@ -57,7 +58,8 @@ - Docker Compose 配置与 Kubernetes Kustomize 已完成静态渲染验证;Redis 8.2 与 PostgreSQL 18 的隔离 Adapter fixture 已运行,完整目标运行拓扑尚未启动。 - `cmd/proxy-controller` 已实现 Admin/Distribution/Metrics 与双存储启动装配; - Gateway、Checker、Loadgen、Provider Leader/分布式限流、业务指标、Redis - 故障转移验证与代表性集群压测属于后续实施范围。 + Provider 分布式协调和 Worker 运行态 Redis 原语已完成,但 WorkerControlPlane + 接收端、Provider Fleet、Gateway、Checker、Loadgen、业务指标、Redis 故障 + 转移验证与代表性集群压测属于后续实施范围。 - `implementation-plan.md` 当前按 73 个验收项统计;已校正为 51 项完成, 验收项完成率约 69.9%,不等同于生产就绪度。