fix(voice): M47 r2 复审两条 P2 —— 关闭期取消不拉起、非法探活地址不启用
复审 p2-2items(P2-1 跨进程去重按裁决只报不修):
(a) **关闭期 ctx 取消 ≠ 没起**:后端关闭时 ctx 被取消,探活返回
`context.Canceled`;原实现把它判成"没起" ⇒ **在关闭过程中拉起 TTS**。
现在按 `errors.Is(err, context.Canceled)` 判为跳过:不记 markAttempt(不消耗
冷却)、不拉、也不 WARN 成故障。探活**自身超时**(DeadlineExceeded)仍是"没起",
照常拉 —— 两者用 errors.Is 区分,不按文案。新增 OutcomeCanceled。
(b) **探活地址必须合法**:写成 `127.0.0.1:9880`(漏 scheme)会让 http.NewRequest
每次都失败 ⇒「没起」永远成立 ⇒ 每次启动都拉一次进程。新增
`ttslaunch.ValidateProbeURL`(url.Parse + scheme ∈ {http,https} + host 非空):
- `New` 里校验 ⇒ 不合法即 WARN + 不启用(兜底闸,覆盖任何调用方);
- 组合根 `validateResolvedProbeURL` 里同一把闸再校验一次,WARN 带
`probe_url_source` 指明该改哪个文件 —— **baseUrl 兜底那条路也被覆盖**。
顺带按复审记录项 1 修正 `Proxy: nil` 的注释:net/http 本来就不给回环 IP 走代理,
这行真正保护的是 probeUrl 被配成主机名的情况(原注释高估了它)。
用例(均变异验证打红):
- TestCheck_ParentCanceled_SkipsLaunch:取消 ⇒ OutcomeCanceled + 0 次拉起 + 不进冷却
(变异:跳过改回"没起" ⇒ Outcome=launched、marker 落地)
- TestCheck_ProbeOwnTimeout_StillLaunches:探活自身超时仍照常拉
- TestNew_InvalidProbeURL_Disables:7 种非法形状 ⇒ 不接单、0 次拉起、有 WARN
- TestStartVoiceTTSLaunch_{ShutdownCancelDoesNotLaunch,InvalidProbeURLStaysOff,
InvalidBaseURLFallbackStaysOff}:组合根三条,均用真 .bat marker 观察"有没有拉起"
This commit is contained in:
1 parent
faf18afb91
commit
67d7e4bd1b
6 files changed
+357
-2
No files matched your search
@@ -20,6 +20,7 @@ package ttslaunch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -85,6 +86,9 @@ const (
|
||||
OutcomeCooling
|
||||
// OutcomeFailed 表示要拉但没拉起来(脚本不存在/起进程失败),已记 WARN。
|
||||
OutcomeFailed
|
||||
// OutcomeCanceled 表示探活被**调用方的 ctx 取消**打断(典型是后端正在关闭):
|
||||
// 这不是"没起",而是"这次不看了" —— 跳过、不拉起、也不消耗冷却。
|
||||
OutcomeCanceled
|
||||
)
|
||||
|
||||
// String 让日志与用例输出可读("outcome":"launched")。
|
||||
@@ -100,6 +104,8 @@ func (o Outcome) String() string {
|
||||
return "cooling"
|
||||
case OutcomeFailed:
|
||||
return "failed"
|
||||
case OutcomeCanceled:
|
||||
return "canceled"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
@@ -190,6 +196,17 @@ func New(cfg Config, opts ...Option) *Launcher {
|
||||
opt(l)
|
||||
}
|
||||
}
|
||||
// 🔴 探活地址是一道**闸**(复审 P2-2b):写成 `127.0.0.1:9880` 这种漏了 scheme
|
||||
// 的形状,`http.NewRequest` 每次都失败 ⇒「没起」永远成立 ⇒ **每次启动都拉一次
|
||||
// 进程**(只有进程内冷却挡着)。宁可不启用,也不做这种自动重试机器。
|
||||
//
|
||||
// 校验放在这里而不是只放组合根:`voice-tts.json` 的 `baseUrl` 兜底同样要过这道
|
||||
// 闸,而那条路也是流进同一个 Config 的 —— 一处校验覆盖两个来源。
|
||||
if err := ValidateProbeURL(l.cfg.ProbeURL); err != nil {
|
||||
l.log.Warn("emotionTTS auto-launch disabled: invalid probe url",
|
||||
"url", l.cfg.ProbeURL, "err", err)
|
||||
l.cfg.Enabled = false
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
@@ -246,6 +263,17 @@ func (l *Launcher) Check(ctx context.Context) Outcome {
|
||||
l.log.Debug("emotionTTS already listening", "url", l.cfg.ProbeURL)
|
||||
return OutcomeAlreadyRunning
|
||||
}
|
||||
// 🔴 **调用方取消 ≠ 没起**(复审 P2-2a):后端关闭时 ctx 被取消,探活随之返回
|
||||
// `context.Canceled`;若把它当"没起",就会在**关闭过程中拉起 TTS**。这条必须是
|
||||
// 跳过 —— 不记 markAttempt、不拉、也不 WARN 成故障(正常关闭不是故障)。
|
||||
//
|
||||
// 判据只用 errors.Is,不按文案:探活**自身超时**是 context.DeadlineExceeded,
|
||||
// 那支仍然是"没起"(继续往下拉)。
|
||||
if errors.Is(probeErr, context.Canceled) {
|
||||
l.log.Debug("emotionTTS probe canceled by caller, skipping launch",
|
||||
"url", l.cfg.ProbeURL, "err", probeErr)
|
||||
return OutcomeCanceled
|
||||
}
|
||||
l.log.Debug("emotionTTS not listening", "url", l.cfg.ProbeURL, "err", probeErr)
|
||||
// 先记时刻再起进程:两个检查若都走到这里,冷却已经把后来者挡住(配合
|
||||
// checkMu,这一段的判定与记录是同一步)。
|
||||
|
||||
Reference in new issue
Block a user