将 River 执行容量按平台模型与用户组的有效并发策略动态调整,并为长任务续租、并发租约原子抢占和限流退避补充保护。\n\n统一平台模型限流继承语义,兼容历史 platformLimits/modelLimits,并为三个迁移图像模型建立独立并发租约。补充管理端显式继承/覆盖配置、指标、单元测试及隔离 PostgreSQL 验收。\n\n验证:go test ./... -count=1;pnpm lint;pnpm test;pnpm build;隔离 PostgreSQL 并发原子性/续租测试;128 任务与三分钟长任务动态 Worker 验收。
153 lines
4.6 KiB
Go
153 lines
4.6 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
type AsyncWorkerCapacitySnapshot struct {
|
|
Capacity int
|
|
Desired int
|
|
HardLimit int
|
|
Capped bool
|
|
EnabledModels int
|
|
UnlimitedModels int
|
|
EnabledGroups int
|
|
UnlimitedGroups int
|
|
ModelDesired int
|
|
GroupDesired int
|
|
}
|
|
|
|
func (s *Store) AsyncWorkerCapacity(ctx context.Context, hardLimit int) (AsyncWorkerCapacitySnapshot, error) {
|
|
if hardLimit < 1 {
|
|
return AsyncWorkerCapacitySnapshot{}, fmt.Errorf("async worker hard limit must be positive")
|
|
}
|
|
rows, err := s.pool.Query(ctx, `
|
|
SELECT COALESCE(b.default_rate_limit_policy, '{}'::jsonb),
|
|
p.rate_limit_policy,
|
|
COALESCE(rp.rate_limit_policy, '{}'::jsonb),
|
|
(m.runtime_policy_set_id IS NOT NULL),
|
|
COALESCE(m.runtime_policy_override, '{}'::jsonb),
|
|
m.rate_limit_policy,
|
|
m.rate_limit_policy_mode
|
|
FROM platform_models m
|
|
JOIN integration_platforms p ON p.id = m.platform_id
|
|
LEFT JOIN base_model_catalog b ON b.id = m.base_model_id
|
|
LEFT JOIN model_runtime_policy_sets rp ON rp.id = COALESCE(m.runtime_policy_set_id, b.runtime_policy_set_id)
|
|
WHERE p.status = 'enabled'
|
|
AND p.deleted_at IS NULL
|
|
AND m.enabled = true`)
|
|
if err != nil {
|
|
return AsyncWorkerCapacitySnapshot{}, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
modelPolicies := make([]map[string]any, 0)
|
|
for rows.Next() {
|
|
var basePolicyBytes, platformPolicyBytes, runtimePolicyBytes []byte
|
|
var runtimeOverrideBytes, modelPolicyBytes []byte
|
|
var runtimeExplicit bool
|
|
var modelPolicyMode string
|
|
if err := rows.Scan(
|
|
&basePolicyBytes,
|
|
&platformPolicyBytes,
|
|
&runtimePolicyBytes,
|
|
&runtimeExplicit,
|
|
&runtimeOverrideBytes,
|
|
&modelPolicyBytes,
|
|
&modelPolicyMode,
|
|
); err != nil {
|
|
return AsyncWorkerCapacitySnapshot{}, err
|
|
}
|
|
modelPolicies = append(modelPolicies, EffectiveRateLimitPolicy(EffectiveRateLimitPolicyInput{
|
|
BasePolicy: decodeObject(basePolicyBytes),
|
|
PlatformPolicy: decodeObject(platformPolicyBytes),
|
|
RuntimePolicy: decodeObject(runtimePolicyBytes),
|
|
RuntimePolicyExplicit: runtimeExplicit,
|
|
RuntimePolicyOverride: decodeObject(runtimeOverrideBytes),
|
|
ModelPolicy: decodeObject(modelPolicyBytes),
|
|
ModelPolicyMode: modelPolicyMode,
|
|
}))
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return AsyncWorkerCapacitySnapshot{}, err
|
|
}
|
|
groupRows, err := s.pool.Query(ctx, `
|
|
SELECT rate_limit_policy
|
|
FROM gateway_user_groups
|
|
WHERE status = 'active'`)
|
|
if err != nil {
|
|
return AsyncWorkerCapacitySnapshot{}, err
|
|
}
|
|
defer groupRows.Close()
|
|
groupPolicies := make([]map[string]any, 0)
|
|
for groupRows.Next() {
|
|
var policyBytes []byte
|
|
if err := groupRows.Scan(&policyBytes); err != nil {
|
|
return AsyncWorkerCapacitySnapshot{}, err
|
|
}
|
|
groupPolicies = append(groupPolicies, NormalizeRateLimitPolicy(decodeObject(policyBytes)))
|
|
}
|
|
if err := groupRows.Err(); err != nil {
|
|
return AsyncWorkerCapacitySnapshot{}, err
|
|
}
|
|
return asyncWorkerCapacityFromPolicySets(modelPolicies, groupPolicies, hardLimit), nil
|
|
}
|
|
|
|
func asyncWorkerCapacityFromPolicies(policies []map[string]any, hardLimit int) AsyncWorkerCapacitySnapshot {
|
|
return asyncWorkerCapacityFromPolicySets(policies, nil, hardLimit)
|
|
}
|
|
|
|
func asyncWorkerCapacityFromPolicySets(modelPolicies []map[string]any, groupPolicies []map[string]any, hardLimit int) AsyncWorkerCapacitySnapshot {
|
|
snapshot := AsyncWorkerCapacitySnapshot{
|
|
HardLimit: hardLimit,
|
|
EnabledModels: len(modelPolicies),
|
|
EnabledGroups: len(groupPolicies),
|
|
}
|
|
modelDesired, modelFinite, unlimitedModels := concurrentPolicySetCapacity(modelPolicies)
|
|
groupDesired, groupFinite, unlimitedGroups := concurrentPolicySetCapacity(groupPolicies)
|
|
snapshot.ModelDesired = modelDesired
|
|
snapshot.GroupDesired = groupDesired
|
|
snapshot.UnlimitedModels = unlimitedModels
|
|
snapshot.UnlimitedGroups = unlimitedGroups
|
|
|
|
desired := hardLimit
|
|
switch {
|
|
case snapshot.EnabledModels == 0:
|
|
desired = 1
|
|
case modelFinite && groupFinite:
|
|
desired = min(modelDesired, groupDesired)
|
|
case modelFinite:
|
|
desired = modelDesired
|
|
case groupFinite:
|
|
desired = groupDesired
|
|
}
|
|
if desired < 1 {
|
|
desired = 1
|
|
}
|
|
snapshot.Desired = desired
|
|
snapshot.Capacity = desired
|
|
if snapshot.Capacity > hardLimit {
|
|
snapshot.Capacity = hardLimit
|
|
snapshot.Capped = true
|
|
}
|
|
return snapshot
|
|
}
|
|
|
|
func concurrentPolicySetCapacity(policies []map[string]any) (total int, finite bool, unlimited int) {
|
|
if len(policies) == 0 {
|
|
return 0, false, 0
|
|
}
|
|
finite = true
|
|
for _, policy := range policies {
|
|
capacity, policyFinite := ConcurrentPolicyCapacity(policy)
|
|
if !policyFinite {
|
|
unlimited++
|
|
finite = false
|
|
continue
|
|
}
|
|
total += capacity
|
|
}
|
|
return total, finite, unlimited
|
|
}
|