fix(queue): 移除未配置的 GPT 图像并发上限
This commit is contained in:
@@ -118,3 +118,123 @@ WHERE id = '20000000-0000-0000-0000-000000000001'`).Scan(&normalizedPlatformPoli
|
|||||||
t.Fatalf("normalized platform policy=%s, want={}", normalizedPlatformPolicy)
|
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;
|
||||||
Reference in New Issue
Block a user