Add redis to useMetadataCache yoga plugin (#5194)

## Context
@lucasbordeau introduced a new Yoga plugin that allows us to cache our
requests (👏), see https://github.com/twentyhq/twenty/pull/5189
I'm simply updating the implementation to allow us to use different
cache storage types such as redis
Also adding a check so it does not use cache for other operations than
ObjectMetadataItems

## Test
locally, first call takes 340ms, 2nd takes 30ms with 'redis' and 13ms
with 'memory'
This commit is contained in:
Weiko 2024-04-26 19:27:09 +02:00 committed by GitHub
parent 5e143f1f49
commit ebc25c8695
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 43 additions and 12 deletions

View File

@ -1,8 +1,12 @@
import { Plugin } from 'graphql-yoga';
export function useCachedMetadata(): Plugin {
const cache = new Map<string, any>();
export type CacheMetadataPluginConfig = {
cacheGetter: (key: string) => any;
cacheSetter: (key: string, value: any) => void;
operationsToCache: string[];
};
export function useCachedMetadata(config: CacheMetadataPluginConfig): Plugin {
const computeCacheKey = (serverContext: any) => {
const workspaceId = serverContext.req.workspace?.id ?? 'anonymous';
const cacheVersion = serverContext.req.cacheVersion ?? '0';
@ -10,28 +14,37 @@ export function useCachedMetadata(): Plugin {
return `${workspaceId}:${cacheVersion}`;
};
const getOperationName = (serverContext: any) =>
serverContext?.req?.body?.operationName;
return {
onRequest: ({ endResponse, serverContext }) => {
onRequest: async ({ endResponse, serverContext }) => {
if (!config.operationsToCache.includes(getOperationName(serverContext))) {
return;
}
const cacheKey = computeCacheKey(serverContext);
const foundInCache = cache.has(cacheKey);
if (foundInCache) {
const cachedResponse = cache.get(cacheKey);
const cachedResponse = await config.cacheGetter(cacheKey);
if (cachedResponse) {
const earlyResponse = Response.json(cachedResponse);
return endResponse(earlyResponse);
}
},
onResponse: async ({ response, serverContext }) => {
if (!config.operationsToCache.includes(getOperationName(serverContext))) {
return;
}
const cacheKey = computeCacheKey(serverContext);
const foundInCache = cache.has(cacheKey);
const cachedResponse = await config.cacheGetter(cacheKey);
if (!foundInCache) {
if (!cachedResponse) {
const responseBody = await response.json();
cache.set(cacheKey, responseBody);
config.cacheSetter(cacheKey, responseBody);
}
},
};

View File

@ -12,6 +12,8 @@ import { EnvironmentService } from 'src/engine/integrations/environment/environm
import { ExceptionHandlerService } from 'src/engine/integrations/exception-handler/exception-handler.service';
import { DataloaderModule } from 'src/engine/dataloaders/dataloader.module';
import { DataloaderService } from 'src/engine/dataloaders/dataloader.service';
import { CacheStorageNamespace } from 'src/engine/integrations/cache-storage/types/cache-storage-namespace.enum';
import { CacheStorageModule } from 'src/engine/integrations/cache-storage/cache-storage.module';
@Module({
imports: [
@ -19,11 +21,17 @@ import { DataloaderService } from 'src/engine/dataloaders/dataloader.service';
driver: YogaDriver,
useFactory: metadataModuleFactory,
imports: [GraphQLConfigModule, DataloaderModule],
inject: [EnvironmentService, ExceptionHandlerService, DataloaderService],
inject: [
EnvironmentService,
ExceptionHandlerService,
DataloaderService,
CacheStorageNamespace.WorkspaceSchema,
],
}),
MetadataEngineModule,
WorkspaceMigrationRunnerModule,
WorkspaceMigrationModule,
CacheStorageModule,
],
})
export class MetadataGraphQLApiModule {}

View File

@ -9,11 +9,13 @@ import { MetadataGraphQLApiModule } from 'src/engine/api/graphql/metadata-graphq
import { renderApolloPlayground } from 'src/engine/utils/render-apollo-playground.util';
import { DataloaderService } from 'src/engine/dataloaders/dataloader.service';
import { useCachedMetadata } from 'src/engine/api/graphql/graphql-config/hooks/use-cached-metadata';
import { CacheStorageService } from 'src/engine/integrations/cache-storage/cache-storage.service';
export const metadataModuleFactory = async (
environmentService: EnvironmentService,
exceptionHandlerService: ExceptionHandlerService,
dataloaderService: DataloaderService,
workspaceSchemaCacheStorage: CacheStorageService,
): Promise<YogaDriverConfig> => {
const config: YogaDriverConfig = {
autoSchemaFile: true,
@ -33,7 +35,15 @@ export const metadataModuleFactory = async (
useExceptionHandler({
exceptionHandlerService,
}),
useCachedMetadata(),
useCachedMetadata({
cacheGetter: workspaceSchemaCacheStorage.get.bind(
workspaceSchemaCacheStorage,
),
cacheSetter: workspaceSchemaCacheStorage.set.bind(
workspaceSchemaCacheStorage,
),
operationsToCache: ['ObjectMetadataItems'],
}),
],
path: '/metadata',
context: () => ({