Add local only cache to cache service and cache typeorm entity metadata (#16287)
## Problem buildEntityMetadatas in GlobalWorkspaceOrmManager is computationally expensive and was running on every executeInWorkspaceContext call. This method uses TypeORM's EntitySchemaTransformer and EntityMetadataBuilder to build metadata for all workspace entities (30-50+ objects with many fields each). The resulting EntityMetadata[] is not serialisable which means it cannot be cached in Redis because they contain: - Circular references - Functions/methods - References to the DataSource instance ## Solution Extended the workspace cache system to support local-only caching, then created a cache provider for entityMetadatas. ## Implementation details Updated @WorkspaceCache decorator (workspace-cache.decorator.ts) - Added localOnly?: boolean option to skip Redis storage for non-serializable data Created WorkspaceEntityMetadatasCacheService - Computes entity metadatas from DB to avoid race condition, this is acceptable Simplified GlobalWorkspaceOrmManager - Now fetches entityMetadatas from cache instead of rebuilding on every call Updated Workspace migration runner - the only entry point where metadata can change - Now invalidate the new 'entityMetadata' local cache when shouldIncrementMetadataGraphqlSchemaVersion is true (== field/object mutations)
This commit is contained in:
+11
-11
@@ -1,13 +1,15 @@
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
import { type RelationType } from 'typeorm/metadata/types/RelationTypes';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import {
|
||||
RelationException,
|
||||
RelationExceptionCode,
|
||||
} from 'src/engine/twenty-orm/exceptions/relation.exception';
|
||||
import {
|
||||
type EntitySchemaFieldMetadata,
|
||||
type EntitySchemaFieldMetadataMaps,
|
||||
type EntitySchemaObjectMetadataMaps,
|
||||
} from 'src/engine/twenty-orm/global-workspace-datasource/types/entity-schema-metadata.type';
|
||||
import { converRelationTypeToTypeORMRelationType } from 'src/engine/twenty-orm/utils/convert-relation-type-to-typeorm-relation-type.util';
|
||||
|
||||
interface RelationDetails {
|
||||
@@ -18,11 +20,11 @@ interface RelationDetails {
|
||||
}
|
||||
|
||||
export function determineSchemaRelationDetails(
|
||||
fieldMetadata: FlatFieldMetadata<
|
||||
fieldMetadata: EntitySchemaFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
objectMetadataMaps: EntitySchemaObjectMetadataMaps,
|
||||
fieldMetadataMaps: EntitySchemaFieldMetadataMaps,
|
||||
): RelationDetails {
|
||||
if (!fieldMetadata.settings) {
|
||||
throw new Error('Field metadata settings are missing');
|
||||
@@ -39,12 +41,10 @@ export function determineSchemaRelationDetails(
|
||||
);
|
||||
}
|
||||
|
||||
const sourceObjectMetadata =
|
||||
flatObjectMetadataMaps.byId[fieldMetadata.objectMetadataId];
|
||||
const targetObjectMetadata =
|
||||
flatObjectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
objectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
|
||||
if (!sourceObjectMetadata || !targetObjectMetadata) {
|
||||
if (!targetObjectMetadata) {
|
||||
throw new RelationException(
|
||||
`Object metadata not found for field ${fieldMetadata.id}`,
|
||||
RelationExceptionCode.RELATION_OBJECT_METADATA_NOT_FOUND,
|
||||
@@ -59,7 +59,7 @@ export function determineSchemaRelationDetails(
|
||||
}
|
||||
|
||||
const targetFieldMetadata =
|
||||
flatFieldMetadataMaps.byId[fieldMetadata.relationTargetFieldMetadataId];
|
||||
fieldMetadataMaps.byId[fieldMetadata.relationTargetFieldMetadataId];
|
||||
|
||||
if (!targetFieldMetadata) {
|
||||
throw new Error('Target field metadata not found');
|
||||
|
||||
Reference in New Issue
Block a user