BgFilter 父侧连接失败有界重试,跨过 worker 重启窗口
主机重启 / worker 崩溃拉起期间,父侧对 127.0.0.1 worker 的 TCP 连接 失败此前直接映射 internal_error:complex(max_attempts=1)终态失败不可 自愈,flat 被迫降级。现仅对连接从未建立的失败(无副作用、天然幂等)做 有界退避重试:每轮按公式重算 maxQueueWaitMs,不突破「预算不足不发送」 不变量;complex 用完整序列(约 22.5s,覆盖 RestartSec=5s + 启动窗), flat 只取前 2 项(≤1.5s,不侵蚀 39s fallback 预留)。收到任何 HTTP 响应(含 5xx)立即停止重试;新增 bgfilter_internal_connect_retry_total 指标。设计文档与数据契约同步刻出「已建立连接后不重试」禁令的精确例外。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -227,6 +227,8 @@ parent client timeout = maxQueueWaitMs + callBudgetMs + 2s 传输窗
|
||||
|
||||
`maxQueueWaitMs <= 0` 时父侧不得发送请求:flat 直接进入既有“阿里云 → 本地”fallback,complex 直接失败;不允许把注定超时的请求塞进队列。flat 扣除的 `39s` 父侧预留由 `37s` fallback 窗口和 `2s` 内部响应传输窗组成;complex 没有 fallback,只保留 `2s` 传输窗。
|
||||
|
||||
父侧对「TCP 连接从未建立」的失败(worker 重启、主机开机排序窗口内的连接拒绝 / 不可达 / connect 阶段超时)做有界自动重试:这类请求从未进入 worker admission,无副作用、天然幂等。每轮重试前按上式重算 `maxQueueWaitMs`,重试消耗的是父预算的自然余量,不突破「预算不足不发送」的不变量;退避序列本身有界——complex 用完整序列(总额约 `22.5s`,按上界覆盖 systemd `RestartSec=5s` + 进程启动窗口,更长的停机应快速失败而非挂住父 job),flat 只取前 2 项(额外延迟 `≤1.5s`,不侵蚀 `39s` fallback 预留)——父无绝对 deadline 时也不会无限等待。收到任何 HTTP 响应(含 5xx)或其它错误类别一律不重试,边界见 §7.1。
|
||||
|
||||
queue job 的总预算从父 job 开始执行时起算,不从开始申请 BgFilter 时重新计时。现有父 worker 还会把 provider deadline 设在 job deadline 前 `60s`,为最终写回和终态保留时间。同步 RPC 实现必须显式读取父侧剩余 provider budget 派生 `maxQueueWaitMs`,不能忽略 `RequestContext` deadline。
|
||||
|
||||
本版没有 BgFilter 子任务等待 claim 的阶段。几个起算点必须区分:父 job 在数据库中尚未被 claim 的等待不消耗 job 执行预算;父 job 开始实际执行后,生图及 BgFilter 之前的耗时都会消耗父总预算;父内部 HTTP client timeout 从开始发送请求起覆盖 loopback 传输、worker admission、排队、provider 和回包;`callBudgetMs` 计时只在子 worker 取得 provider permit 后启动,attempt timer 只在真正开始一次 provider HTTP 时启动。父侧不是放弃超时,而是不再直接执行 provider 单次 attempt 的计时器。
|
||||
@@ -335,7 +337,7 @@ flat / complex 统一使用的 `GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_FAILURE_THRE
|
||||
|
||||
### 7.1 内部 RPC 断连
|
||||
|
||||
- 父侧不得自动重试整次内部 HTTP。断连时结果未知,重试可能让一次逻辑调用从最多两次 provider attempt 扩大为四次,并可能突破瞬时并发预期。
|
||||
- 连接已建立后的断连不得自动重试整次内部 HTTP:此时结果未知,重试可能让一次逻辑调用从最多两次 provider attempt 扩大为四次,并可能突破瞬时并发预期。唯一例外是 TCP 连接从未建立的失败(连接拒绝 / 不可达 / connect 阶段超时):请求从未进入 worker admission,结果确定为「未发生」,父侧按 §5.1 的预算约束有界退避重试,用于跨过 worker 重启与主机开机排序窗口;收到任何 HTTP 响应后即回到本条禁令。
|
||||
- 尚在等待 permit 的请求到达自身 deadline 后必须取消,不再发送 provider 请求;运行时若能可靠观察客户端断连,也可提前取消,但正确性不能只依赖断连事件。
|
||||
- 已经开始的 provider attempt 必须继续读取到完成或本次 attempt timeout,并持有 permit;可观察到的 handler / client drop 只丢弃最终结果,不能让已启动请求变成无人管理的本地 future。
|
||||
- deadline 已被子 worker 观察到后,不再开始第二次 attempt。首版没有显式 cancellation signal 通道;单纯 TCP 断连只能 best-effort 阻止二试(handler future 被 drop 后自然不再开始新 attempt),Axum / Hyper 不保证立刻通知 handler,因此不能承诺所有断连都阻止二试,排队阶段的 queue timeout 与 permit 后的 `callBudgetMs` deadline 是最终可靠的停止条件。
|
||||
@@ -345,7 +347,7 @@ flat / complex 统一使用的 `GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_FAILURE_THRE
|
||||
### 7.2 进程崩溃
|
||||
|
||||
- 父进程崩溃:内部连接最终断开,父 job 按现有 heartbeat、lease、`max_attempts = 1`、失败和退款语义收口。
|
||||
- 子 worker 崩溃或重启:当前内部 RPC 失败;父侧不查询、不恢复、不重发同一次 RPC。
|
||||
- 子 worker 崩溃或重启:已在途的内部 RPC 失败;父侧不查询、不恢复、不重发同一次 RPC。重启窗口内连接从未建立的新调用按 §7.1 的例外有界重试。
|
||||
- 子 worker 成功但响应在网络中丢失:结果视为未知;flat 进入原 fallback,complex 失败。
|
||||
- 子 worker 不得反向 complete / fail 父 job,也不得写画布、业务资源或账单。
|
||||
|
||||
@@ -406,6 +408,7 @@ BgFilter 成功二进制不是一份新的业务资产:
|
||||
- `bgfilter_internal_waiting_requests`(即 admission 后等待 `N` permit 的队长)
|
||||
- `bgfilter_internal_queue_timeout_total{bound}`(排队超时按触发边界 `estimate | parent` 分维度)
|
||||
- `bgfilter_internal_call_budget_drift_total{mode}`(请求 `callBudgetMs` 与本进程公式值不一致;发布窗口内短暂非零正常,持续增长说明父子 N / est 真漂移)
|
||||
- `bgfilter_internal_connect_retry_total{mode}`(父侧连接失败重试次数;worker 重启窗口内短暂非零正常,持续增长说明 worker 长期不可达)
|
||||
- `bgfilter_internal_in_flight`
|
||||
- `bgfilter_internal_request_seconds{mode,outcome}`
|
||||
- `bgfilter_provider_http_seconds{mode,attempt,outcome}`
|
||||
@@ -466,7 +469,7 @@ flat / complex 的每次 provider 失败审计都必须留在子 worker,保留
|
||||
- `maxQueueWaitMs <= 0` 时父侧不发送请求:flat 直接 fallback,complex 直接失败。
|
||||
- `callBudgetMs` 与 worker 本进程公式值不一致时不拒绝:worker 以自身公式值执行,记 warn 并递增漂移指标;发布调优 N / est 的新旧进程共存窗口内,在途 flat 任务仍能正常执行或走既有 fallback,不得因瞬态漂移触发 `invalid_request`(该码禁止 fallback)。attempt、callBudget、client timeout 全部由 `N / est` 运行时派生,代码不存在硬编码结果值。
|
||||
- parent client timeout 精确取 `maxQueueWaitMs + callBudgetMs + 2s`,helper 保持 infallible;父绝对预算通过 `maxQueueWaitMs` 的派生公式预先约束,结果校验等待也必须 deadline-aware,不能只在校验完成后事后判超时。
|
||||
- 第一次失败后预算不足时不开始第二次;父侧从不重试整次内部 RPC。
|
||||
- 第一次失败后预算不足时不开始第二次;父侧从不重试已建立连接的内部 RPC。连接从未建立的失败按 §5.1 有界退避重试:仅 connect 类失败重入、收到任何 HTTP 响应立即停止、每轮重算 `maxQueueWaitMs`、complex / flat 各自的退避配额封顶(Rust 集成测试覆盖「重试跨过监听空窗后停在首个 HTTP 响应」「配额耗尽返回 connect 失败」「deadline 放不下下一轮时不空睡」三条路径)。
|
||||
- 父业务预算仍有效时,flat 两次失败、熔断、overload、内部 RPC deadline 或断连仍走“阿里云 → 本地”;complex 任意失败或自身熔断都直接失败,不接 flat fallback。
|
||||
- flat / complex 分别按自身真实失败 attempt 计数且状态互不影响;由剩余业务预算截短的 timeout 不计入。两种模式都在 permit 前二次检查;已获准调用可完成第二次,后续同模式排队请求快速 `circuit_open`。
|
||||
- `cancelled`(仅验证父侧映射,保留码首版不产生)、父 cancellation / 绝对 deadline、`invalid_request` 和 `unauthorized` 不启动 flat fallback;其它 flat 错误只在父业务预算仍有效时进入 fallback。
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user