9018 fix batch delete (#9149)

Closes #9018
This commit is contained in:
Raphaël Bosi
2024-12-20 10:46:24 +01:00
committed by GitHub
parent a0b5720831
commit 925294675c
22 changed files with 413 additions and 50 deletions

View File

@ -0,0 +1 @@
export const FAVORITE_DELETION_BATCH_SIZE = 100;

View File

@ -0,0 +1,24 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { FavoriteDeletionJob } from 'src/modules/favorite/jobs/favorite-deletion.job';
import { FavoriteDeletionListener } from 'src/modules/favorite/listeners/favorite-deletion.listener';
import { FavoriteDeletionService } from 'src/modules/favorite/services/favorite-deletion.service';
@Module({
imports: [
TypeOrmModule.forFeature(
[ObjectMetadataEntity, FieldMetadataEntity],
'metadata',
),
],
providers: [
FavoriteDeletionService,
FavoriteDeletionListener,
FavoriteDeletionJob,
],
exports: [],
})
export class FavoriteModule {}

View File

@ -0,0 +1,29 @@
import { Scope } from '@nestjs/common';
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
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 { FavoriteDeletionService } from 'src/modules/favorite/services/favorite-deletion.service';
export type FavoriteDeletionJobData = {
workspaceId: string;
deletedRecordIds: string[];
};
@Processor({
queueName: MessageQueue.favoriteQueue,
scope: Scope.REQUEST,
})
export class FavoriteDeletionJob {
constructor(
private readonly favoriteDeletionService: FavoriteDeletionService,
) {}
@Process(FavoriteDeletionJob.name)
async handle(data: FavoriteDeletionJobData): Promise<void> {
await this.favoriteDeletionService.deleteFavoritesForDeletedRecords(
data.deletedRecordIds,
data.workspaceId,
);
}
}

View File

@ -0,0 +1,36 @@
import { Injectable } from '@nestjs/common';
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
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 { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
import {
FavoriteDeletionJob,
FavoriteDeletionJobData,
} from 'src/modules/favorite/jobs/favorite-deletion.job';
@Injectable()
export class FavoriteDeletionListener {
constructor(
@InjectMessageQueue(MessageQueue.favoriteQueue)
private readonly messageQueueService: MessageQueueService,
) {}
@OnDatabaseBatchEvent('*', DatabaseEventAction.DELETED)
async handleDeletedEvent(
payload: WorkspaceEventBatch<ObjectRecordDeleteEvent>,
) {
const deletedRecordIds = payload.events.map(({ recordId }) => recordId);
await this.messageQueueService.add<FavoriteDeletionJobData>(
FavoriteDeletionJob.name,
{
workspaceId: payload.workspaceId,
deletedRecordIds,
},
);
}
}

View File

@ -0,0 +1,87 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';
import {
FieldMetadataEntity,
FieldMetadataType,
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { FAVORITE_DELETION_BATCH_SIZE } from 'src/modules/favorite/constants/favorite-deletion-batch-size';
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
@Injectable()
export class FavoriteDeletionService {
constructor(
@InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
@InjectRepository(FieldMetadataEntity, 'metadata')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
private readonly twentyORMManager: TwentyORMManager,
) {}
async deleteFavoritesForDeletedRecords(
deletedRecordIds: string[],
workspaceId: string,
): Promise<void> {
const favoriteRepository =
await this.twentyORMManager.getRepository<FavoriteWorkspaceEntity>(
'favorite',
);
const favoriteObjectMetadata = await this.objectMetadataRepository.findOne({
where: {
nameSingular: 'favorite',
workspaceId,
},
});
if (!favoriteObjectMetadata) {
throw new Error('Favorite object metadata not found');
}
const favoriteFields = await this.fieldMetadataRepository.find({
where: {
objectMetadataId: favoriteObjectMetadata.id,
type: FieldMetadataType.RELATION,
},
});
const favoritesToDelete = await favoriteRepository.find({
select: {
id: true,
},
where: favoriteFields.map((field) => ({
[`${field.name}Id`]: In(deletedRecordIds),
})),
withDeleted: true,
});
if (favoritesToDelete.length === 0) {
return;
}
const favoriteIdsToDelete = favoritesToDelete.map(
(favorite) => favorite.id,
);
const batches: string[][] = [];
for (
let i = 0;
i < favoriteIdsToDelete.length;
i += FAVORITE_DELETION_BATCH_SIZE
) {
batches.push(
favoriteIdsToDelete.slice(i, i + FAVORITE_DELETION_BATCH_SIZE),
);
}
for (const batch of batches) {
await favoriteRepository.delete(batch);
}
}
}

View File

@ -3,6 +3,7 @@ import { Module } from '@nestjs/common';
import { CalendarModule } from 'src/modules/calendar/calendar.module';
import { ConnectedAccountModule } from 'src/modules/connected-account/connected-account.module';
import { FavoriteFolderModule } from 'src/modules/favorite-folder/favorite-folder.module';
import { FavoriteModule } from 'src/modules/favorite/favorite.module';
import { MessagingModule } from 'src/modules/messaging/messaging.module';
import { ViewModule } from 'src/modules/view/view.module';
import { WorkflowModule } from 'src/modules/workflow/workflow.module';
@ -15,6 +16,7 @@ import { WorkflowModule } from 'src/modules/workflow/workflow.module';
ViewModule,
WorkflowModule,
FavoriteFolderModule,
FavoriteModule,
],
providers: [],
exports: [],