fix: replace slow deep-equal with fastDeepEqual to resolve CPU bottleneck (#19771)
## Summary - Replaced the `deep-equal` npm package with the existing `fastDeepEqual` from `twenty-shared/utils` across 5 files in the server and shared packages - `deep-equal` was causing severe CPU overhead in the record update hot path (`executeMany` → `formatTwentyOrmEventToDatabaseBatchEvent` → `objectRecordChangedValues` → `deepEqual`, called **per field per record**) - `fastDeepEqual` is ~100x faster for plain JSON database records since it skips unnecessary prototype chain inspection and edge-case handling - Removed the now-unnecessary `LARGE_JSON_FIELDS` branching in `objectRecordChangedValues` since all fields now use the fast implementation
This commit is contained in:
+2
-2
@@ -1,5 +1,5 @@
|
||||
import deepEqual from 'deep-equal';
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
import { fastDeepEqual } from 'twenty-shared/utils';
|
||||
|
||||
import { type BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
|
||||
@@ -13,7 +13,7 @@ export const objectRecordChangedProperties = <
|
||||
) => {
|
||||
const changedProperties = Object.keys(newRecord).filter(
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
(key) => !deepEqual(oldRecord[key], newRecord[key]),
|
||||
(key) => !fastDeepEqual(oldRecord[key], newRecord[key]),
|
||||
);
|
||||
|
||||
return changedProperties;
|
||||
|
||||
+1
-27
@@ -1,7 +1,5 @@
|
||||
import deepEqual from 'deep-equal';
|
||||
import { FieldMetadataType, type ObjectRecord } from 'twenty-shared/types';
|
||||
import { fastDeepEqual } from 'twenty-shared/utils';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
@@ -9,26 +7,6 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
const LARGE_JSON_FIELDS: Record<string, Set<string>> = {
|
||||
[STANDARD_OBJECTS.workflowVersion.universalIdentifier]: new Set([
|
||||
'steps',
|
||||
'trigger',
|
||||
]),
|
||||
[STANDARD_OBJECTS.workflowAutomatedTrigger.universalIdentifier]: new Set([
|
||||
'settings',
|
||||
]),
|
||||
[STANDARD_OBJECTS.workflowRun.universalIdentifier]: new Set(['state']),
|
||||
};
|
||||
|
||||
const isLargeJsonField = (
|
||||
objectMetadataItem: Pick<FlatObjectMetadata, 'universalIdentifier'>,
|
||||
key: string,
|
||||
): boolean => {
|
||||
const universalIdentifier = objectMetadataItem.universalIdentifier;
|
||||
|
||||
return LARGE_JSON_FIELDS[universalIdentifier]?.has(key) ?? false;
|
||||
};
|
||||
|
||||
export const objectRecordChangedValues = (
|
||||
oldRecord: Partial<ObjectRecord>,
|
||||
newRecord: Partial<ObjectRecord>,
|
||||
@@ -62,11 +40,7 @@ export const objectRecordChangedValues = (
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (isLargeJsonField(objectMetadataItem, key)) {
|
||||
if (fastDeepEqual(oldRecordValue, newRecordValue)) {
|
||||
return acc;
|
||||
}
|
||||
} else if (deepEqual(oldRecordValue, newRecordValue)) {
|
||||
if (fastDeepEqual(oldRecordValue, newRecordValue)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -1,7 +1,10 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import deepEqual from 'deep-equal';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { getUniqueConstraintsFields, isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
fastDeepEqual,
|
||||
getUniqueConstraintsFields,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
@@ -326,7 +329,7 @@ const checkUniqueConstraintsAreSameOrThrow = (
|
||||
uniqueConstraintFields: FlatFieldMetadata<FieldMetadataType>[],
|
||||
) => {
|
||||
if (
|
||||
!deepEqual(
|
||||
!fastDeepEqual(
|
||||
relationConnectQueryConfig.uniqueConstraintFields,
|
||||
uniqueConstraintFields,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user