proxy-pool/findings.md
youfak d6ac783c1d
Some checks failed
ci / openapi (push) Has been cancelled
ci / proto (push) Has been cancelled
ci / image (push) Has been cancelled
ci / deployment (push) Has been cancelled
ci / test (ubuntu-latest) (push) Has been cancelled
ci / test (windows-latest) (push) Has been cancelled
ci / race (push) Has been cancelled
ci / integration (push) Has been cancelled
ci: build linux application image
2026-08-07 21:11:36 +08:00

295 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 对话内容提取结果
## 权威来源
- 文件:`对话内容.md`
- 大小167,284 字节
- 行数9,404 行
- 读取时间2026-07-28
## 最终产品边界
系统同时提供两种入口:
1. Gateway系统选择上游代理并代转发 HTTP、HTTPS CONNECT预留
SOCKS5 扩展。
2. Distribution API一次性、独占地返回真实上游代理。代理成功提取后
立即从可分配池移除,不需要租约、释放接口或使用量追踪。
系统管理多个 Upstream Provider并使用配置化 Routing 决定 Gateway 或
Extract 请求使用哪些 Upstream。
## 后出现并覆盖早期建议的决策
- Distribution API 的 Lease/Release/Shared Allocation 方案被最终的
Exclusive Extraction 取代。
- `pool.maxSize` 表示当前系统维护的未提取代理硬上限;累计供应商提取额度
使用独立的 `fetch.maxTotal`,不能复用同一个字段。
- `switchAfterEmptyFetch` 只统计“上游请求成功、解析成功,但解析后没有任何
合法代理”的结果超时、HTTP 错误、认证错误、DNS 错误和模板错误只计
`fetchErrorCount`。全重复结果不当作空结果,单独记录。
- `consecutiveEmptyFetch` 属于 UpstreamSequential 当前选择属于 Routing。
某 Upstream 达到阈值时,引用它的 Routing 原子切换;已有代理继续耗尽。
- Extract API 默认部分满足 `partial`;也支持 `allOrNothing`
- Extract API 从 `AVAILABLE` 原子转换到 `EXTRACTED` 后才返回,保证同一代理
永不发放两次。
## 核心不变量
- 请求热路径不得调用 Provider API也不得查询全量 Redis/PostgreSQL 后排序。
- 代理分配必须原子预留容量,防止并发超卖。
- 代理唯一键为 `scheme + host + port + username + credentialVersion`;日志
和指标不得暴露密码。
- TTL 优先级为响应 `expiresAt`、响应 `ttl`、配置固定 TTL活动池中的每个
Proxy 必须有明确过期时间,内部时间统一 UTC。
- 健康检查至少区分全局健康和 Routing/目标健康,并使用抖动和并发上限。
- GET/HEAD 可按配置安全重试非幂等方法默认不自动重试CONNECT 建立后
不透明重放。
- 默认不直连;所有 Upstream 不可用时必须显式选择 reject、wait 或 direct。
- Gateway、Distribution、Admin、Metrics 使用独立监听和认证/访问控制。
- 非回环监听且无认证、无 CIDR 保护时,严格模式必须拒绝启动。
## 可观测性边界
- Provider 拉取结果只按固定 `class` 聚合:`valid`、`empty`、`duplicate_only`、`error`。
指标分别记录拉取结果次数、合法候选数和新入池代理数,不按 Upstream、Proxy、IP、错误
文本或凭据拆分。
- Provider 指标已完成。
- Extraction 只按固定 `result` 聚合请求:`complete`、`partial`、`empty`、
`insufficient`、`idempotency_conflict`、`rate_limited`、`unavailable`、`invalid`、
`error`。计数只记录请求数量和响应交付数量;幂等重放不被误记为新的 Proxy 消费。
- Extraction 指标已完成。
- 容量指标由 Provider 库存对账循环采样,聚合 managed、available、effective、pending
与 active Upstream Gauge。Upstream ID 仅保留在 Controller 进程内用于聚合替换和
任期结束清理,不输出为 Prometheus 标签;读取结果只允许 `success``error`
- 容量指标已完成。
- 进程级致命错误统一经 JSON `slog` 输出敏感属性、错误对象、URL 用户信息和查询
Secret 在写出前脱敏。该出口只用于生命周期边界,不在 Gateway/Distribution 请求热路径
逐条写日志。
## 集群与性能
- 用户补充:高峰可能达到 100,000 请求/秒。
- 数据面采用多 Worker本地不可变代理快照和本地容量计数。
- 同一代理必须由单个 Worker 所有,或由控制面下发容量切片;禁止每请求
访问 Redis 做全局并发计数。
- 控制面集中 Provider 获取、独立限流、singleflight、Leader 选举和快照分发。
Proxy 明细只进入 Redis TTL 活动池,不写 PostgreSQL。
- 副本数必须由单 Worker 实测能力、目标利用率和故障域余量计算。
## 配置模型
顶层包含:`version`、`defaults`、`security`、`gateway`、`distribution`、
`admin`、`metrics`、`storage`、`routing`、`upstreams`。
每个 Upstream 包含:`enabled`、`exposure`、`provider`、`api`、`proxyAuth`、
`pool`、`capacity`、`lifecycle`、`fetch`、`check`。
Routing 自上而下匹配,首条命中停止;支持 Gateway 与 Extract 两种 purpose
策略至少包括 sequential、random、roundRobin、weighted、leastConnections。
## 必测场景
- 连续 4 次空后成功不得切换;连续 5 次空只切换一次。
- 100 个并发缺池请求只触发有限次 Provider fetch。
- 并发切换不能从 A 一次跳到 C。
- 并发 fetch 不得突破 pool.maxSize 或 fetch.maxTotal。
- TTL safety margin 内不得分配。
- Gateway 与 Extract 共享池时不得容量超卖或重复提取。
- 配置原子热更新期间请求不中断。
- Provider 超时不得计入 Empty Fetch。
- 重复代理不得重复入池,也不得触发空结果切换。
- 所有 Upstream 不可用时按显式策略执行。
## Checker 与健康归并事实2026-07-29
- Checker 是可水平扩展的事实采集进程,只执行有截止时间的探测并上报
Observation只有 Controller Reducer 能改变权威 Proxy/目标健康状态。
- 健康层级为 BASIC、EGRESS、TARGET。新 Proxy 必须先完成全局基础检查;目标级
失败只影响对应 Routing/Target Profile不得把全局仍健康的 Proxy 淘汰。
- 全局状态规则固定为:首次有意义失败进入 SUSPECT连续失败达到
`maxConsecutiveFailures` 后进入 UNHEALTHY复检成功恢复 AVAILABLE。
- 调度不能为每个 Proxy 建立常驻 goroutine 或无界队列;必须稳定分散任务、加入
jitter、限制 `maxInFlight`,并按 FETCHED、SUSPECT、AVAILABLE 的顺序优先。
- 现有 `activitypool.HealthStore.ApplyHealth` 只能提交最终状态、时间和延迟,不能
原子维护连续失败计数或目标级 Profile新模块需要将“纯 Reducer 决策”和
“活动池原子提交”分离,通过公用窄接口复用 Memory/Redis 行为契约。
- `api/proto/controlplane/v1/controlplane.proto` 已定义 CheckTask、CheckLevel 和
HealthObservation后续 Go 领域类型必须保持字段语义一致,但不直接依赖生成的
transport 类型。
## 健康故障移除实现2026-08-02
- `health.GlobalState` 和 Redis 活动记录都保存首次 `UNHEALTHY` 时间;后续
`UNHEALTHY -> CHECKING -> UNHEALTHY` 复检不会重置,成功恢复 `AVAILABLE` 才清除。
- `check.unhealthyRemoveAfter` 默认 `0s`,按 Effective Check 为每个启用 Upstream
生效。Controller Reaper 读取当前配置快照,以固定批次调用公用
`activitypool.UnhealthyRemover`,不进入 Gateway 热路径。
- Redis 使用 `{activity}` 槽内的有序索引扫描到期候选Memory 参考实现保持相同
`SweepUnhealthy` 契约。两者只删除无 Worker ownership 的记录;仍归属 Worker 的
候选会延后一秒并在受限回执中返回 assignment epoch 与首次异常时间。Reaper 通过公共
条件式 Drain 再次核验记录仍为 `UNHEALTHY`、时间戳和归属均未变化,才创建 Ticket。
- Health、Upsert、Extract、Ownership、状态读取与过期清理脚本都在同一原子删除边界
维护该索引。Memory 单测、Redis 集成契约和配置/Reaper 单测覆盖恢复、阈值、延后与
Drain/ACK 后清理。
## Worker Snapshot 刷新2026-08-02
- `OwnedSnapshotSource` 每次调用只构建一份权威完整快照;新增
`RefreshingSnapshotSource` 以有效期一半为上限持续调用该窄接口,并传递最新版本和
checksum。短于全局 `maxStaleAge` 的 Proxy/ownership 租约会进一步缩短下一次刷新间隔。
- gRPC Handler 每次成功发送完整 Snapshot 都重置服务端到期计时器;流中刷新失败或中断时
Gateway 使用既有 `SessionSupervisor` 退避重连。Gateway 本地 Store 对从完整 Snapshot
消失且仍有 Active/Reserved 的 Proxy 已按 draining 继续上报。Redis Runtime 替换已在当前
session ACK、Ticket 屏障和完整零计数同时成立时自动释放 ownerReaper 已对持续
`UNHEALTHY` 的已归属代理调用条件式 Drain。配置停用也会发布有效的上游策略视图
从上游 owned 索引有界选择候选;候选携带上游 revisionRedis 在创建 Drain Ticket 前
再次核验策略仍停用、Proxy source、Worker 和 assignment epoch避免旧候选跨启停使用。
- Snapshot 的 `version` 是同一 Worker 流的连续序列,`ownership_epoch` 是独立且只能前进
的权威栅栏epoch 变化不重置 version。此前 Gateway 错把 epoch 前进要求为 version=1
与 Controller 的 `lastAppliedVersion+1` 生成规则冲突,现已用连续版本规则统一。
- `SnapshotRefreshBroker` 是 Controller 进程内的公共扇出接口:已提交的 Upstream 启停、
Routing 切换和配置发布各向每条本地 Worker 流发送一个可合并刷新信号。Routing 不记录
单代理归属,不能按单条 Routing 对共享 Upstream 强制 DrainSequential 切换只改变后续
新分配,旧 Proxy/连接由原有 Snapshot 与运行态自然排空。其他 Controller 副本继续在
固定刷新周期内读取 PostgreSQL 权威管理态并收敛。
## Drain Ticket2026-08-02
- 原有 `BeginDrain` 只会把 Proxy 从 Worker 下发索引移除,无法让后续步骤区分“已发起
撤销”与“Gateway 已收到排除该 Proxy 的完整快照”。现已增加按 Worker 有界读取的持久化
Ticket并以 RequiredSnapshotEpoch 强制下一份权威快照至少跨过 Drain 操作。Handler 在
Snapshot 引用登记成功后才绑定屏障,绑定内容包含 session、version、epoch 与 checksum。
- Ticket 不能单独成为释放依据。`replace_report` Lua 已把 Ticket 屏障、当前 session ACK
和完整 Runtime 替换中的零计数置于同一原子边界;只有报告缺失该 Proxy 或其
Active/Reserved 均为零时,才删除 owner/Ticket/Drain 索引并恢复可分配状态。
## Git 同步事实2026-07-29
- PostgreSQL 管理面基础文档已提交为 `7951c29`
- 推送远端时返回 `Authentication failed`;没有重复相同失败操作,本地提交保持
完整,待 Git 凭据恢复后同步。
## Routing/Sequential 验收审计2026-07-29
- 五种 Routing 策略已有独立领域实现和单测,但 `Rule` 未携带 Strategy/
OnUnavailableGateway 仍把全部 Upstream 交给一个全局轮询 Dispatcher配置
策略尚未影响真实请求链。
- `onUnavailable` 已严格校验 reject/wait/direct但 Gateway 无候选时统一返回
503wait/direct 和默认 reject 尚未形成运行时闭环。
- Sequential 当前版本 CAS 只在单进程内生效,构造时总从第一个 Upstream 开始;
PostgreSQL 管理态尚未接入游标恢复和跨实例 CAS。
- 对话最终语义已确认并落实:单 Upstream Sequential 启动校验失败,
`endBehavior` 省略时默认 `stop`disabled candidate 的运行时推进仍待实现。
## Proxy Capacity 验收审计2026-07-29
- 每个 Proxy ID 已有独立打包原子计数,固定 Max 下 1,000 并发不会超卖;这满足
当前 Gateway 热路径的基本预留不变量。
- Reservation 已补齐 Cancel、重复终结、错误顺序和并发 Commit/Cancel/Release
的领域测试Gateway 仍会忽略 Release/Cancel 错误,尚无低基数不变量观测 seam。
- `SetMax` 与 counters 分离更新;降到当前占用以下时会出现 overcommitted 状态,
需要先确定“拒绝降容”或“允许排空”的正式契约。
- Snapshot Store 永久保留见过的 Proxy ID 对应 Capacity短 TTL、高换 IP 场景下
需要排空后回收,避免运行态注册表长期增长。
## Routing Runtime 设计输入2026-07-29
- `对话内容.md` 最后一个明确结论将 Sequential `endBehavior` 默认设为 `stop`
领域构造器、配置校验与配置参考现已统一,且拒绝单 Upstream Sequential。
- 产品安全默认已确定为 `onUnavailable=reject`,但当前严格配置要求字段必填;
需要确认省略时自动补 reject还是继续拒绝启动。
- disabled Upstream 的确定语义是停止 Fetch、健康检查和新分配已有 Proxy/连接
自然排空Sequential 是临时跳过还是永久推进尚需固化。
- `direct` 对 Gateway 表示经过目标安全策略后绕过代理直连;对独占提取没有可返回
的 Proxy推荐在 `purpose=extract` 时拒绝 `direct` 配置。
- 推荐采用混合 Routing RuntimeController 维护 Sequential 权威游标和跨实例
CASGateway 消费不可变路由快照并在本地执行 random/roundRobin/weighted/
leastConnections热路径不访问 Redis/PostgreSQLDistribution 在 Controller
内复用同一编译策略并交给 Redis 原子提取执行。
- Gateway 当前没有完整进程装配,`RulesRouter` 仅存在于领域/Handler 测试;
Distribution 只接受调用方 `allowedUpstreams`,尚未把 `purpose=extract` 路由与
Client 许可集求交。
## 架构证据一致性审计2026-07-29
- `.github/workflows/ci.yml` 已覆盖 Windows/Linux vet、unit、build 及 Linux race
`scripts/verify.ps1` 也覆盖格式、vet、60 秒单测、条件 race 和 build因此两项
基础工程验收应计为完成。
- OpenAPI 通过 Go 测试执行结构/路径/响应码检查Protobuf descriptor 曾手工编译
通过,但 CI 没有安装/调用 `protoc`;“在 CI 编译 descriptor”仍未完成。
- `cmd/proxy-gateway`、`proxy-controller`、`proxy-checker`、`proxy-loadgen` 均不存在;
Docker/Kubernetes 当前只是目标拓扑,不能作为构建产物或运行验证证据。
- Provider Reconciler 的合并通知和请求约束已完成,但没有 Redis Leader Adapter
或多实例锁测试FETCH-004 仍是未完成要求。
- Checker/Health 当前只有配置、Protobuf 和 Proxy 状态迁移骨架,没有 Scheduler、
Reducer 或目标 ProfileHEALTH-001..003 不能记为已实现。
- 当前没有 Prometheus 指标模块或描述符测试OBS-001 只有文档约束,运行时证据
缺失。
- Available Slots 当前纳入 AVAILABLE、TTL、安全余量、Max、Active、Reserved
ownership、route/target health 与 Gateway reserve 尚未进入同一聚合模型。
## 文档可执行性审计2026-07-29
- 文档相对链接人工扫描通过,但原先缺少持续门禁;新增 `docs` 包契约测试,统一
扫描 README、docs、deploy 与 diagrams防止链接随文件调整后失效。
- 配置参考曾把尚不存在的 `cmd/proxy-controller` 作为推荐启动命令;现改为真实
可执行的 `deploy/tools/configcheck`,并明确生产 Controller 入口仍是计划能力。
- 文档中的具体 `go run`/`go build` 目标现在必须存在;包含 `...` 的通配包命令
由 Go 工具链自身解析并在完整验证中执行。
## PostgreSQL 18 Fixture 审计2026-07-29
- 集成 Compose 已新增仅绑定 `127.0.0.1:15432``postgres:18-alpine` 服务,
数据目录挂载为 `/var/lib/postgresql` tmpfs不声明测试持久卷。
- Redis 测试脚本现使用专属 Compose 项目并只启动 Redis防止未来加入的
PostgreSQL fixture 被无关测试启动或清理。
- 生产 `docker-compose.yml` 仍把 PostgreSQL 18 命名卷挂在旧路径
`/var/lib/postgresql/data`;直接修改可能影响已有本地数据,必须配套迁移步骤后
单独处理,当前不能把生产持久化拓扑视为已验证。
## 控制面 mTLS 身份审计2026-08-07
- X.509-SVID 叶证书承载单一工作负载身份Gateway/Checker 启动时的自动身份派生与
Controller 授权必须都拒绝包含多个 URI SAN 的证书,即使其中只有一个 URI 与当前角色匹配。
- 解析边界已统一到 `workerruntime.SingleSPIFFEIdentity`:精确匹配信任域、环境、角色和
标识符,拒绝用户信息、端口、查询、片段、转义路径、额外 URI SAN 与跨角色复用。
- TLS 校验可能形成多条等价验证链;授权只检查同一叶证书一次,不能将链路数误判为多个身份。
- Windows 本地 Go 运行环境为 `CGO_ENABLED=0` 且没有 C 编译器Docker Engine 可用,但
`golang:1.26-bookworm``debian:bookworm-slim` 未缓存Docker Desktop HTTPS 代理也不可用。
因此已完成 Go 全量、Compose 静态和 Kustomize 静态验证,容器端到端验证仍待具备镜像网络的环境。
## OpenAPI 契约审计2026-08-07
- Go 结构契约继续锁定本地引用、operationId、响应与认证引用额外以固定
`@redocly/cli@2.25.4` 对两份 OpenAPI 3.1 文档执行标准验证,避免只依赖自定义遍历器。
- 标准最小规则集中的 Tag 描述已提升为错误Distribution、Health、Status、Audit、Upstreams、
Routing 和 Configuration 标签均有面向 API 使用者的稳定说明。
## Docker 构建上下文审计2026-08-07
- Dockerfile 构建阶段需要 `COPY . .`,因此根 `.dockerignore` 是隔离本地工作区与镜像上下文的
必要边界。它显式排除 Git 元数据、环境文件、本地配置、控制面证书/私钥、PEM/CRT/KEY 文件及
构建和测试产物;部署测试会在清单被删除或放宽时失败。
## 部署静态门禁审计2026-08-07
- GitHub Actions 的 Deployment job 在同一组无敏感值 fixture 下渲染生产 Compose、测试 Compose
以及 Kubernetes base/development mTLS Overlay并对两份配置运行严格校验它仍不替代
需要镜像网络和真实服务的容器端到端测试。
## Kubernetes 配置发布审计2026-08-07
- development mTLS Overlay 的 ConfigMap 采用 Kustomize 内容哈希,渲染契约要求三个工作负载
都引用同一版本化名称,因此配置变更进入 Pod 模板并触发滚动。外部固定名称 Secret 不含内容哈希,
它们的轮换需要平台侧 reloader 或受控显式滚动;这一限制已写入运行手册。
## Linux 镜像构建审计2026-08-07
- GitHub Actions 现在在 Linux Runner 执行 Dockerfile 多阶段构建,覆盖目标平台 Go 编译和最小
运行时层。该 job 依赖 Runner 镜像网络;它不启动 Compose 服务,因此不能作为 mTLS、存储、
探针或优雅停机端到端证据。