线上 P24 验收暴露出高频 kubectl exec 放大 K3s API 压力、门禁查询挤占关键连接池,以及 PostgreSQL 锁超时被误判为租约所有权丢失。 本次合并验收身份查询、在租约有效期内重试瞬态续期错误、修复人工审核残留 attempt,并增加滚动后 etcd 稳定窗口、节点直连指标和双站独立报告。 验证:Go 全量测试、go vet、聚焦 race、gofmt、迁移安全检查、bash -n、ShellCheck、manual release test。
104 lines
2.7 KiB
Go
104 lines
2.7 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/easyai/easyai-ai-gateway/apps/api/internal/config"
|
|
)
|
|
|
|
func TestAdmittedTaskRunModeReservesAcceptanceAndProductionSimulation(t *testing.T) {
|
|
server := &Server{cfg: config.Config{AppEnv: "production"}}
|
|
for _, runMode := range []string{"simulation", "SIMULATION", "acceptance", "acceptance_canary"} {
|
|
_, err := server.admittedTaskRunMode(taskTrafficAdmission{}, map[string]any{"runMode": runMode})
|
|
var trafficErr *taskTrafficError
|
|
if !errors.As(err, &trafficErr) || trafficErr.Status != 403 {
|
|
t.Fatalf("run mode %q error=%v", runMode, err)
|
|
}
|
|
}
|
|
for _, runMode := range []string{"acceptance", "acceptance_canary"} {
|
|
got, err := server.admittedTaskRunMode(taskTrafficAdmission{RunMode: runMode}, map[string]any{
|
|
"runMode": "production",
|
|
})
|
|
if err != nil || got != runMode {
|
|
t.Fatalf("admitted mode=%q got=%q err=%v", runMode, got, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|