perf(worker): 解耦准入调度与远端执行
原因:线上 P24 同构验收确认两台香港 Worker 的全局容量为 48,但跨地域逐行准入事务每轮只能形成 8 个活跃租约,队列最老等待超过 15 分钟。\n\n影响:新增可配置的异步准入 dispatcher 角色;生产两地 API 负责准入和过期回收,Worker 仅执行 River job。当前主库同站点 API 可低延迟填满容量,主库切换后另一地 API 通过既有数据库锁安全接管;未配置环境变量时保持原 Worker 一体化行为。\n\n验证:Go 全量测试、go vet、gofmt、真实 PostgreSQL 1000 任务双进程回归、前端 lint/test/build、Kubernetes server-side dry-run、Compose、ShellCheck、cluster/manual release tests 全部通过。
This commit is contained in:
@@ -168,7 +168,7 @@ AI_GATEWAY_DATABASE_URL=postgresql://easyai:easyai2025@localhost:5432/easyai_ai_
|
|||||||
|
|
||||||
如果现有 `easyai-pgvector` 没有把 `5432` 映射到宿主机,就需要补端口映射,或者把 AI Gateway 后端容器化后接入同一个 `easyai` Docker network。
|
如果现有 `easyai-pgvector` 没有把 `5432` 映射到宿主机,就需要补端口映射,或者把 AI Gateway 后端容器化后接入同一个 `easyai` Docker network。
|
||||||
|
|
||||||
异步队列 worker 不使用固定业务并发。服务分别汇总启用平台模型和活跃用户组的有效 `concurrent` 策略,采用两者中更严格的集群容量,默认每 5 秒在线调整 River 执行容量。策略解析兼容历史 `platformLimits/modelLimits.max_concurrent_requests`,运行时统一转换为 `rules`。`AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT` 默认 `2048`,限制策略推导出的集群目标;`AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT` 默认 `32`,按单 Worker 的内存安全容量限制实例分配,即使其他实例失活也不会突破。Worker 的 `AI_GATEWAY_DATABASE_MAX_CONNS` 必须高于实例执行容量,为心跳、选主、租约续期和健康检查保留连接;生产环境按每实例执行容量 `24` 配置连接池上限 `32`。`AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE` 默认 `8`,把短任务准入、容量租约和唯一 River job 合并为有界原子微批次,摊薄跨地域同步提交,同时禁止重新形成整窗大事务。异步任务首次排队时会持久化已经鉴权的候选和 admission scope 快照;Worker 批量读取快照并仅刷新动态执行容量,只有显式重新选路才重新水化媒体和计算候选,避免队列越大、重复路由查询越多。`AI_GATEWAY_DATABASE_MIN_IDLE_CONNS` 控制启动预热连接数,默认 `0`,生产 K3s 配置为 `4`;`AI_GATEWAY_DATABASE_MAX_CONN_IDLE_SECONDS` 生产配置为 `300` 秒。平台模型和用户组的 PostgreSQL concurrency lease 仍是业务并发真值。可通过 `AI_GATEWAY_ASYNC_WORKER_REFRESH_INTERVAL_SECONDS` 调整刷新周期。
|
异步队列 worker 不使用固定业务并发。服务分别汇总启用平台模型和活跃用户组的有效 `concurrent` 策略,采用两者中更严格的集群容量,默认每 5 秒在线调整 River 执行容量。策略解析兼容历史 `platformLimits/modelLimits.max_concurrent_requests`,运行时统一转换为 `rules`。`AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT` 默认 `2048`,限制策略推导出的集群目标;`AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT` 默认 `32`,按单 Worker 的内存安全容量限制实例分配,即使其他实例失活也不会突破。Worker 的 `AI_GATEWAY_DATABASE_MAX_CONNS` 必须高于实例执行容量,为心跳、选主、租约续期和健康检查保留连接;生产环境按每实例执行容量 `24` 配置连接池上限 `32`。`AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE` 默认 `8`,把短任务准入、容量租约和唯一 River job 合并为有界原子微批次,摊薄同步提交,同时禁止重新形成整窗大事务。异步任务首次排队时会持久化已经鉴权的候选和 admission scope 快照;启用 `AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED=true` 的进程批量读取快照并仅刷新动态执行容量,只有显式重新选路才重新水化媒体和计算候选。生产 K3s 在两地 API 启用 dispatcher、在 Worker 禁用,使当前 PostgreSQL 主库同站点 API 负责低延迟准入,远端 Worker 只执行 River job;数据库任务锁和 scope 锁保证主库切换时另一地 API 可安全接管。未显式配置该变量时保持兼容行为,由执行 Worker 同时运行 dispatcher。`AI_GATEWAY_DATABASE_MIN_IDLE_CONNS` 控制启动预热连接数,默认 `0`,生产 K3s 配置为 `4`;`AI_GATEWAY_DATABASE_MAX_CONN_IDLE_SECONDS` 生产配置为 `300` 秒。平台模型和用户组的 PostgreSQL concurrency lease 仍是业务并发真值。可通过 `AI_GATEWAY_ASYNC_WORKER_REFRESH_INTERVAL_SECONDS` 调整刷新周期。
|
||||||
|
|
||||||
## 迁移原则
|
## 迁移原则
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ type Config struct {
|
|||||||
AsyncWorkerInstanceHardLimit int
|
AsyncWorkerInstanceHardLimit int
|
||||||
AsyncWorkerRefreshIntervalSeconds int
|
AsyncWorkerRefreshIntervalSeconds int
|
||||||
AsyncAdmissionMicrobatchSize int
|
AsyncAdmissionMicrobatchSize int
|
||||||
|
AsyncAdmissionDispatcherEnabled bool
|
||||||
|
AsyncAdmissionDispatcherConfigured bool
|
||||||
WorkerAutoscalingEnabled bool
|
WorkerAutoscalingEnabled bool
|
||||||
WorkerReplicasNingbo int
|
WorkerReplicasNingbo int
|
||||||
WorkerReplicasHongkong int
|
WorkerReplicasHongkong int
|
||||||
@@ -188,6 +190,10 @@ func Load() Config {
|
|||||||
AsyncWorkerInstanceHardLimit: envIntValidated("AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT", 32),
|
AsyncWorkerInstanceHardLimit: envIntValidated("AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT", 32),
|
||||||
AsyncWorkerRefreshIntervalSeconds: envIntValidated("AI_GATEWAY_ASYNC_WORKER_REFRESH_INTERVAL_SECONDS", 5),
|
AsyncWorkerRefreshIntervalSeconds: envIntValidated("AI_GATEWAY_ASYNC_WORKER_REFRESH_INTERVAL_SECONDS", 5),
|
||||||
AsyncAdmissionMicrobatchSize: envIntValidated("AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE", 8),
|
AsyncAdmissionMicrobatchSize: envIntValidated("AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE", 8),
|
||||||
|
AsyncAdmissionDispatcherEnabled: envValue("AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED") == "true",
|
||||||
|
AsyncAdmissionDispatcherConfigured: envValue(
|
||||||
|
"AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED",
|
||||||
|
) != "",
|
||||||
WorkerAutoscalingEnabled: env("AI_GATEWAY_WORKER_AUTOSCALING_ENABLED", "false") == "true",
|
WorkerAutoscalingEnabled: env("AI_GATEWAY_WORKER_AUTOSCALING_ENABLED", "false") == "true",
|
||||||
WorkerReplicasNingbo: envOptionalIntValidated("AI_GATEWAY_WORKER_REPLICAS_NINGBO", 1),
|
WorkerReplicasNingbo: envOptionalIntValidated("AI_GATEWAY_WORKER_REPLICAS_NINGBO", 1),
|
||||||
WorkerReplicasHongkong: envOptionalIntValidated("AI_GATEWAY_WORKER_REPLICAS_HONGKONG", 1),
|
WorkerReplicasHongkong: envOptionalIntValidated("AI_GATEWAY_WORKER_REPLICAS_HONGKONG", 1),
|
||||||
@@ -463,6 +469,18 @@ func (c Config) RunsAsyncExecutionWorker() bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunsAsyncAdmissionDispatcher keeps the legacy all-in-one/Worker behaviour
|
||||||
|
// unless a deployment explicitly separates admission coordination from task
|
||||||
|
// execution. Production enables this on both API sites so the instance nearest
|
||||||
|
// the current PostgreSQL primary wins the existing database scope locks, while
|
||||||
|
// remote Worker replicas only execute River jobs.
|
||||||
|
func (c Config) RunsAsyncAdmissionDispatcher() bool {
|
||||||
|
if c.AsyncAdmissionDispatcherConfigured {
|
||||||
|
return c.AsyncAdmissionDispatcherEnabled
|
||||||
|
}
|
||||||
|
return c.RunsAsyncExecutionWorker()
|
||||||
|
}
|
||||||
|
|
||||||
func (c Config) RunsBackgroundWorkers() bool {
|
func (c Config) RunsBackgroundWorkers() bool {
|
||||||
switch c.EffectiveProcessRole() {
|
switch c.EffectiveProcessRole() {
|
||||||
case "api", "capacity-controller":
|
case "api", "capacity-controller":
|
||||||
|
|||||||
@@ -159,12 +159,30 @@ func TestProcessRolePrecedenceAndCompatibility(t *testing.T) {
|
|||||||
if cfg.RunsPublicHTTP() || !cfg.RunsAsyncExecutionWorker() || !cfg.RunsBackgroundWorkers() {
|
if cfg.RunsPublicHTTP() || !cfg.RunsAsyncExecutionWorker() || !cfg.RunsBackgroundWorkers() {
|
||||||
t.Fatalf("worker role capabilities are inconsistent: %+v", cfg)
|
t.Fatalf("worker role capabilities are inconsistent: %+v", cfg)
|
||||||
}
|
}
|
||||||
|
if !cfg.RunsAsyncAdmissionDispatcher() {
|
||||||
|
t.Fatal("legacy Worker role no longer runs the admission dispatcher")
|
||||||
|
}
|
||||||
|
|
||||||
t.Setenv("AI_GATEWAY_PROCESS_ROLE", "api")
|
t.Setenv("AI_GATEWAY_PROCESS_ROLE", "api")
|
||||||
cfg = Load()
|
cfg = Load()
|
||||||
if !cfg.RunsPublicHTTP() || cfg.RunsAsyncExecutionWorker() || cfg.RunsBackgroundWorkers() {
|
if !cfg.RunsPublicHTTP() || cfg.RunsAsyncExecutionWorker() || cfg.RunsBackgroundWorkers() {
|
||||||
t.Fatalf("api role capabilities are inconsistent: %+v", cfg)
|
t.Fatalf("api role capabilities are inconsistent: %+v", cfg)
|
||||||
}
|
}
|
||||||
|
if cfg.RunsAsyncAdmissionDispatcher() {
|
||||||
|
t.Fatal("API role unexpectedly runs the admission dispatcher without explicit configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Setenv("AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED", "true")
|
||||||
|
cfg = Load()
|
||||||
|
if !cfg.RunsAsyncAdmissionDispatcher() || cfg.RunsAsyncExecutionWorker() {
|
||||||
|
t.Fatalf("API admission-only role is inconsistent: %+v", cfg)
|
||||||
|
}
|
||||||
|
t.Setenv("AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED", "false")
|
||||||
|
t.Setenv("AI_GATEWAY_PROCESS_ROLE", "worker")
|
||||||
|
cfg = Load()
|
||||||
|
if cfg.RunsAsyncAdmissionDispatcher() || !cfg.RunsAsyncExecutionWorker() {
|
||||||
|
t.Fatalf("Worker execution-only role is inconsistent: %+v", cfg)
|
||||||
|
}
|
||||||
|
|
||||||
t.Setenv("AI_GATEWAY_PROCESS_ROLE", "capacity-controller")
|
t.Setenv("AI_GATEWAY_PROCESS_ROLE", "capacity-controller")
|
||||||
cfg = Load()
|
cfg = Load()
|
||||||
|
|||||||
@@ -136,33 +136,37 @@ func TestAsyncWorkerThousandConcurrentSchedulingAcceptance(t *testing.T) {
|
|||||||
serverCtx, cancelServer := context.WithCancel(ctx)
|
serverCtx, cancelServer := context.WithCancel(ctx)
|
||||||
defer cancelServer()
|
defer cancelServer()
|
||||||
server := httptest.NewServer(NewServerWithStores(serverCtx, config.Config{
|
server := httptest.NewServer(NewServerWithStores(serverCtx, config.Config{
|
||||||
AppEnv: "test",
|
AppEnv: "test",
|
||||||
HTTPAddr: ":0",
|
HTTPAddr: ":0",
|
||||||
DatabaseURL: databaseURL,
|
DatabaseURL: databaseURL,
|
||||||
IdentityMode: "hybrid",
|
IdentityMode: "hybrid",
|
||||||
JWTSecret: "test-secret",
|
JWTSecret: "test-secret",
|
||||||
BillingEngineMode: "observe",
|
BillingEngineMode: "observe",
|
||||||
CORSAllowedOrigin: "*",
|
CORSAllowedOrigin: "*",
|
||||||
ProcessRole: "api",
|
ProcessRole: "api",
|
||||||
AsyncQueueWorkerEnabled: true,
|
AsyncQueueWorkerEnabled: true,
|
||||||
AsyncWorkerHardLimit: 1000,
|
AsyncAdmissionDispatcherEnabled: true,
|
||||||
AsyncWorkerInstanceHardLimit: 1000,
|
AsyncAdmissionDispatcherConfigured: true,
|
||||||
AsyncWorkerRefreshIntervalSeconds: 1,
|
AsyncWorkerHardLimit: 1000,
|
||||||
|
AsyncWorkerInstanceHardLimit: 1000,
|
||||||
|
AsyncWorkerRefreshIntervalSeconds: 1,
|
||||||
}, apiDB, apiCoordinationDB, apiDB, slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))))
|
}, apiDB, apiCoordinationDB, apiDB, slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
workerServer := httptest.NewServer(NewServerWithStores(serverCtx, config.Config{
|
workerServer := httptest.NewServer(NewServerWithStores(serverCtx, config.Config{
|
||||||
AppEnv: "test",
|
AppEnv: "test",
|
||||||
HTTPAddr: ":0",
|
HTTPAddr: ":0",
|
||||||
DatabaseURL: databaseURL,
|
DatabaseURL: databaseURL,
|
||||||
IdentityMode: "hybrid",
|
IdentityMode: "hybrid",
|
||||||
JWTSecret: "test-secret",
|
JWTSecret: "test-secret",
|
||||||
BillingEngineMode: "observe",
|
BillingEngineMode: "observe",
|
||||||
CORSAllowedOrigin: "*",
|
CORSAllowedOrigin: "*",
|
||||||
ProcessRole: "worker",
|
ProcessRole: "worker",
|
||||||
AsyncQueueWorkerEnabled: true,
|
AsyncQueueWorkerEnabled: true,
|
||||||
AsyncWorkerHardLimit: 1000,
|
AsyncAdmissionDispatcherEnabled: false,
|
||||||
AsyncWorkerInstanceHardLimit: 1000,
|
AsyncAdmissionDispatcherConfigured: true,
|
||||||
AsyncWorkerRefreshIntervalSeconds: 1,
|
AsyncWorkerHardLimit: 1000,
|
||||||
|
AsyncWorkerInstanceHardLimit: 1000,
|
||||||
|
AsyncWorkerRefreshIntervalSeconds: 1,
|
||||||
}, workerDB, workerCoordinationDB, workerDB, slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))))
|
}, workerDB, workerCoordinationDB, workerDB, slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))))
|
||||||
defer workerServer.Close()
|
defer workerServer.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ func NewServerWithStores(
|
|||||||
logger.Info("asynchronous queue worker disabled for this process")
|
logger.Info("asynchronous queue worker disabled for this process")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if cfg.RunsAsyncAdmissionDispatcher() {
|
||||||
|
server.runner.StartAsyncAdmissionDispatcher(ctx)
|
||||||
|
}
|
||||||
if cfg.RunsBackgroundWorkers() {
|
if cfg.RunsBackgroundWorkers() {
|
||||||
server.runner.StartBillingSettlementWorker(ctx)
|
server.runner.StartBillingSettlementWorker(ctx)
|
||||||
server.runner.StartTaskHistoryWorkers(ctx)
|
server.runner.StartTaskHistoryWorkers(ctx)
|
||||||
|
|||||||
@@ -274,13 +274,24 @@ func (s *Service) startRiverQueue(ctx context.Context, workerEnabled bool) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go s.refreshAsyncWorkerCapacity(ctx)
|
go s.refreshAsyncWorkerCapacity(ctx)
|
||||||
go s.dispatchWaitingAsyncAdmissions(ctx)
|
|
||||||
go s.reapExpiredTaskAdmissions(ctx)
|
|
||||||
go s.recoverOrphanedAsyncRiverJobs(ctx)
|
go s.recoverOrphanedAsyncRiverJobs(ctx)
|
||||||
go s.stopAsyncWorkersOnShutdown(ctx)
|
go s.stopAsyncWorkersOnShutdown(ctx)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartAsyncAdmissionDispatcher starts the lightweight admission/reaping
|
||||||
|
// coordinator independently from River execution workers. This allows the
|
||||||
|
// dispatcher to run beside PostgreSQL while Worker pods scale on other nodes;
|
||||||
|
// the existing database task/scope locks make multiple API-site dispatchers
|
||||||
|
// safe during primary failover.
|
||||||
|
func (s *Service) StartAsyncAdmissionDispatcher(ctx context.Context) {
|
||||||
|
s.asyncAdmissionRunner.Do(func() {
|
||||||
|
go s.dispatchWaitingAsyncAdmissions(ctx)
|
||||||
|
go s.reapExpiredTaskAdmissions(ctx)
|
||||||
|
s.logger.Info("asynchronous admission dispatcher started")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) newRiverAsyncExecutionClient(capacity int) (*river.Client[pgx.Tx], error) {
|
func (s *Service) newRiverAsyncExecutionClient(capacity int) (*river.Client[pgx.Tx], error) {
|
||||||
workers := river.NewWorkers()
|
workers := river.NewWorkers()
|
||||||
if err := river.AddWorkerSafely(workers, &asyncTaskWorker{service: s}); err != nil {
|
if err := river.AddWorkerSafely(workers, &asyncTaskWorker{service: s}); err != nil {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ type Service struct {
|
|||||||
asyncAdmissionWake chan struct{}
|
asyncAdmissionWake chan struct{}
|
||||||
admissionTaskWaiters map[string]*admissionTaskWaiter
|
admissionTaskWaiters map[string]*admissionTaskWaiter
|
||||||
admissionListener sync.Once
|
admissionListener sync.Once
|
||||||
|
asyncAdmissionRunner sync.Once
|
||||||
asyncClientFactory func(int) (asyncExecutionClient, error)
|
asyncClientFactory func(int) (asyncExecutionClient, error)
|
||||||
mediaResultSlots chan struct{}
|
mediaResultSlots chan struct{}
|
||||||
imageNormalizeSlots chan struct{}
|
imageNormalizeSlots chan struct{}
|
||||||
|
|||||||
@@ -403,6 +403,7 @@ rollout_worker_site() {
|
|||||||
"AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT" \
|
"AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT" \
|
||||||
"AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT" \
|
"AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT" \
|
||||||
"AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE=$AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE" \
|
"AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE=$AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE" \
|
||||||
|
"AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED=false" \
|
||||||
"AI_GATEWAY_DATABASE_MAX_CONNS=$AI_GATEWAY_DATABASE_MAX_CONNS" \
|
"AI_GATEWAY_DATABASE_MAX_CONNS=$AI_GATEWAY_DATABASE_MAX_CONNS" \
|
||||||
"AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS=$AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS" \
|
"AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS=$AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS" \
|
||||||
"AI_GATEWAY_DATABASE_RIVER_MAX_CONNS=$AI_GATEWAY_DATABASE_RIVER_MAX_CONNS" \
|
"AI_GATEWAY_DATABASE_RIVER_MAX_CONNS=$AI_GATEWAY_DATABASE_RIVER_MAX_CONNS" \
|
||||||
@@ -433,6 +434,8 @@ rollout_worker_site() {
|
|||||||
-n "$NAMESPACE" --timeout=300s || return 1
|
-n "$NAMESPACE" --timeout=300s || return 1
|
||||||
[[ $("${kubectl[@]}" get deployment "easyai-worker-$site" -n "$NAMESPACE" \
|
[[ $("${kubectl[@]}" get deployment "easyai-worker-$site" -n "$NAMESPACE" \
|
||||||
-o jsonpath='{.spec.template.spec.containers[?(@.name=="worker")].env[?(@.name=="AI_GATEWAY_PROCESS_ROLE")].value}') == worker ]] || return 1
|
-o jsonpath='{.spec.template.spec.containers[?(@.name=="worker")].env[?(@.name=="AI_GATEWAY_PROCESS_ROLE")].value}') == worker ]] || return 1
|
||||||
|
[[ $("${kubectl[@]}" get deployment "easyai-worker-$site" -n "$NAMESPACE" \
|
||||||
|
-o jsonpath='{.spec.template.spec.containers[?(@.name=="worker")].env[?(@.name=="AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED")].value}') == false ]] || return 1
|
||||||
ready_replicas=$("${kubectl[@]}" get deployment "easyai-worker-$site" -n "$NAMESPACE" \
|
ready_replicas=$("${kubectl[@]}" get deployment "easyai-worker-$site" -n "$NAMESPACE" \
|
||||||
-o json | jq -r '.status.readyReplicas // 0') || return 1
|
-o json | jq -r '.status.readyReplicas // 0') || return 1
|
||||||
[[ $ready_replicas == "$replicas" ]] || return 1
|
[[ $ready_replicas == "$replicas" ]] || return 1
|
||||||
@@ -592,6 +595,7 @@ rollout_api_capacity_site() {
|
|||||||
"${kubectl[@]}" set env "deployment/easyai-api-$site" -n "$NAMESPACE" \
|
"${kubectl[@]}" set env "deployment/easyai-api-$site" -n "$NAMESPACE" \
|
||||||
"AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT" \
|
"AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT" \
|
||||||
"AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT" \
|
"AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT" \
|
||||||
|
"AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED=true" \
|
||||||
"AI_GATEWAY_DATABASE_MAX_CONNS=$AI_GATEWAY_API_DATABASE_MAX_CONNS" \
|
"AI_GATEWAY_DATABASE_MAX_CONNS=$AI_GATEWAY_API_DATABASE_MAX_CONNS" \
|
||||||
"AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS=$AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS" \
|
"AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS=$AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS" \
|
||||||
"AI_GATEWAY_DATABASE_RIVER_MAX_CONNS=$AI_GATEWAY_API_DATABASE_RIVER_MAX_CONNS" \
|
"AI_GATEWAY_DATABASE_RIVER_MAX_CONNS=$AI_GATEWAY_API_DATABASE_RIVER_MAX_CONNS" \
|
||||||
@@ -637,6 +641,7 @@ rollout_site() {
|
|||||||
"${kubectl[@]}" set env "deployment/easyai-api-$site" -n "$NAMESPACE" \
|
"${kubectl[@]}" set env "deployment/easyai-api-$site" -n "$NAMESPACE" \
|
||||||
"AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT" \
|
"AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT" \
|
||||||
"AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT" \
|
"AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT=$AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT" \
|
||||||
|
"AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED=true" \
|
||||||
"AI_GATEWAY_DATABASE_MAX_CONNS=$AI_GATEWAY_API_DATABASE_MAX_CONNS" \
|
"AI_GATEWAY_DATABASE_MAX_CONNS=$AI_GATEWAY_API_DATABASE_MAX_CONNS" \
|
||||||
"AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS=$AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS" \
|
"AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS=$AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS" \
|
||||||
"AI_GATEWAY_DATABASE_RIVER_MAX_CONNS=$AI_GATEWAY_API_DATABASE_RIVER_MAX_CONNS" \
|
"AI_GATEWAY_DATABASE_RIVER_MAX_CONNS=$AI_GATEWAY_API_DATABASE_RIVER_MAX_CONNS" \
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ spec:
|
|||||||
value: api
|
value: api
|
||||||
- name: AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED
|
- name: AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED
|
||||||
value: "false"
|
value: "false"
|
||||||
|
- name: AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED
|
||||||
|
value: "true"
|
||||||
- name: AI_GATEWAY_DATABASE_MAX_CONNS
|
- name: AI_GATEWAY_DATABASE_MAX_CONNS
|
||||||
value: "32"
|
value: "32"
|
||||||
- name: AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS
|
- name: AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS
|
||||||
@@ -330,6 +332,8 @@ spec:
|
|||||||
value: api
|
value: api
|
||||||
- name: AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED
|
- name: AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED
|
||||||
value: "false"
|
value: "false"
|
||||||
|
- name: AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED
|
||||||
|
value: "true"
|
||||||
- name: AI_GATEWAY_DATABASE_MAX_CONNS
|
- name: AI_GATEWAY_DATABASE_MAX_CONNS
|
||||||
value: "32"
|
value: "32"
|
||||||
- name: AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS
|
- name: AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS
|
||||||
@@ -501,6 +505,8 @@ spec:
|
|||||||
value: worker
|
value: worker
|
||||||
- name: AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED
|
- name: AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED
|
||||||
value: "true"
|
value: "true"
|
||||||
|
- name: AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED
|
||||||
|
value: "false"
|
||||||
- name: AI_GATEWAY_DATABASE_MAX_CONNS
|
- name: AI_GATEWAY_DATABASE_MAX_CONNS
|
||||||
value: "32"
|
value: "32"
|
||||||
- name: AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS
|
- name: AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS
|
||||||
@@ -676,6 +682,8 @@ spec:
|
|||||||
value: worker
|
value: worker
|
||||||
- name: AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED
|
- name: AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED
|
||||||
value: "true"
|
value: "true"
|
||||||
|
- name: AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED
|
||||||
|
value: "false"
|
||||||
- name: AI_GATEWAY_DATABASE_MAX_CONNS
|
- name: AI_GATEWAY_DATABASE_MAX_CONNS
|
||||||
value: "32"
|
value: "32"
|
||||||
- name: AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS
|
- name: AI_GATEWAY_DATABASE_CRITICAL_MAX_CONNS
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ WebP、合法 4K 图片和越过 Seedance 2.0 官方输入边界的 `6144x2160`
|
|||||||
```text
|
```text
|
||||||
AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT
|
AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT
|
||||||
AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE
|
AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE
|
||||||
|
AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED
|
||||||
AI_GATEWAY_DATABASE_MAX_CONNS
|
AI_GATEWAY_DATABASE_MAX_CONNS
|
||||||
AI_GATEWAY_DATABASE_MIN_IDLE_CONNS
|
AI_GATEWAY_DATABASE_MIN_IDLE_CONNS
|
||||||
AI_GATEWAY_DATABASE_MAX_CONN_IDLE_SECONDS
|
AI_GATEWAY_DATABASE_MAX_CONN_IDLE_SECONDS
|
||||||
|
|||||||
Reference in New Issue
Block a user