102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
import { tryCatch } from "@trigger.dev/core";
|
|
import { ManageConcurrencyPresenter } from "~/presenters/v3/ManageConcurrencyPresenter.server";
|
|
import { BaseService } from "./baseService.server";
|
|
import { updateEnvConcurrencyLimits } from "../runQueue.server";
|
|
import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server";
|
|
|
|
type Input = {
|
|
userId: string;
|
|
projectId: string;
|
|
organizationId: string;
|
|
environments: { id: string; amount: number }[];
|
|
};
|
|
|
|
type Result =
|
|
| {
|
|
success: true;
|
|
}
|
|
| {
|
|
success: false;
|
|
error: string;
|
|
};
|
|
|
|
export class AllocateConcurrencyService extends BaseService {
|
|
async call({ userId, projectId, organizationId, environments }: Input): Promise<Result> {
|
|
// fetch the current concurrency
|
|
const presenter = new ManageConcurrencyPresenter(this._prisma, this._replica);
|
|
const [error, result] = await tryCatch(
|
|
presenter.call({
|
|
userId,
|
|
projectId,
|
|
organizationId,
|
|
})
|
|
);
|
|
|
|
if (error) {
|
|
return {
|
|
success: false,
|
|
error: "Unknown error",
|
|
};
|
|
}
|
|
|
|
const previousExtra = result.environments.reduce(
|
|
(acc, e) => Math.max(0, e.maximumConcurrencyLimit - e.planConcurrencyLimit) + acc,
|
|
0
|
|
);
|
|
const requested = new Map(environments.map((e) => [e.id, e.amount]));
|
|
const newExtra = result.environments.reduce((acc, env) => {
|
|
const targetExtra = requested.has(env.id)
|
|
? Math.max(0, requested.get(env.id)!)
|
|
: Math.max(0, env.maximumConcurrencyLimit - env.planConcurrencyLimit);
|
|
return acc + targetExtra;
|
|
}, 0);
|
|
const change = newExtra - previousExtra;
|
|
|
|
const totalExtra = result.extraAllocatedConcurrency + change;
|
|
|
|
if (change > result.extraUnallocatedConcurrency) {
|
|
return {
|
|
success: false,
|
|
error: `You don't have enough unallocated concurrency available. You requested ${totalExtra} but only have ${result.extraUnallocatedConcurrency}.`,
|
|
};
|
|
}
|
|
|
|
for (const environment of environments) {
|
|
const existingEnvironment = result.environments.find((e) => e.id === environment.id);
|
|
|
|
if (!existingEnvironment) {
|
|
return {
|
|
success: false,
|
|
error: `Environment not found ${environment.id}`,
|
|
};
|
|
}
|
|
|
|
const newConcurrency = existingEnvironment.planConcurrencyLimit + environment.amount;
|
|
|
|
const updatedEnvironment = await this._prisma.runtimeEnvironment.update({
|
|
where: {
|
|
id: environment.id,
|
|
},
|
|
data: {
|
|
maximumConcurrencyLimit: newConcurrency,
|
|
},
|
|
include: {
|
|
project: true,
|
|
organization: true,
|
|
},
|
|
});
|
|
|
|
if (!updatedEnvironment.paused) {
|
|
await updateEnvConcurrencyLimits(updatedEnvironment);
|
|
}
|
|
|
|
// maximumConcurrencyLimit changed in the control-plane; drop any cached copy.
|
|
controlPlaneResolver.invalidateEnvironment(environment.id);
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
};
|
|
}
|
|
}
|