feat(db,server): T20-S2 hardening batch B1/B4/B7 (XFF trust, pool statement_timeout, shutdown drain)
This commit is contained in:
+25
-1
@@ -125,11 +125,35 @@ func main() {
|
||||
if eventsH != nil {
|
||||
eventsH.Stop()
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
// T20-B7:drain 窗口与 AGENT_TIMEOUT 量级对齐(默认 min(AgentTimeout, 35m)+30s 缓冲),
|
||||
// 消除「5s Shutdown vs 30m 长任务」错配;可由 SHUTDOWN_DRAIN_TIMEOUT 显式覆盖。
|
||||
drain := shutdownDrainTimeout(cfg)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), drain)
|
||||
defer cancel()
|
||||
fmt.Printf("等待 in-flight 请求/长任务排空(drain 上限 %s)...\n", drain)
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "服务停止异常: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("服务已安全退出。")
|
||||
}
|
||||
}
|
||||
|
||||
// shutdownDrainTimeout 解析 T20-B7 drain 窗口:
|
||||
// - SHUTDOWN_DRAIN_TIMEOUT 显式设置则优先(校验 >0);
|
||||
// - 否则默认 min(AGENT_TIMEOUT, 35m) + 30s 缓冲(compose stop_grace_period 需 ≥ 此值)。
|
||||
func shutdownDrainTimeout(cfg *config.Config) time.Duration {
|
||||
if v := os.Getenv("SHUTDOWN_DRAIN_TIMEOUT"); v != "" {
|
||||
if d, err := time.ParseDuration(v); err == nil && d > 0 {
|
||||
return d
|
||||
}
|
||||
}
|
||||
at := 30 * time.Minute
|
||||
if cfg != nil && cfg.AgentTimeout > 0 {
|
||||
at = cfg.AgentTimeout
|
||||
}
|
||||
cap := 35 * time.Minute
|
||||
if at > cap {
|
||||
at = cap
|
||||
}
|
||||
return at + 30*time.Second
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"chat-agent-system/internal/config"
|
||||
)
|
||||
|
||||
// T20-B7 回归:drain 窗口与 AGENT_TIMEOUT 对齐。
|
||||
func TestShutdownDrainTimeout_DefaultCapped35m(t *testing.T) {
|
||||
cfg := &config.Config{AgentTimeout: 60 * time.Minute} // 超过上限 → 封顶 35m
|
||||
got := shutdownDrainTimeout(cfg)
|
||||
want := 35*time.Minute + 30*time.Second
|
||||
if got != want {
|
||||
t.Fatalf("expect cap %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownDrainTimeout_FollowsShortAgentTimeout(t *testing.T) {
|
||||
cfg := &config.Config{AgentTimeout: 10 * time.Minute} // 短于上限 → 跟随 +30s
|
||||
got := shutdownDrainTimeout(cfg)
|
||||
want := 10*time.Minute + 30*time.Second
|
||||
if got != want {
|
||||
t.Fatalf("expect %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownDrainTimeout_EnvOverride(t *testing.T) {
|
||||
t.Setenv("SHUTDOWN_DRAIN_TIMEOUT", "45s")
|
||||
cfg := &config.Config{AgentTimeout: 30 * time.Minute}
|
||||
if got := shutdownDrainTimeout(cfg); got != 45*time.Second {
|
||||
t.Fatalf("env override expect 45s, got %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownDrainTimeout_InvalidEnvFallsBack(t *testing.T) {
|
||||
t.Setenv("SHUTDOWN_DRAIN_TIMEOUT", "not-a-duration")
|
||||
cfg := &config.Config{AgentTimeout: 30 * time.Minute}
|
||||
want := 30*time.Minute + 30*time.Second
|
||||
if got := shutdownDrainTimeout(cfg); got != want {
|
||||
t.Fatalf("invalid env expect fallback %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownDrainTimeout_NilCfg(t *testing.T) {
|
||||
got := shutdownDrainTimeout(nil)
|
||||
want := 30*time.Minute + 30*time.Second
|
||||
if got != want {
|
||||
t.Fatalf("nil cfg expect default %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user