feat(acceptance): 增加生产同构媒体压力验收模式
引入动态流量门禁、隔离验收身份与协议级 Gemini/Volces 模拟器,覆盖双站点 API、Worker、PostgreSQL、River、账务、回调和媒体物化链路。 新增 P24/P28/P32 容量阶梯、Worker 强杀恢复、真实小流量 canary、CAS 放量和失败保持 validation 的生产编排;Worker 执行槽、连接池、媒体并发和双站点副本数改为环境配置。 验证:Go 全量测试、真实 PostgreSQL 迁移集成测试、迁移安全检查、OpenAPI 生成、ShellCheck、Kustomize、gofmt 和 git diff --check。
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
package acceptanceworkload
|
||||
|
||||
import "time"
|
||||
|
||||
type GeminiProfile struct {
|
||||
Name string
|
||||
Requests int
|
||||
InputBytes int
|
||||
OutputBytes int
|
||||
DelayMin time.Duration
|
||||
DelayMax time.Duration
|
||||
}
|
||||
|
||||
var (
|
||||
GeminiBaseline = GeminiProfile{
|
||||
Name: "gemini-baseline", Requests: 1000, InputBytes: 256 << 10, OutputBytes: 256 << 10,
|
||||
DelayMin: 4 * time.Second, DelayMax: 4 * time.Second,
|
||||
}
|
||||
GeminiLarge = GeminiProfile{
|
||||
Name: "gemini-large", Requests: 128, InputBytes: 2 << 20, OutputBytes: 4 << 20,
|
||||
DelayMin: 8 * time.Second, DelayMax: 15 * time.Second,
|
||||
}
|
||||
GeminiPeak = GeminiProfile{
|
||||
Name: "gemini-peak", Requests: 32, InputBytes: 8 << 20, OutputBytes: 8 << 20,
|
||||
DelayMin: 15 * time.Second, DelayMax: 30 * time.Second,
|
||||
}
|
||||
)
|
||||
|
||||
func GeminiProfileByName(name string) (GeminiProfile, bool) {
|
||||
switch name {
|
||||
case GeminiBaseline.Name:
|
||||
return GeminiBaseline, true
|
||||
case GeminiLarge.Name:
|
||||
return GeminiLarge, true
|
||||
case GeminiPeak.Name:
|
||||
return GeminiPeak, true
|
||||
default:
|
||||
return GeminiProfile{}, false
|
||||
}
|
||||
}
|
||||
|
||||
// VideoImageCount provides a deterministic 60/30/10 split for 3/6/9-image tasks.
|
||||
func VideoImageCount(index int) int {
|
||||
switch index % 10 {
|
||||
case 0, 1, 2, 3, 4, 5:
|
||||
return 3
|
||||
case 6, 7, 8:
|
||||
return 6
|
||||
default:
|
||||
return 9
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package acceptanceworkload
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGeminiProfilesAndVideoDistribution(t *testing.T) {
|
||||
for _, profile := range []GeminiProfile{GeminiBaseline, GeminiLarge, GeminiPeak} {
|
||||
resolved, ok := GeminiProfileByName(profile.Name)
|
||||
if !ok || resolved != profile {
|
||||
t.Fatalf("profile %q resolved to %+v, ok=%v", profile.Name, resolved, ok)
|
||||
}
|
||||
if profile.Requests <= 0 || profile.InputBytes <= 0 || profile.OutputBytes <= 0 ||
|
||||
profile.DelayMin <= 0 || profile.DelayMax < profile.DelayMin {
|
||||
t.Fatalf("invalid Gemini profile: %+v", profile)
|
||||
}
|
||||
}
|
||||
counts := map[int]int{}
|
||||
for index := 0; index < 100; index++ {
|
||||
counts[VideoImageCount(index)]++
|
||||
}
|
||||
if counts[3] != 60 || counts[6] != 30 || counts[9] != 10 {
|
||||
t.Fatalf("video image distribution=%v", counts)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user