feat(queue): 增加非文本模型分布式准入队列

使用 PostgreSQL 统一同步与异步非文本任务的并发准入、持久化等待和 Worker 容量分配,并将生产 API 与独立 Worker 角色拆分。

补充策略管理、共享契约、OpenAPI、Kubernetes 双节点 Worker 清单及跨节点验收脚本;未默认启用任何生产 queue_size 策略。

已在原基线完成 Go、前端、迁移、Shell、Kustomize 与长任务容量验收;合入最新主干后将重新执行发布门禁。
This commit is contained in:
2026-07-29 16:15:43 +08:00
parent 2e5a90731b
commit 9e4fc7362d
55 changed files with 4565 additions and 237 deletions
@@ -42,6 +42,10 @@ type ModelRateLimitStatus struct {
ModelCooldownUntil string `json:"modelCooldownUntil,omitempty"`
Concurrent RateLimitMetricStatus `json:"concurrent"`
QueuedTasks float64 `json:"queuedTasks"`
WaitingSyncTasks int `json:"waitingSyncTasks,omitempty"`
WaitingAsyncTasks int `json:"waitingAsyncTasks,omitempty"`
OldestQueueWaitSeconds float64 `json:"oldestQueueWaitSeconds,omitempty"`
QueueLimit int `json:"queueLimit,omitempty"`
RPM RateLimitMetricStatus `json:"rpm"`
TPM RateLimitMetricStatus `json:"tpm"`
LoadRatio float64 `json:"loadRatio"`
@@ -151,6 +155,9 @@ func (s *Store) ListModelRateLimitStatuses(ctx context.Context) ([]ModelRateLimi
COALESCE(to_char(m.cooldown_until AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'), ''),
COALESCE(con.active, 0)::float8,
COALESCE(queued.waiting, 0)::float8,
COALESCE(admission.waiting_sync, 0)::int,
COALESCE(admission.waiting_async, 0)::int,
COALESCE(admission.oldest_wait_seconds, 0)::float8,
COALESCE(rpm.used_value, 0)::float8, COALESCE(rpm.reserved_value, 0)::float8, COALESCE(rpm.reset_at::text, ''),
COALESCE(tpm.used_value, 0)::float8, COALESCE(tpm.reserved_value, 0)::float8, COALESCE(tpm.reset_at::text, '')
FROM platform_models m
@@ -192,6 +199,15 @@ LEFT JOIN (
) queued_sources
GROUP BY queued_sources.platform_model_id
) queued ON queued.platform_model_id = m.id::text
LEFT JOIN (
SELECT platform_model_id::text AS platform_model_id,
COUNT(*) FILTER (WHERE mode = 'sync') AS waiting_sync,
COUNT(*) FILTER (WHERE mode = 'async') AS waiting_async,
EXTRACT(EPOCH FROM now() - MIN(enqueued_at)) AS oldest_wait_seconds
FROM gateway_task_admissions
WHERE status = 'waiting'
GROUP BY platform_model_id
) admission ON admission.platform_model_id = m.id::text
LEFT JOIN (
SELECT DISTINCT ON (scope_key) scope_key, used_value, reserved_value, reset_at
FROM gateway_rate_limit_counters
@@ -231,6 +247,9 @@ ORDER BY p.priority ASC, m.model_name ASC`)
var modelCooldownUntil string
var concurrentCurrent float64
var queuedTasks float64
var waitingSyncTasks int
var waitingAsyncTasks int
var oldestQueueWaitSeconds float64
var rpmUsed float64
var rpmReserved float64
var rpmResetAt string
@@ -263,6 +282,9 @@ ORDER BY p.priority ASC, m.model_name ASC`)
&modelCooldownUntil,
&concurrentCurrent,
&queuedTasks,
&waitingSyncTasks,
&waitingAsyncTasks,
&oldestQueueWaitSeconds,
&rpmUsed,
&rpmReserved,
&rpmResetAt,
@@ -287,6 +309,12 @@ ORDER BY p.priority ASC, m.model_name ASC`)
item.ModelCooldownUntil = modelCooldownUntil
item.RateLimitPolicy = policy
item.QueuedTasks = queuedTasks
item.WaitingSyncTasks = waitingSyncTasks
item.WaitingAsyncTasks = waitingAsyncTasks
item.OldestQueueWaitSeconds = oldestQueueWaitSeconds
if queueRule, ok := QueueRuleFromPolicy(policy); ok {
item.QueueLimit = queueRule.Limit
}
item.Concurrent = metricStatus(concurrentCurrent, concurrentCurrent, 0, rateLimitForMetric(policy, "concurrent"), "")
item.RPM = metricStatus(rpmUsed+rpmReserved, rpmUsed, rpmReserved, rateLimitForMetric(policy, "rpm"), rpmResetAt)
item.TPM = metricStatus(tpmUsed+tpmReserved, tpmUsed, tpmReserved, tpmLimit(policy), tpmResetAt)