Read feature flags from cache (#11556)
We are now storing a workspace's feature flag map in our redis cache. The cache is invalidated upon feature flag update through the lab resolver.
This commit is contained in:
@ -0,0 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { Workspace } 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([Workspace, FeatureFlag], 'core'),
|
||||
WorkspaceCacheStorageModule,
|
||||
],
|
||||
providers: [WorkspaceFeatureFlagsMapCacheService],
|
||||
exports: [WorkspaceFeatureFlagsMapCacheService],
|
||||
})
|
||||
export class WorkspaceFeatureFlagsMapCacheModule {}
|
||||
@ -0,0 +1,102 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
|
||||
|
||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { TwentyORMExceptionCode } from 'src/engine/twenty-orm/exceptions/twenty-orm.exception';
|
||||
import { getFromCacheWithRecompute } from 'src/engine/utils/get-data-from-cache-with-recompute.util';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceFeatureFlagsMapCacheService {
|
||||
logger = new Logger(WorkspaceFeatureFlagsMapCacheService.name);
|
||||
|
||||
constructor(
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
@InjectRepository(FeatureFlag, 'core')
|
||||
private readonly featureFlagRepository: Repository<FeatureFlag>,
|
||||
) {}
|
||||
|
||||
async getWorkspaceFeatureFlagsMap({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<FeatureFlagMap> {
|
||||
const { data: workspaceFeatureFlagsMap } =
|
||||
await this.getWorkspaceFeatureFlagsMapAndVersion({ workspaceId });
|
||||
|
||||
return workspaceFeatureFlagsMap;
|
||||
}
|
||||
|
||||
async getWorkspaceFeatureFlagsMapAndVersion({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}) {
|
||||
return getFromCacheWithRecompute<string, FeatureFlagMap>({
|
||||
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,
|
||||
ignoreLock = false,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
ignoreLock?: boolean;
|
||||
}): Promise<void> {
|
||||
const isAlreadyCaching =
|
||||
await this.workspaceCacheStorageService.getFeatureFlagsMapOngoingCachingLock(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!ignoreLock && isAlreadyCaching) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.workspaceCacheStorageService.addFeatureFlagMapOngoingCachingLock(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const freshFeatureFlagMap =
|
||||
await this.getFeatureFlagsMapFromDatabase(workspaceId);
|
||||
|
||||
await this.workspaceCacheStorageService.setFeatureFlagsMap(
|
||||
workspaceId,
|
||||
freshFeatureFlagMap,
|
||||
);
|
||||
|
||||
await this.workspaceCacheStorageService.removeFeatureFlagsMapOngoingCachingLock(
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async getFeatureFlagsMapFromDatabase(workspaceId: string) {
|
||||
const workspaceFeatureFlags = await this.featureFlagRepository.find({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
const workspaceFeatureFlagsMap = workspaceFeatureFlags.reduce(
|
||||
(result, currentFeatureFlag) => {
|
||||
result[currentFeatureFlag.key] = currentFeatureFlag.value;
|
||||
|
||||
return result;
|
||||
},
|
||||
{} as FeatureFlagMap,
|
||||
);
|
||||
|
||||
return workspaceFeatureFlagsMap;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user