diff --git a/packages/twenty-server/src/engine/core-modules/workspace/services/__tests__/workspace.service.spec.ts b/packages/twenty-server/src/engine/core-modules/workspace/services/__tests__/workspace.service.spec.ts index 7c1c77f13e..3d491ddfde 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/services/__tests__/workspace.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/services/__tests__/workspace.service.spec.ts @@ -26,6 +26,7 @@ import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-in import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; +import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; @@ -60,6 +61,12 @@ describe('WorkspaceService', () => { findOneBy: jest.fn(), }, }, + { + provide: ObjectMetadataService, + useValue: { + deleteWorkspaceAllObjectMetadata: jest.fn(), + }, + }, { provide: getRepositoryToken(UserWorkspaceEntity), useValue: { @@ -280,16 +287,10 @@ describe('WorkspaceService', () => { .spyOn(workspaceRepository, 'findOne') .mockResolvedValue(mockWorkspace); jest.spyOn(userWorkspaceRepository, 'find').mockResolvedValue([]); - jest - .spyOn(service, 'deleteMetadataSchemaCacheAndUserWorkspace') - .mockResolvedValue({} as WorkspaceEntity); await service.deleteWorkspace(mockWorkspace.id, false); expect(workspaceRepository.delete).toHaveBeenCalledWith(mockWorkspace.id); - expect( - service.deleteMetadataSchemaCacheAndUserWorkspace, - ).toHaveBeenCalled(); expect(workspaceRepository.softDelete).not.toHaveBeenCalled(); expect(workspaceCacheStorageService.flush).toHaveBeenCalledWith( mockWorkspace.id, @@ -331,9 +332,6 @@ describe('WorkspaceService', () => { .spyOn(workspaceRepository, 'findOne') .mockResolvedValue(mockWorkspace); jest.spyOn(userWorkspaceRepository, 'find').mockResolvedValue([]); - jest - .spyOn(service, 'deleteMetadataSchemaCacheAndUserWorkspace') - .mockResolvedValue({} as WorkspaceEntity); await service.deleteWorkspace(mockWorkspace.id, false); diff --git a/packages/twenty-server/src/engine/core-modules/workspace/services/workspace.service.ts b/packages/twenty-server/src/engine/core-modules/workspace/services/workspace.service.ts index c0e74f782f..5ed0bafc52 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/services/workspace.service.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/services/workspace.service.ts @@ -37,6 +37,7 @@ import { WorkspaceNotFoundDefaultError, } from 'src/engine/core-modules/workspace/workspace.exception'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; +import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; import { PermissionsException, PermissionsExceptionCode, @@ -44,8 +45,8 @@ import { } from 'src/engine/metadata-modules/permissions/permissions.exception'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; +import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { prefillCompanies } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-companies'; import { prefillDashboards } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-dashboards'; import { prefillOpportunities } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-opportunities'; @@ -102,6 +103,7 @@ export class WorkspaceService extends TypeOrmQueryService { private readonly workspaceCacheStorageService: WorkspaceCacheStorageService, private readonly subdomainManagerService: SubdomainManagerService, private readonly workspaceDataSourceService: WorkspaceDataSourceService, + private readonly objectMetadataService: ObjectMetadataService, private readonly customDomainManagerService: CustomDomainManagerService, @InjectMessageQueue(MessageQueue.deleteCascadeQueue) private readonly messageQueueService: MessageQueueService, @@ -282,20 +284,6 @@ export class WorkspaceService extends TypeOrmQueryService { }); } - /** - * @deprecated Should be removed once AddWorkspaceForeignKeysMigrationCommand has been run successfully in production - * As we will be able to rely on foreignKey delete cascading - */ - async deleteMetadataSchemaCacheAndUserWorkspace(workspace: WorkspaceEntity) { - await this.userWorkspaceService.deleteUserWorkspace({ - userWorkspaceId: workspace.id, - }); - - await this.workspaceManagerService.delete(workspace.id); - - return workspace; - } - async deleteWorkspace(id: string, softDelete = false) { const workspace = await this.workspaceRepository.findOne({ where: { id }, @@ -320,13 +308,6 @@ export class WorkspaceService extends TypeOrmQueryService { } this.logger.log(`workspace ${id} user workspaces deleted`); - await this.workspaceCacheStorageService.flush( - workspace.id, - workspace.metadataVersion, - ); - await this.flatEntityMapsCacheService.flushFlatEntityMaps({ - workspaceId: workspace.id, - }); this.logger.log(`workspace ${id} cache flushed`); if (this.billingService.isBillingEnabled()) { @@ -341,10 +322,21 @@ export class WorkspaceService extends TypeOrmQueryService { return workspace; } - await this.deleteMetadataSchemaCacheAndUserWorkspace(workspace); + // Note: not relying on workspace id FK cascade deletion here to avoid query read timeout later on workspace deletion + await this.objectMetadataService.deleteWorkspaceAllObjectMetadata({ + workspaceId: workspace.id, + }); await this.workspaceDataSourceService.deleteWorkspaceDBSchema(workspace.id); + await this.workspaceCacheStorageService.flush( + workspace.id, + workspace.metadataVersion, + ); + await this.flatEntityMapsCacheService.flushFlatEntityMaps({ + workspaceId: workspace.id, + }); + await this.messageQueueService.add( FileWorkspaceFolderDeletionJob.name, { workspaceId: id }, diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.module.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.module.ts index c37e813420..edb5df1cf8 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.module.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.module.ts @@ -31,6 +31,7 @@ import { WorkspaceResolver } from 'src/engine/core-modules/workspace/workspace.r import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.module'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module'; +import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; import { RoleModule } from 'src/engine/metadata-modules/role/role.module'; import { ViewModule } from 'src/engine/metadata-modules/view/view.module'; @@ -55,6 +56,7 @@ import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-m UserWorkspaceEntity, PublicDomainEntity, ]), + ObjectMetadataModule, UserWorkspaceModule, WorkspaceManagerModule, FeatureFlagModule, diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service.ts index 72077827e0..f095f94884 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service.ts @@ -24,6 +24,7 @@ export class WorkspaceMetadataVersionService { async incrementMetadataVersion(workspaceId: string): Promise { const workspace = await this.workspaceRepository.findOne({ where: { id: workspaceId }, + withDeleted: true, }); const metadataVersion = workspace?.metadataVersion; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-soft-deleted-suspended-workspaces.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-soft-deleted-suspended-workspaces.command.ts new file mode 100644 index 0000000000..9c4c6744e4 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-soft-deleted-suspended-workspaces.command.ts @@ -0,0 +1,106 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command, Option } from 'nest-commander'; +import { isDefined } from 'twenty-shared/utils'; +import { WorkspaceActivationStatus } from 'twenty-shared/workspace'; +import { In, IsNull, Not, Repository } from 'typeorm'; + +import { + type MigrationCommandOptions, + MigrationCommandRunner, +} from 'src/database/commands/command-runners/migration.command-runner'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { CleanerWorkspaceService } from 'src/engine/workspace-manager/workspace-cleaner/services/cleaner.workspace-service'; + +type HardDeleteSoftDeletedSuspendedWorkspacesCommandOptions = + MigrationCommandOptions & { + ignoreGracePeriod?: boolean; + }; + +@Command({ + name: 'workspace:hard-delete-soft-deleted', + description: 'Hard delete soft-deleted suspended workspaces', +}) +export class HardDeleteSoftDeletedSuspendedWorkspacesCommand extends MigrationCommandRunner { + private workspaceIds: string[] = []; + + constructor( + private readonly cleanerWorkspaceService: CleanerWorkspaceService, + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + ) { + super(); + } + + @Option({ + flags: '-w, --workspace-id [workspace_id]', + description: + 'workspace id. Command runs on all suspended workspaces if not provided', + required: false, + }) + parseWorkspaceId(val: string): string[] { + this.workspaceIds.push(val); + + return this.workspaceIds; + } + + @Option({ + flags: '--ignore-grace-period', + description: + 'Ignore the grace period and hard delete soft-deleted workspaces immediately', + required: false, + }) + parseIgnoreGracePeriod(): boolean { + return true; + } + + async fetchSuspendedSoftDeletedWorkspaces(): Promise { + return await this.workspaceRepository.find({ + where: { + activationStatus: In([WorkspaceActivationStatus.SUSPENDED]), + deletedAt: Not(IsNull()), + ...(this.workspaceIds.length > 0 ? { id: In(this.workspaceIds) } : {}), + }, + withDeleted: true, + }); + } + + override async runMigrationCommand( + _passedParams: string[], + options: HardDeleteSoftDeletedSuspendedWorkspacesCommandOptions, + ): Promise { + const { dryRun, ignoreGracePeriod } = options; + + const softDeletedSuspendedWorkspaces = + await this.fetchSuspendedSoftDeletedWorkspaces(); + let deletedWorkspaceCounter = 0; + + this.logger.log( + `${dryRun ? 'DRY RUN - ' : ''}${ignoreGracePeriod ? 'IGNORING GRACE PERIOD - ' : ''}Iterating over ${softDeletedSuspendedWorkspaces.length} soft deleted suspended workspaces`, + ); + + for (const workspace of softDeletedSuspendedWorkspaces) { + try { + const result = + await this.cleanerWorkspaceService.hardDeleteSoftDeletedWorkspace({ + workspace, + dryRun, + ignoreGracePeriod, + }); + + if (isDefined(result)) { + deletedWorkspaceCounter++; + } + } catch (error) { + this.logger.error( + `Failed to destroy soft deleted workspace ${workspace.id}`, + error, + ); + } + } + + this.logger.log( + `Destroyed ${deletedWorkspaceCounter}/${softDeletedSuspendedWorkspaces.length}`, + ); + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.command.ts index ab73608765..2dab102a03 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.command.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.command.ts @@ -65,9 +65,9 @@ export class CleanSuspendedWorkspacesCommand extends MigrationCommandRunner { `${dryRun ? 'DRY RUN - ' : ''}Cleaning ${suspendedWorkspaceIds.length} suspended workspaces`, ); - await this.cleanerWorkspaceService.batchWarnOrCleanSuspendedWorkspaces( - suspendedWorkspaceIds, + await this.cleanerWorkspaceService.batchWarnOrCleanSuspendedWorkspaces({ + workspaceIds: suspendedWorkspaceIds, dryRun, - ); + }); } } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/crons/clean-suspended-workspaces.job.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/crons/clean-suspended-workspaces.job.ts index 4ba827f7ef..3fcdde8788 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/crons/clean-suspended-workspaces.job.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/crons/clean-suspended-workspaces.job.ts @@ -33,8 +33,8 @@ export class CleanSuspendedWorkspacesJob { withDeleted: true, }); - await this.cleanerWorkspaceService.batchWarnOrCleanSuspendedWorkspaces( - suspendedWorkspaceIds.map((workspace) => workspace.id), - ); + await this.cleanerWorkspaceService.batchWarnOrCleanSuspendedWorkspaces({ + workspaceIds: suspendedWorkspaceIds.map((workspace) => workspace.id), + }); } } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/services/cleaner.workspace-service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/services/cleaner.workspace-service.ts index 8844c6e70e..87764a404c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/services/cleaner.workspace-service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/services/cleaner.workspace-service.ts @@ -24,7 +24,6 @@ import { UserService } from 'src/engine/core-modules/user/services/user.service' import { UserVarsService } from 'src/engine/core-modules/user/user-vars/services/user-vars.service'; import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { USER_WORKSPACE_DELETION_WARNING_SENT_KEY } from 'src/engine/workspace-manager/workspace-cleaner/constants/user-workspace-deletion-warning-sent-key.constant'; import { WorkspaceCleanerException, @@ -49,7 +48,6 @@ export class CleanerWorkspaceService { private readonly workspaceRepository: Repository, @InjectRepository(BillingSubscriptionEntity) private readonly billingSubscriptionRepository: Repository, - private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, @InjectRepository(UserWorkspaceEntity) private readonly userWorkspaceRepository: Repository, private readonly i18nService: I18nService, @@ -331,10 +329,56 @@ export class CleanerWorkspaceService { } } - async batchWarnOrCleanSuspendedWorkspaces( - workspaceIds: string[], + async hardDeleteSoftDeletedWorkspace({ + workspace, + ignoreGracePeriod = false, dryRun = false, - ): Promise { + }: { + ignoreGracePeriod?: boolean; + dryRun?: boolean; + workspace: WorkspaceEntity; + }): Promise { + if (!isDefined(workspace.deletedAt)) { + return; + } + + const daysSinceSoftDeleted = workspace.deletedAt + ? differenceInDays(new Date(), workspace.deletedAt) + : 0; + + const hasPassedGracePeriod = + daysSinceSoftDeleted > + this.inactiveDaysBeforeDelete - this.inactiveDaysBeforeSoftDelete; + + const canHardDelete = ignoreGracePeriod || hasPassedGracePeriod; + + if (!canHardDelete) { + return; + } + + this.logger.log( + `${dryRun ? 'DRY RUN - ' : ''}Destroying workspace ${workspace.id} ${workspace.displayName}`, + ); + if (dryRun) { + return; + } + + await this.workspaceService.deleteWorkspace(workspace.id); + this.metricsService.incrementCounter({ + key: MetricsKeys.CronJobDeletedWorkspace, + shouldStoreInCache: false, + }); + + return workspace; + } + + async batchWarnOrCleanSuspendedWorkspaces({ + workspaceIds, + dryRun = false, + }: { + workspaceIds: string[]; + dryRun?: boolean; + }): Promise { this.logger.log( `${dryRun ? 'DRY RUN - ' : ''}batchWarnOrCleanSuspendedWorkspaces running...`, ); @@ -356,29 +400,18 @@ export class CleanerWorkspaceService { try { const isSoftDeletedWorkspace = isDefined(workspace.deletedAt); + const isWithinDeletionLimit = + deletedWorkspacesCount < + this.maxNumberOfWorkspacesDeletedPerExecution; - if (isSoftDeletedWorkspace) { - const daysSinceSoftDeleted = workspace.deletedAt - ? differenceInDays(new Date(), workspace.deletedAt) - : 0; + if (isSoftDeletedWorkspace && isWithinDeletionLimit) { + const result = await this.hardDeleteSoftDeletedWorkspace({ + workspace, + dryRun, + ignoreGracePeriod: false, + }); - if ( - daysSinceSoftDeleted > - this.inactiveDaysBeforeDelete - - this.inactiveDaysBeforeSoftDelete && - deletedWorkspacesCount < - this.maxNumberOfWorkspacesDeletedPerExecution - ) { - this.logger.log( - `${dryRun ? 'DRY RUN - ' : ''}Destroying workspace ${workspace.id} ${workspace.displayName}`, - ); - if (!dryRun) { - await this.workspaceService.deleteWorkspace(workspace.id); - this.metricsService.incrementCounter({ - key: MetricsKeys.CronJobDeletedWorkspace, - shouldStoreInCache: false, - }); - } + if (isDefined(result)) { deletedWorkspacesCount++; } continue; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/workspace-cleaner.module.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/workspace-cleaner.module.ts index 3c1f03229a..826ad675f0 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/workspace-cleaner.module.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/workspace-cleaner.module.ts @@ -13,6 +13,7 @@ import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.mod import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { CleanOnboardingWorkspacesCommand } from 'src/engine/workspace-manager/workspace-cleaner/commands/clean-onboarding-workspaces.command'; import { CleanOnboardingWorkspacesCronCommand } from 'src/engine/workspace-manager/workspace-cleaner/commands/clean-onboarding-workspaces.cron.command'; +import { HardDeleteSoftDeletedSuspendedWorkspacesCommand } from 'src/engine/workspace-manager/workspace-cleaner/commands/clean-soft-deleted-suspended-workspaces.command'; import { CleanSuspendedWorkspacesCommand } from 'src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.command'; import { CleanSuspendedWorkspacesCronCommand } from 'src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.cron.command'; import { DeleteWorkspacesCommand } from 'src/engine/workspace-manager/workspace-cleaner/commands/delete-workspaces.command'; @@ -39,6 +40,7 @@ import { CleanerWorkspaceService } from 'src/engine/workspace-manager/workspace- DestroyWorkspaceCommand, CleanSuspendedWorkspacesCronCommand, CleanSuspendedWorkspacesCommand, + HardDeleteSoftDeletedSuspendedWorkspacesCommand, CleanOnboardingWorkspacesCommand, CleanOnboardingWorkspacesCronCommand, CleanerWorkspaceService, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-manager.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-manager.service.ts index ed9dd3b628..923c2de3c7 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-manager.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-manager.service.ts @@ -8,12 +8,9 @@ import { FlatApplication } from 'src/engine/core-modules/application/types/flat- import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; -import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity'; import { ADMIN_ROLE } from 'src/engine/metadata-modules/role/constants/admin-role'; import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { RoleService } from 'src/engine/metadata-modules/role/role.service'; -import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service'; import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { TwentyStandardApplicationService } from 'src/engine/workspace-manager/twenty-standard-application/services/twenty-standard-application.service'; @@ -24,7 +21,6 @@ export class WorkspaceManagerService { constructor( private readonly workspaceDataSourceService: WorkspaceDataSourceService, - private readonly objectMetadataService: ObjectMetadataService, private readonly dataSourceService: DataSourceService, @InjectRepository(UserWorkspaceEntity) private readonly userWorkspaceRepository: Repository, @@ -35,10 +31,6 @@ export class WorkspaceManagerService { private readonly workspaceRepository: Repository, @InjectRepository(RoleEntity) private readonly roleRepository: Repository, - @InjectRepository(RoleTargetEntity) - private readonly roleTargetRepository: Repository, - @InjectRepository(ServerlessFunctionEntity) - private readonly serverlessFunctionRepository: Repository, private readonly applicationService: ApplicationService, ) {} @@ -99,29 +91,6 @@ export class WorkspaceManagerService { }); } - /** - * @deprecated Should be removed once AddWorkspaceForeignKeysMigrationCommand has been run successfully in production - * As we will be able to rely on foreignKey delete cascading - */ - public async delete(workspaceId: string): Promise { - await this.roleTargetRepository.delete({ - workspaceId, - }); - await this.roleRepository.delete({ - workspaceId, - }); - - await this.serverlessFunctionRepository.delete({ - workspaceId, - }); - - await this.objectMetadataService.deleteWorkspaceAllObjectMetadata({ - workspaceId, - }); - - await this.dataSourceService.delete(workspaceId); - } - private async setupDefaultRoles({ userId, workspaceId, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service.ts index 875b3ed4a0..3d18094f0e 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service.ts @@ -199,6 +199,12 @@ export class WorkspaceMigrationRunnerService { ); if (invalidationFailures.length > 0) { + invalidationFailures.forEach((err) => + this.logger.error( + `Failed to invalidate a legacy cache ${err.reason}`, + 'Runner', + ), + ); throw new Error( `Failed to invalidate ${invalidationFailures.length} cache operations`, );