fix(queue): 移除未配置的 GPT 图像并发上限

This commit is contained in:
2026-07-24 17:03:28 +08:00
parent 20945dbbcb
commit 09375bfae7
2 changed files with 138 additions and 0 deletions
@@ -118,3 +118,123 @@ WHERE id = '20000000-0000-0000-0000-000000000001'`).Scan(&normalizedPlatformPoli
t.Fatalf("normalized platform policy=%s, want={}", normalizedPlatformPolicy)
}
}
func TestRemoveUnconfiguredGPTImageConcurrencyLimitMigration(t *testing.T) {
databaseURL := strings.TrimSpace(os.Getenv("AI_GATEWAY_TEST_DATABASE_URL"))
if databaseURL == "" {
t.Skip("set AI_GATEWAY_TEST_DATABASE_URL to run rate-limit migration PostgreSQL integration tests")
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
schema := fmt.Sprintf("remove_gpt_image_limit_%d", time.Now().UnixNano())
adminPool, err := pgxpool.New(ctx, databaseURL)
if err != nil {
t.Fatalf("connect migration test database: %v", err)
}
defer adminPool.Close()
if _, err := adminPool.Exec(ctx, `CREATE SCHEMA `+schema); err != nil {
t.Fatalf("create migration test schema: %v", err)
}
defer adminPool.Exec(context.Background(), `DROP SCHEMA IF EXISTS `+schema+` CASCADE`)
schemaURL, err := url.Parse(databaseURL)
if err != nil {
t.Fatalf("parse test database url: %v", err)
}
query := schemaURL.Query()
query.Set("search_path", schema)
schemaURL.RawQuery = query.Encode()
pool, err := pgxpool.New(ctx, schemaURL.String())
if err != nil {
t.Fatalf("connect migration test schema: %v", err)
}
defer pool.Close()
if _, err := pool.Exec(ctx, `
CREATE TABLE base_model_catalog (
id uuid PRIMARY KEY,
invocation_name text NOT NULL,
default_rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
updated_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO base_model_catalog (id, invocation_name, default_rate_limit_policy, metadata) VALUES
(
'00000000-0000-0000-0000-000000000001',
'gpt-image-2',
'{"rules":[{"metric":"concurrent","limit":5,"leaseTtlSeconds":300}]}',
'{"rateLimitSource":"image-gateway-migration","rateLimitUpdatedAt":"2026-07-24"}'
),
(
'00000000-0000-0000-0000-000000000002',
'gpt-image-2',
'{"rules":[{"metric":"concurrent","limit":6,"leaseTtlSeconds":300}]}',
'{"rateLimitSource":"image-gateway-migration"}'
),
(
'00000000-0000-0000-0000-000000000003',
'gpt-image-2',
'{"rules":[{"metric":"concurrent","limit":5,"leaseTtlSeconds":300}]}',
'{"rateLimitSource":"operator"}'
),
(
'00000000-0000-0000-0000-000000000004',
'gemini-3-pro-image',
'{"rules":[{"metric":"concurrent","limit":10,"leaseTtlSeconds":600}]}',
'{"rateLimitSource":"image-gateway-migration"}'
);`); err != nil {
t.Fatalf("seed pre-migration rows: %v", err)
}
_, currentFile, _, _ := runtime.Caller(0)
migrationPath := filepath.Join(filepath.Dir(currentFile), "..", "..", "migrations", "0082_remove_unconfigured_gpt_image_concurrency_limit.sql")
migration, err := os.ReadFile(migrationPath)
if err != nil {
t.Fatalf("read migration: %v", err)
}
if _, err := pool.Exec(ctx, string(migration)); err != nil {
t.Fatalf("apply migration: %v", err)
}
rows, err := pool.Query(ctx, `
SELECT id::text, default_rate_limit_policy::text, metadata->>'rateLimitSource',
metadata->>'rateLimitRemovalReason'
FROM base_model_catalog
ORDER BY id`)
if err != nil {
t.Fatalf("read migrated rows: %v", err)
}
defer rows.Close()
type result struct {
policy string
source *string
removalReason *string
}
got := make([]result, 0, 4)
for rows.Next() {
var id string
var item result
if err := rows.Scan(&id, &item.policy, &item.source, &item.removalReason); err != nil {
t.Fatalf("scan migrated row: %v", err)
}
got = append(got, item)
}
if err := rows.Err(); err != nil {
t.Fatalf("read migrated rows: %v", err)
}
if len(got) != 4 {
t.Fatalf("migrated %d rows, want 4", len(got))
}
if got[0].policy != "{}" || got[0].source != nil ||
got[0].removalReason == nil || *got[0].removalReason != "upstream-limit-unconfigured" {
t.Fatalf("migration-owned GPT policy not removed: %+v", got[0])
}
if got[1].policy == "{}" || got[1].source == nil || *got[1].source != "image-gateway-migration" {
t.Fatalf("custom GPT policy was changed: %+v", got[1])
}
if got[2].policy == "{}" || got[2].source == nil || *got[2].source != "operator" {
t.Fatalf("operator GPT policy was changed: %+v", got[2])
}
if got[3].policy == "{}" || got[3].source == nil || *got[3].source != "image-gateway-migration" {
t.Fatalf("Gemini policy was changed: %+v", got[3])
}
}
@@ -0,0 +1,18 @@
-- gpt-image-2 platforms do not declare an upstream concurrency limit. The
-- migration-only base default added in 0081 therefore turned an unspecified
-- limit into an artificial per-platform-model cap of five.
--
-- Remove only the exact policy written by 0081. Explicit platform, runtime, or
-- platform-model policies continue to take precedence and remain untouched.
UPDATE base_model_catalog
SET default_rate_limit_policy = '{}'::jsonb,
metadata = (COALESCE(metadata, '{}'::jsonb) - 'rateLimitSource' - 'rateLimitUpdatedAt')
|| jsonb_build_object(
'rateLimitRemovedAt', '2026-07-24',
'rateLimitRemovalReason', 'upstream-limit-unconfigured'
),
updated_at = now()
WHERE invocation_name = 'gpt-image-2'
AND metadata->>'rateLimitSource' = 'image-gateway-migration'
AND default_rate_limit_policy =
'{"rules":[{"metric":"concurrent","limit":5,"leaseTtlSeconds":300}]}'::jsonb;