8725 workflow avoid serverless function autosave errors (#8916)

See issue #8725 
- Build function asynchronously using a job
- prevent useless builds
- run promises simultaneously

Todo:
- fix outputSchema computing
This commit is contained in:
martmull
2024-12-06 11:13:12 +01:00
committed by GitHub
parent a8867fd090
commit 2b3b073570
9 changed files with 129 additions and 51 deletions

View File

@ -0,0 +1,52 @@
import { Scope } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
import { ServerlessService } from 'src/engine/core-modules/serverless/serverless.service';
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
import { isDefined } from 'src/utils/is-defined';
export type BuildServerlessFunctionBatchEvent = {
serverlessFunctions: {
serverlessFunctionId: string;
serverlessFunctionVersion: string;
}[];
workspaceId: string;
};
@Processor({
queueName: MessageQueue.serverlessFunctionQueue,
scope: Scope.REQUEST,
})
export class BuildServerlessFunctionJob {
constructor(
@InjectRepository(ServerlessFunctionEntity, 'metadata')
private readonly serverlessFunctionRepository: Repository<ServerlessFunctionEntity>,
private readonly serverlessService: ServerlessService,
) {}
@Process(BuildServerlessFunctionJob.name)
async handle(batchEvent: BuildServerlessFunctionBatchEvent): Promise<void> {
for (const {
serverlessFunctionId,
serverlessFunctionVersion,
} of batchEvent.serverlessFunctions) {
const serverlessFunction =
await this.serverlessFunctionRepository.findOneBy({
id: serverlessFunctionId,
workspaceId: batchEvent.workspaceId,
});
if (isDefined(serverlessFunction)) {
await this.serverlessService.build(
serverlessFunction,
serverlessFunctionVersion,
);
}
}
}
}

View File

@ -1,33 +0,0 @@
import { Scope } from '@nestjs/common';
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
export type DeleteServerlessFunctionBatchEvent = {
ids: string[];
workspaceId: string;
};
@Processor({
queueName: MessageQueue.serverlessFunctionQueue,
scope: Scope.REQUEST,
})
export class DeleteServerlessFunctionJob {
constructor(
private readonly serverlessFunctionService: ServerlessFunctionService,
) {}
@Process(DeleteServerlessFunctionJob.name)
async handle(batchEvent: DeleteServerlessFunctionBatchEvent): Promise<void> {
await Promise.all(
batchEvent.ids.map((id) =>
this.serverlessFunctionService.deleteOneServerlessFunction(
id,
batchEvent.workspaceId,
),
),
);
}
}

View File

@ -11,6 +11,7 @@ import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.mod
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
import { ServerlessFunctionResolver } from 'src/engine/metadata-modules/serverless-function/serverless-function.resolver';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
import { BuildServerlessFunctionJob } from 'src/engine/metadata-modules/serverless-function/jobs/build-serverless-function.job';
@Module({
imports: [
@ -21,7 +22,11 @@ import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverles
ThrottlerModule,
AnalyticsModule,
],
providers: [ServerlessFunctionService, ServerlessFunctionResolver],
providers: [
ServerlessFunctionService,
ServerlessFunctionResolver,
BuildServerlessFunctionJob,
],
exports: [ServerlessFunctionService],
})
export class ServerlessFunctionModule {}

View File

@ -32,6 +32,13 @@ import {
} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
import { isDefined } from 'src/utils/is-defined';
import { getLayerDependencies } from 'src/engine/core-modules/serverless/drivers/utils/get-last-layer-dependencies';
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import {
BuildServerlessFunctionBatchEvent,
BuildServerlessFunctionJob,
} from 'src/engine/metadata-modules/serverless-function/jobs/build-serverless-function.job';
@Injectable()
export class ServerlessFunctionService {
@ -43,6 +50,8 @@ export class ServerlessFunctionService {
private readonly throttlerService: ThrottlerService,
private readonly environmentService: EnvironmentService,
private readonly analyticsService: AnalyticsService,
@InjectMessageQueue(MessageQueue.serverlessFunctionQueue)
private readonly messageQueueService: MessageQueueService,
) {}
async findManyServerlessFunctions(where) {
@ -263,7 +272,11 @@ export class ServerlessFunctionService {
});
}
await this.serverlessService.build(existingServerlessFunction, 'draft');
await this.buildServerlessFunction({
serverlessFunctionId: existingServerlessFunction.id,
serverlessFunctionVersion: 'draft',
workspaceId,
});
await this.serverlessFunctionRepository.update(
existingServerlessFunction.id,
{
@ -330,7 +343,11 @@ export class ServerlessFunctionService {
});
}
await this.serverlessService.build(createdServerlessFunction, 'draft');
await this.buildServerlessFunction({
serverlessFunctionId: createdServerlessFunction.id,
serverlessFunctionVersion: 'draft',
workspaceId,
});
return this.serverlessFunctionRepository.findOneBy({
id: createdServerlessFunction.id,
@ -351,4 +368,25 @@ export class ServerlessFunctionService {
);
}
}
private async buildServerlessFunction({
serverlessFunctionId,
serverlessFunctionVersion,
workspaceId,
}: {
serverlessFunctionId: string;
serverlessFunctionVersion: string;
workspaceId: string;
}) {
await this.messageQueueService.add<BuildServerlessFunctionBatchEvent>(
BuildServerlessFunctionJob.name,
{
serverlessFunctions: [
{ serverlessFunctionId, serverlessFunctionVersion },
],
workspaceId,
},
{ id: `${serverlessFunctionId}-${serverlessFunctionVersion}` },
);
}
}