Improve infer deletion from entities (#15807)

## Context
inferDeletionFromEntities only accepts a set of keys for each entities
that needs deletion. This could be error-prone if tmr we want to add a
new side effect and forget to add the entity when in practice you want
to delete all entities that are in the fromToAllFlatEntityMaps (this is
the case for applications for example)
This commit is contained in:
Weiko
2025-11-13 18:18:27 +01:00
committed by GitHub
parent 56b3a0e8a5
commit 998365457e
7 changed files with 33 additions and 22 deletions
@@ -2,7 +2,6 @@ import { Injectable, Logger } from '@nestjs/common';
import { parse } from 'path';
import { ALL_METADATA_NAME, AllMetadataName } from 'twenty-shared/metadata';
import { isDefined } from 'twenty-shared/utils';
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
@@ -1012,15 +1011,7 @@ export class ApplicationSyncService {
workspaceId,
buildOptions: {
isSystemBuild: true,
inferDeletionFromMissingEntities: {
...Object.values(ALL_METADATA_NAME).reduce(
(acc, metadataName) => ({
...acc,
[metadataName]: true,
}),
{} as Partial<Record<AllMetadataName, boolean>>,
),
},
inferDeletionFromMissingEntities: true,
},
},
);
@@ -15,7 +15,6 @@ import {
import { featureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/feature-flag.validate';
import { publicFeatureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/is-public-feature-flag.validate';
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
@Injectable()
export class FeatureFlagService {
@@ -23,7 +22,6 @@ export class FeatureFlagService {
@InjectRepository(FeatureFlagEntity)
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
private readonly workspaceFeatureFlagsMapCacheService: WorkspaceFeatureFlagsMapCacheService,
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
) {}
public async isFeatureEnabled(
@@ -10,8 +10,6 @@ import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-sche
import { GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource';
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
const TWENTY_MINUTES_IN_MS = 120_000;
@Injectable()
export class GlobalWorkspaceDataSourceService
implements OnModuleInit, OnApplicationShutdown
@@ -38,8 +36,8 @@ export class GlobalWorkspaceDataSourceService
}
: undefined,
extra: {
query_timeout: 10000,
idleTimeoutMillis: TWENTY_MINUTES_IN_MS,
query_timeout: 10000, // 10 seconds,
idleTimeoutMillis: 120_000, // 2 minutes,
max: 4,
allowExitOnIdle: true,
},
@@ -11,6 +11,7 @@ import { type MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-en
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
import { compareTwoFlatEntity } from 'src/engine/metadata-modules/flat-entity/utils/compare-two-flat-entity.util';
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration-v2/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
import { shouldInferDeletionFromMissingEntities } from 'src/engine/workspace-manager/workspace-migration-v2/utils/should-infer-deletion-from-missing-entities.util';
import { type WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-builder-options.type';
export type DeletedCreatedUpdatedMatrix<T extends AllMetadataName> = {
@@ -54,7 +55,7 @@ export const flatEntityDeletedCreatedUpdatedMatrixDispatcher = <
const fromMap = new Map(from.map((obj) => [obj.universalIdentifier, obj]));
const toMap = new Map(to.map((obj) => [obj.universalIdentifier, obj]));
if (buildOptions.inferDeletionFromMissingEntities?.[metadataName]) {
if (shouldInferDeletionFromMissingEntities({ buildOptions, metadataName })) {
for (const [universalIdentifier, fromEntity] of fromMap) {
if (toMap.has(universalIdentifier)) {
continue;
@@ -0,0 +1,16 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-builder-options.type';
export const shouldInferDeletionFromMissingEntities = ({
buildOptions,
metadataName,
}: {
buildOptions: WorkspaceMigrationBuilderOptions;
metadataName: AllMetadataName;
}): boolean => {
return (
buildOptions.inferDeletionFromMissingEntities === true ||
buildOptions.inferDeletionFromMissingEntities?.[metadataName] === true
);
};
@@ -20,6 +20,7 @@ import { deleteFlatEntityFromFlatEntityMapsThroughMutationOrThrow } from 'src/en
import { flatEntityDeletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/utils/flat-entity-deleted-created-updated-matrix-dispatcher.util';
import { getMetadataEmptyWorkspaceMigrationActionRecord } from 'src/engine/workspace-manager/workspace-migration-v2/utils/get-metadata-empty-workspace-migration-action-record.util';
import { replaceFlatEntityInFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration-v2/utils/replace-flat-entity-in-flat-entity-maps-through-mutation-or-throw.util';
import { shouldInferDeletionFromMissingEntities } from 'src/engine/workspace-manager/workspace-migration-v2/utils/should-infer-deletion-from-missing-entities.util';
import { FailedFlatEntityValidateAndBuild } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/failed-flat-entity-validate-and-build.type';
import { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-update-validation-args.type';
import { FlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-args.type';
@@ -161,10 +162,14 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
deletedFlatEntityMaps,
);
for (const flatEntityToDeleteId in buildOptions
.inferDeletionFromMissingEntities?.[this.metadataName]
? deletedFlatEntityMaps.byId
: {}) {
const flatEntityToDeleteIds = shouldInferDeletionFromMissingEntities({
buildOptions,
metadataName: this.metadataName,
})
? Object.keys(deletedFlatEntityMaps.byId)
: [];
for (const flatEntityToDeleteId of flatEntityToDeleteIds) {
const flatEntityToDelete =
deletedFlatEntityMaps.byId[flatEntityToDeleteId];
@@ -1,6 +1,8 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
export type WorkspaceMigrationBuilderOptions = {
inferDeletionFromMissingEntities?: Partial<Record<AllMetadataName, boolean>>;
inferDeletionFromMissingEntities?:
| true
| Partial<Record<AllMetadataName, boolean>>;
isSystemBuild: boolean;
};