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:
Weiko
2025-12-03 19:50:40 +01:00
committed by GitHub
parent 9c334b5f03
commit 3d95d6ca00
20 changed files with 775 additions and 533 deletions
@@ -63,18 +63,23 @@ export class UserRoleService {
userWorkspaceId,
}: {
workspaceId: string;
userWorkspaceId?: string;
}): Promise<string | undefined> {
if (!isDefined(userWorkspaceId)) {
return;
}
userWorkspaceId: string;
}): Promise<string> {
const { userWorkspaceRoleMap } =
await this.workspaceCacheService.getOrRecompute(workspaceId, [
'userWorkspaceRoleMap',
]);
return userWorkspaceRoleMap[userWorkspaceId];
const roleId = userWorkspaceRoleMap[userWorkspaceId];
if (!isDefined(roleId)) {
throw new PermissionsException(
`User workspace ${userWorkspaceId} has no role assigned`,
PermissionsExceptionCode.NO_ROLE_FOUND_FOR_USER_WORKSPACE,
);
}
return roleId;
}
public async getRolesByUserWorkspaces({