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
@@ -2,15 +2,10 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
@Module({
imports: [
TypeOrmModule.forFeature([WorkspaceEntity, FeatureFlagEntity]),
WorkspaceCacheStorageModule,
],
imports: [TypeOrmModule.forFeature([FeatureFlagEntity])],
providers: [WorkspaceFeatureFlagsMapCacheService],
exports: [WorkspaceFeatureFlagsMapCacheService],
})
@@ -1,76 +1,25 @@
import { Injectable, Logger } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { TwentyORMExceptionCode } from 'src/engine/twenty-orm/exceptions/twenty-orm.exception';
import { GetDataFromCacheWithRecomputeService } from 'src/engine/workspace-cache-storage/services/get-data-from-cache-with-recompute.service';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
const FEATURE_FLAG_MAP = 'Feature flag map';
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
@Injectable()
export class WorkspaceFeatureFlagsMapCacheService {
logger = new Logger(WorkspaceFeatureFlagsMapCacheService.name);
@WorkspaceCache('featureFlagsMap')
export class WorkspaceFeatureFlagsMapCacheService extends WorkspaceCacheProvider<FeatureFlagMap> {
constructor(
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
@InjectRepository(FeatureFlagEntity)
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
private readonly getFromCacheWithRecomputeService: GetDataFromCacheWithRecomputeService<
string,
FeatureFlagMap
>,
) {}
async getWorkspaceFeatureFlagsMap({
workspaceId,
}: {
workspaceId: string;
}): Promise<FeatureFlagMap> {
const { data: workspaceFeatureFlagsMap } =
await this.getWorkspaceFeatureFlagsMapAndVersion({ workspaceId });
return workspaceFeatureFlagsMap;
) {
super();
}
async getWorkspaceFeatureFlagsMapAndVersion({
workspaceId,
}: {
workspaceId: string;
}) {
return this.getFromCacheWithRecomputeService.getFromCacheWithRecompute({
workspaceId,
getCacheData: () =>
this.workspaceCacheStorageService.getFeatureFlagsMap(workspaceId),
getCacheVersion: () =>
this.workspaceCacheStorageService.getFeatureFlagsMapVersionFromCache(
workspaceId,
),
recomputeCache: (params) => this.recomputeFeatureFlagsMapCache(params),
cachedEntityName: FEATURE_FLAG_MAP,
exceptionCode: TwentyORMExceptionCode.FEATURE_FLAG_MAP_VERSION_NOT_FOUND,
});
}
async recomputeFeatureFlagsMapCache({
workspaceId,
}: {
workspaceId: string;
}): Promise<void> {
const freshFeatureFlagMap =
await this.getFeatureFlagsMapFromDatabase(workspaceId);
await this.workspaceCacheStorageService.setFeatureFlagsMap(
workspaceId,
freshFeatureFlagMap,
);
}
private async getFeatureFlagsMapFromDatabase(workspaceId: string) {
async computeForCache(workspaceId: string): Promise<FeatureFlagMap> {
const workspaceFeatureFlags = await this.featureFlagRepository.find({
where: { workspaceId },
});