fix(acceptance): 隔离控制面抖动与租约瞬态故障

线上 P24 验收暴露出高频 kubectl exec 放大 K3s API 压力、门禁查询挤占关键连接池,以及 PostgreSQL 锁超时被误判为租约所有权丢失。

本次合并验收身份查询、在租约有效期内重试瞬态续期错误、修复人工审核残留 attempt,并增加滚动后 etcd 稳定窗口、节点直连指标和双站独立报告。

验证:Go 全量测试、go vet、聚焦 race、gofmt、迁移安全检查、bash -n、ShellCheck、manual release test。
This commit is contained in:
2026-08-01 01:51:44 +08:00
parent be6ce7f78a
commit 132cda35d8
10 changed files with 495 additions and 56 deletions
@@ -1,8 +1,12 @@
package httpapi
import (
"context"
"errors"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/config"
)
@@ -25,3 +29,75 @@ func TestAdmittedTaskRunModeReservesAcceptanceAndProductionSimulation(t *testing
}
}
}
func TestCachedTaskTrafficAuthorizationCollapsesConcurrentLoads(t *testing.T) {
server := &Server{}
key := taskTrafficAuthorizationCacheKey{runID: "run", apiKeyID: "key", userID: "user"}
var loads atomic.Int32
start := make(chan struct{})
const callers = 64
var wg sync.WaitGroup
errCh := make(chan error, callers)
for range callers {
wg.Add(1)
go func() {
defer wg.Done()
<-start
runID, err := server.cachedTaskTrafficAuthorization(
context.Background(), key, time.Minute,
func() (string, error) {
loads.Add(1)
time.Sleep(10 * time.Millisecond)
return "run", nil
},
)
if err != nil || runID != "run" {
errCh <- errors.New("unexpected cached authorization result")
}
}()
}
close(start)
wg.Wait()
close(errCh)
for err := range errCh {
t.Fatal(err)
}
if got := loads.Load(); got != 1 {
t.Fatalf("authorization loads=%d, want 1", got)
}
if _, err := server.cachedTaskTrafficAuthorization(
context.Background(), key, time.Minute,
func() (string, error) {
loads.Add(1)
return "run", nil
},
); err != nil {
t.Fatal(err)
}
if got := loads.Load(); got != 1 {
t.Fatalf("cached authorization loads=%d, want 1", got)
}
}
func TestCachedTaskTrafficAuthorizationDoesNotCacheFailures(t *testing.T) {
server := &Server{}
key := taskTrafficAuthorizationCacheKey{runID: "run", apiKeyID: "key", userID: "user"}
wantErr := errors.New("database unavailable")
loads := 0
for range 2 {
_, err := server.cachedTaskTrafficAuthorization(
context.Background(), key, time.Minute,
func() (string, error) {
loads++
return "", wantErr
},
)
if !errors.Is(err, wantErr) {
t.Fatalf("authorization error=%v, want %v", err, wantErr)
}
}
if loads != 2 {
t.Fatalf("failed authorization loads=%d, want 2", loads)
}
}