perf(queue): 按策略动态扩缩异步 Worker

将 River 执行容量按平台模型与用户组的有效并发策略动态调整,并为长任务续租、并发租约原子抢占和限流退避补充保护。\n\n统一平台模型限流继承语义,兼容历史 platformLimits/modelLimits,并为三个迁移图像模型建立独立并发租约。补充管理端显式继承/覆盖配置、指标、单元测试及隔离 PostgreSQL 验收。\n\n验证:go test ./... -count=1;pnpm lint;pnpm test;pnpm build;隔离 PostgreSQL 并发原子性/续租测试;128 任务与三分钟长任务动态 Worker 验收。
This commit is contained in:
2026-07-24 12:26:56 +08:00
parent 290b8c1854
commit 6c5daf29ca
37 changed files with 2365 additions and 205 deletions
+1
View File
@@ -1513,6 +1513,7 @@ function mergeExistingPlatformModelInput(input: PlatformModelBindingInput, curre
discountFactor: (input.discountFactor ?? existing.discountFactor) || undefined,
pricingRuleSetId: input.pricingRuleSetId ?? existing.pricingRuleSetId,
rateLimitPolicy: input.rateLimitPolicy ?? existing.rateLimitPolicy,
rateLimitPolicyMode: input.rateLimitPolicyMode ?? existing.rateLimitPolicyMode,
retryPolicy: input.retryPolicy ?? existing.retryPolicy,
runtimePolicyOverride: input.runtimePolicyOverride ?? (existing.runtimePolicyOverride as Record<string, unknown> | undefined),
runtimePolicySetId: input.runtimePolicySetId ?? existing.runtimePolicySetId,
@@ -409,13 +409,13 @@ function ModelBindingPolicy(props: { form: PlatformWizardForm; onChange: (value:
</Label>
</>
)}
<ToggleField checked={form.modelOverrideRateLimit} label="覆盖模型限流策略" onChange={(checked) => onChange({ ...form, modelOverrideRateLimit: checked })} />
<ToggleField checked={form.modelOverrideRateLimit} label="覆盖模型限流策略" onChange={(checked) => onChange({ ...form, modelOverrideRateLimit: checked, modelRateLimitOverrideTouched: true })} />
{form.modelOverrideRateLimit && (
<>
<Label> RPM<Input value={form.modelRpmLimit} placeholder="不填则不限制" inputMode="numeric" onChange={(event) => onChange({ ...form, modelRpmLimit: event.target.value })} /></Label>
<Label> RPS<Input value={form.modelRpsLimit} placeholder="不填则不限制" inputMode="numeric" onChange={(event) => onChange({ ...form, modelRpsLimit: event.target.value })} /></Label>
<Label> TPM<Input value={form.modelTpmLimit} placeholder="不填则不限制" inputMode="numeric" onChange={(event) => onChange({ ...form, modelTpmLimit: event.target.value })} /></Label>
<Label><Input value={form.modelConcurrencyLimit} placeholder="不填则不限制" inputMode="numeric" onChange={(event) => onChange({ ...form, modelConcurrencyLimit: event.target.value })} /></Label>
<Label> RPM<Input value={form.modelRpmLimit} placeholder="不填则不限制" inputMode="numeric" onChange={(event) => onChange({ ...form, modelRpmLimit: event.target.value, modelRateLimitOverrideTouched: true })} /></Label>
<Label> RPS<Input value={form.modelRpsLimit} placeholder="不填则不限制" inputMode="numeric" onChange={(event) => onChange({ ...form, modelRpsLimit: event.target.value, modelRateLimitOverrideTouched: true })} /></Label>
<Label> TPM<Input value={form.modelTpmLimit} placeholder="不填则不限制" inputMode="numeric" onChange={(event) => onChange({ ...form, modelTpmLimit: event.target.value, modelRateLimitOverrideTouched: true })} /></Label>
<Label><Input value={form.modelConcurrencyLimit} placeholder="不填则不限制" inputMode="numeric" onChange={(event) => onChange({ ...form, modelConcurrencyLimit: event.target.value, modelRateLimitOverrideTouched: true })} /></Label>
</>
)}
</div>
@@ -1004,6 +1004,7 @@ function platformToForm(
const rateLimitPolicy = platform.rateLimitPolicy ?? {};
const networkProxy = readNetworkProxyConfig(config);
const currentModels = platformModels.filter((model) => model.platformId === platform.id);
const modelRateLimitPolicy = currentModels.find((model) => model.rateLimitPolicyMode === 'override')?.rateLimitPolicy ?? {};
return {
...createEmptyPlatformForm(platform.provider, defaults),
provider: platform.provider,
@@ -1034,6 +1035,12 @@ function platformToForm(
selectedModelIds: platformModelBaseIds(platform, baseModels, currentModels),
modelDiscountFactors: platformModelDiscountFactors(platform, baseModels, currentModels),
modelNameMappings: platformModelNameMappings(platform, baseModels, currentModels),
modelOverrideRateLimit: currentModels.length > 0 && currentModels.every((model) => model.rateLimitPolicyMode === 'override'),
modelRateLimitOverrideTouched: false,
modelRpmLimit: readLimit(modelRateLimitPolicy, 'rpm'),
modelRpsLimit: readLimit(modelRateLimitPolicy, 'rps'),
modelTpmLimit: readLimit(modelRateLimitPolicy, 'tpm_total'),
modelConcurrencyLimit: readLimit(modelRateLimitPolicy, 'concurrent'),
selectionMode: 'partial',
};
}
@@ -0,0 +1,56 @@
import type { BaseModelCatalogItem } from '@easyai-ai-gateway/contracts';
import { describe, expect, it } from 'vitest';
import { createEmptyPlatformForm, platformModelPayloads } from './platform-form';
const baseModel = {
id: 'model-1',
providerKey: 'simulation',
canonicalModelKey: 'simulation:model-1',
invocationName: 'model-1',
providerModelName: 'model-1',
modelType: ['video_generate'],
modelAlias: 'model-1',
displayName: 'Model 1',
referenceCount: 0,
pricingVersion: 1,
status: 'active',
createdAt: '2026-07-24T00:00:00Z',
updatedAt: '2026-07-24T00:00:00Z',
} satisfies BaseModelCatalogItem;
function selectedForm() {
return {
...createEmptyPlatformForm('simulation'),
selectedModelIds: [baseModel.id],
};
}
describe('platform model rate limit payload', () => {
it('preserves an existing model policy when the edit form was not touched', () => {
const [payload] = platformModelPayloads([baseModel], selectedForm());
expect(payload.rateLimitPolicyMode).toBeUndefined();
expect(payload.rateLimitPolicy).toBeUndefined();
});
it('writes inherit only after the override is explicitly disabled', () => {
const [payload] = platformModelPayloads([baseModel], {
...selectedForm(),
modelRateLimitOverrideTouched: true,
});
expect(payload.rateLimitPolicyMode).toBe('inherit');
expect(payload.rateLimitPolicy).toBeUndefined();
});
it('writes an explicit unlimited override when the enabled policy has no rules', () => {
const [payload] = platformModelPayloads([baseModel], {
...selectedForm(),
modelOverrideRateLimit: true,
modelRateLimitOverrideTouched: true,
});
expect(payload.rateLimitPolicyMode).toBe('override');
expect(payload.rateLimitPolicy).toEqual({ rules: [] });
});
});
+11 -14
View File
@@ -41,6 +41,7 @@ export interface PlatformWizardForm {
modelRetryEnabled: boolean;
modelRetryMaxAttempts: string;
modelOverrideRateLimit: boolean;
modelRateLimitOverrideTouched: boolean;
modelRpmLimit: string;
modelRpsLimit: string;
modelTpmLimit: string;
@@ -100,6 +101,7 @@ export function createEmptyPlatformForm(provider = '', defaults?: ProviderConnec
modelRetryEnabled: true,
modelRetryMaxAttempts: '2',
modelOverrideRateLimit: false,
modelRateLimitOverrideTouched: false,
modelRpmLimit: '',
modelRpsLimit: '',
modelTpmLimit: '',
@@ -187,12 +189,15 @@ export function platformModelPayloads(models: BaseModelCatalogItem[], form: Plat
retryPolicy: form.modelOverrideRetry
? { enabled: form.modelRetryEnabled, maxAttempts: form.modelRetryEnabled ? positiveInt(form.modelRetryMaxAttempts, 2) : 1 }
: undefined,
rateLimitPolicy: form.modelOverrideRateLimit ? rateLimitPolicyPayload({
rpmLimit: form.modelRpmLimit,
rpsLimit: form.modelRpsLimit,
tpmLimit: form.modelTpmLimit,
concurrencyLimit: form.modelConcurrencyLimit,
}) : undefined,
rateLimitPolicy: form.modelRateLimitOverrideTouched && form.modelOverrideRateLimit ? rateLimitPolicyPayload({
rpmLimit: form.modelRpmLimit,
rpsLimit: form.modelRpsLimit,
tpmLimit: form.modelTpmLimit,
concurrencyLimit: form.modelConcurrencyLimit,
}) : undefined,
rateLimitPolicyMode: form.modelRateLimitOverrideTouched
? (form.modelOverrideRateLimit ? 'override' : 'inherit')
: undefined,
runtimePolicyOverride: platformModelRuntimeOverride(form),
};
});
@@ -304,14 +309,6 @@ function platformModelRuntimeOverride(form: PlatformWizardForm) {
maxAttempts: form.modelRetryEnabled ? positiveInt(form.modelRetryMaxAttempts, 2) : 1,
};
}
if (form.modelOverrideRateLimit) {
override.rateLimitPolicy = rateLimitPolicyPayload({
rpmLimit: form.modelRpmLimit,
rpsLimit: form.modelRpsLimit,
tpmLimit: form.modelTpmLimit,
concurrencyLimit: form.modelConcurrencyLimit,
});
}
return Object.keys(override).length ? override : undefined;
}
+1
View File
@@ -152,6 +152,7 @@ export interface PlatformModelBindingInput {
pricingMode?: string;
retryPolicy?: Record<string, unknown>;
rateLimitPolicy?: Record<string, unknown>;
rateLimitPolicyMode?: 'inherit' | 'override';
runtimePolicySetId?: string;
runtimePolicyOverride?: Record<string, unknown>;
pricingRuleSetId?: string;