Refactor workspace cache service (#16208)

## Context
We've recently introduced a new workspace cache service which now acts
as a cache access and local storage for all workspace related data,
deprecating the individual specific services.
- Better performance through multiple caching/fetching strategies
- Consistent data access patterns across the codebase
- Reduced redis queries through MGET/MSET/PIPELINE with multiple cache
keys
This commit is contained in:
Weiko
2025-12-01 17:08:21 +01:00
committed by GitHub
parent 4e0545ebc5
commit 1eb2e44058
77 changed files with 1306 additions and 1863 deletions
@@ -1,10 +1,11 @@
import { Module } from '@nestjs/common';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
import { WorkspaceFlatMapCacheModule } from 'src/engine/workspace-flat-map-cache/workspace-flat-map-cache.module';
@Module({
imports: [WorkspaceFlatMapCacheModule],
imports: [WorkspaceFlatMapCacheModule, WorkspaceCacheModule],
providers: [WorkspaceManyOrAllFlatEntityMapsCacheService],
exports: [WorkspaceManyOrAllFlatEntityMapsCacheService],
})
@@ -1,66 +1,12 @@
import { Injectable, Logger } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
import { Injectable } from '@nestjs/common';
import { ALL_FLAT_ENTITY_MAPS_PROPERTIES } from 'src/engine/metadata-modules/flat-entity/constant/all-flat-entity-maps-properties.constant';
import { AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { WorkspaceFlatMapCacheRegistryService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache-registry.service';
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
@Injectable()
export class WorkspaceManyOrAllFlatEntityMapsCacheService {
private readonly logger = new Logger(
WorkspaceManyOrAllFlatEntityMapsCacheService.name,
);
constructor(
private readonly cacheRegistry: WorkspaceFlatMapCacheRegistryService,
) {}
private async executeActionForManyOrAllFlatEntity<
K extends (keyof AllFlatEntityMaps)[] = (keyof AllFlatEntityMaps)[],
>({
action,
flatMapsKeys,
}: {
flatMapsKeys: K | undefined;
action: (args: {
service: WorkspaceFlatMapCacheService<AllFlatEntityMaps[K[number]]>;
flatMapKey: K[number];
}) => Promise<void>;
}): Promise<void> {
const keysToProcess = isDefined(flatMapsKeys)
? flatMapsKeys
: ALL_FLAT_ENTITY_MAPS_PROPERTIES;
const results = await Promise.allSettled(
keysToProcess.map(async (flatMapKey) => {
try {
const service = this.cacheRegistry.getCacheServiceOrThrow(
flatMapKey as K[number],
);
return await action({
flatMapKey: flatMapKey,
service,
});
} catch (error) {
this.logger.error(
`Failed to run action on flat entity maps of ${flatMapKey}`,
error,
);
throw error;
}
}),
);
const failures = results.filter((result) => result.status === 'rejected');
if (failures.length > 0) {
this.logger.error(`${failures.length} operations failed`);
throw new Error(`Failed to process ${failures.length} flat entity maps`);
}
}
constructor(private readonly workspaceCacheService: WorkspaceCacheService) {}
public async getOrRecomputeManyOrAllFlatEntityMaps<
T extends (keyof AllFlatEntityMaps)[] = (keyof AllFlatEntityMaps)[],
@@ -71,20 +17,10 @@ export class WorkspaceManyOrAllFlatEntityMapsCacheService {
workspaceId: string;
flatMapsKeys?: T;
}): Promise<Pick<AllFlatEntityMaps, T[number]>> {
let pickedFlatEntityMaps = {} as Pick<AllFlatEntityMaps, T[number]>;
await this.executeActionForManyOrAllFlatEntity({
action: async ({ service, flatMapKey }) => {
const cacheResult = await service.getExistingOrRecomputeFlatMaps({
workspaceId,
});
pickedFlatEntityMaps[flatMapKey] = cacheResult;
},
flatMapsKeys,
});
return pickedFlatEntityMaps;
return await this.workspaceCacheService.getOrRecompute(
workspaceId,
flatMapsKeys ?? ALL_FLAT_ENTITY_MAPS_PROPERTIES,
);
}
public async invalidateFlatEntityMaps<
@@ -96,11 +32,10 @@ export class WorkspaceManyOrAllFlatEntityMapsCacheService {
workspaceId: string;
flatMapsKeys?: T;
}): Promise<void> {
await this.executeActionForManyOrAllFlatEntity({
action: async ({ service }) =>
await service.invalidateCache({ workspaceId }),
flatMapsKeys,
});
await this.workspaceCacheService.invalidateAndRecompute(
workspaceId,
flatMapsKeys ?? ALL_FLAT_ENTITY_MAPS_PROPERTIES,
);
}
public async flushFlatEntityMaps<
@@ -112,9 +47,9 @@ export class WorkspaceManyOrAllFlatEntityMapsCacheService {
workspaceId: string;
flatMapsKeys?: T;
}): Promise<void> {
await this.executeActionForManyOrAllFlatEntity({
action: async ({ service }) => await service.flushCache({ workspaceId }),
flatMapsKeys,
});
await this.workspaceCacheService.flush(
workspaceId,
flatMapsKeys ?? ALL_FLAT_ENTITY_MAPS_PROPERTIES,
);
}
}