Fix twenty-server integration test on main (#14905)

Following https://github.com/twentyhq/twenty/pull/14869

Dynamically clearing the cache, as one entry was forgotten
It seems like a permission test deletes a role that's expected to be
existing in following tests, as now cache is getting invalidated tests
are failing

Seems to be related to
https://discord.com/channels/1130383047699738754/1423768505911869460

## Singleton local cache key collision
When set for the first time caches local keys looks like
`workspaceId:undefined", singleton is shared between several cache
instances
If the given key is undefined it will read on other entity cache entry.
This commit is contained in:
Paul Rastoin
2025-10-06 15:51:48 +02:00
committed by GitHub
parent db7fe8b327
commit b870ed238f
13 changed files with 183 additions and 191 deletions
@@ -40,11 +40,13 @@ export class GetDataFromCacheWithRecomputeService<T, U> {
cachedVersion = await getCacheVersion(workspaceId);
const cacheKey = `${workspaceId}-${cachedVersion}`;
const cachedValue = this.cache.get(cacheKey);
if (isDefined(cachedVersion)) {
const cacheKey = `${workspaceId}-${cachedVersion}`;
const cachedValue = this.cache.get(cacheKey);
if (cachedValue) {
return cachedValue;
if (cachedValue) {
return cachedValue;
}
}
cachedData = await getCacheData(workspaceId);
@@ -77,6 +79,8 @@ export class GetDataFromCacheWithRecomputeService<T, U> {
}
}
const cacheKey = `${workspaceId}-${cachedVersion}`;
this.cache.set(cacheKey, {
version: cachedVersion,
data: cachedData,
@@ -20,23 +20,29 @@ import {
WorkspaceMetadataVersionExceptionCode,
} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception';
export enum WorkspaceCacheKeys {
GraphQLTypeDefs = 'graphql:type-defs',
GraphQLUsedScalarNames = 'graphql:used-scalar-names',
GraphQLOperations = 'graphql:operations',
ORMEntitySchemas = 'orm:entity-schemas',
GraphQLFeatureFlag = 'graphql:feature-flag',
MetadataObjectMetadataMaps = 'metadata:object-metadata-maps',
MetadataVersion = 'metadata:workspace-metadata-version',
FeatureFlagMap = 'feature-flag:feature-flag-map',
FeatureFlagMapVersion = 'feature-flag:feature-flag-map-version',
MetadataPermissionsRolesPermissions = 'metadata:permissions:roles-permissions',
MetadataPermissionsRolesPermissionsVersion = 'metadata:permissions:roles-permissions-version',
MetadataPermissionsUserWorkspaceRoleMap = 'metadata:permissions:user-workspace-role-map',
MetadataPermissionsUserWorkspaceRoleMapVersion = 'metadata:permissions:user-workspace-role-map-version',
MetadataPermissionsApiKeyRoleMap = 'metadata:permissions:api-key-role-map',
MetadataPermissionsApiKeyRoleMapVersion = 'metadata:permissions:api-key-role-map-version',
}
export const METADATA_VERSIONED_WORKSPACE_CACHE_KEY = {
GraphQLTypeDefs: 'graphql:type-defs',
MetadataVersion: 'metadata:workspace-metadata-version',
MetadataObjectMetadataMaps: 'metadata:object-metadata-maps',
GraphQLUsedScalarNames: 'graphql:used-scalar-names',
ORMEntitySchemas: 'orm:entity-schemas',
} as const;
export const WORKSPACE_CACHE_KEYS = {
GraphQLOperations: 'graphql:operations',
GraphQLFeatureFlag: 'graphql:feature-flag',
FeatureFlagMap: 'feature-flag:feature-flag-map',
FeatureFlagMapVersion: 'feature-flag:feature-flag-map-version',
MetadataPermissionsRolesPermissions: 'metadata:permissions:roles-permissions',
MetadataPermissionsRolesPermissionsVersion:
'metadata:permissions:roles-permissions-version',
MetadataPermissionsUserWorkspaceRoleMap:
'metadata:permissions:user-workspace-role-map',
MetadataPermissionsUserWorkspaceRoleMapVersion:
'metadata:permissions:user-workspace-role-map-version',
MetadataPermissionsApiKeyRoleMap: 'metadata:permissions:api-key-role-map',
MetadataPermissionsApiKeyRoleMapVersion:
'metadata:permissions:api-key-role-map-version',
} as const;
const TTL_ONE_WEEK = 1000 * 60 * 60 * 24 * 7;
@@ -55,7 +61,7 @@ export class WorkspaceCacheStorageService {
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return this.cacheStorageService.set<EntitySchemaOptions<any>[]>(
`${WorkspaceCacheKeys.ORMEntitySchemas}:${workspaceId}:${metadataVersion}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.ORMEntitySchemas}:${workspaceId}:${metadataVersion}`,
entitySchemas,
TTL_ONE_WEEK,
);
@@ -68,7 +74,7 @@ export class WorkspaceCacheStorageService {
): Promise<EntitySchemaOptions<any>[] | undefined> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return this.cacheStorageService.get<EntitySchemaOptions<any>[]>(
`${WorkspaceCacheKeys.ORMEntitySchemas}:${workspaceId}:${metadataVersion}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.ORMEntitySchemas}:${workspaceId}:${metadataVersion}`,
);
}
@@ -77,7 +83,7 @@ export class WorkspaceCacheStorageService {
metadataVersion: number,
): Promise<void> {
return this.cacheStorageService.set<number>(
`${WorkspaceCacheKeys.MetadataVersion}:${workspaceId}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.MetadataVersion}:${workspaceId}`,
metadataVersion,
TTL_ONE_WEEK,
);
@@ -85,7 +91,7 @@ export class WorkspaceCacheStorageService {
getMetadataVersion(workspaceId: string): Promise<number | undefined> {
return this.cacheStorageService.get<number>(
`${WorkspaceCacheKeys.MetadataVersion}:${workspaceId}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.MetadataVersion}:${workspaceId}`,
);
}
@@ -95,7 +101,7 @@ export class WorkspaceCacheStorageService {
objectMetadataMaps: ObjectMetadataMaps,
) {
return this.cacheStorageService.set<ObjectMetadataMaps>(
`${WorkspaceCacheKeys.MetadataObjectMetadataMaps}:${workspaceId}:${metadataVersion}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.MetadataObjectMetadataMaps}:${workspaceId}:${metadataVersion}`,
objectMetadataMaps,
TTL_ONE_WEEK,
);
@@ -106,7 +112,7 @@ export class WorkspaceCacheStorageService {
metadataVersion: number,
): Promise<ObjectMetadataMaps | undefined> {
return this.cacheStorageService.get<ObjectMetadataMaps>(
`${WorkspaceCacheKeys.MetadataObjectMetadataMaps}:${workspaceId}:${metadataVersion}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.MetadataObjectMetadataMaps}:${workspaceId}:${metadataVersion}`,
);
}
@@ -141,7 +147,7 @@ export class WorkspaceCacheStorageService {
typeDefs: string,
): Promise<void> {
return this.cacheStorageService.set<string>(
`${WorkspaceCacheKeys.GraphQLTypeDefs}:${workspaceId}:${metadataVersion}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.GraphQLTypeDefs}:${workspaceId}:${metadataVersion}`,
typeDefs,
TTL_ONE_WEEK,
);
@@ -152,7 +158,7 @@ export class WorkspaceCacheStorageService {
metadataVersion: number,
): Promise<string | undefined> {
return this.cacheStorageService.get<string>(
`${WorkspaceCacheKeys.GraphQLTypeDefs}:${workspaceId}:${metadataVersion}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.GraphQLTypeDefs}:${workspaceId}:${metadataVersion}`,
);
}
@@ -162,7 +168,7 @@ export class WorkspaceCacheStorageService {
usedScalarNames: string[],
): Promise<void> {
return this.cacheStorageService.set<string[]>(
`${WorkspaceCacheKeys.GraphQLUsedScalarNames}:${workspaceId}:${metadataVersion}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.GraphQLUsedScalarNames}:${workspaceId}:${metadataVersion}`,
usedScalarNames,
TTL_ONE_WEEK,
);
@@ -173,7 +179,7 @@ export class WorkspaceCacheStorageService {
metadataVersion: number,
): Promise<string[] | undefined> {
return this.cacheStorageService.get<string[]>(
`${WorkspaceCacheKeys.GraphQLUsedScalarNames}:${workspaceId}:${metadataVersion}`,
`${METADATA_VERSIONED_WORKSPACE_CACHE_KEY.GraphQLUsedScalarNames}:${workspaceId}:${metadataVersion}`,
);
}
@@ -181,7 +187,7 @@ export class WorkspaceCacheStorageService {
workspaceId: string,
): Promise<string | undefined> {
return this.cacheStorageService.get<string>(
`${WorkspaceCacheKeys.FeatureFlagMapVersion}:${workspaceId}`,
`${WORKSPACE_CACHE_KEYS.FeatureFlagMapVersion}:${workspaceId}`,
);
}
@@ -189,7 +195,7 @@ export class WorkspaceCacheStorageService {
const featureFlagMapVersion = crypto.randomUUID();
await this.cacheStorageService.set<string>(
`${WorkspaceCacheKeys.FeatureFlagMapVersion}:${workspaceId}`,
`${WORKSPACE_CACHE_KEYS.FeatureFlagMapVersion}:${workspaceId}`,
featureFlagMapVersion,
TTL_ONE_WEEK,
);
@@ -205,7 +211,7 @@ export class WorkspaceCacheStorageService {
}> {
const [, newFeatureFlagMapVersion] = await Promise.all([
this.cacheStorageService.set<FeatureFlagMap>(
`${WorkspaceCacheKeys.FeatureFlagMap}:${workspaceId}`,
`${WORKSPACE_CACHE_KEYS.FeatureFlagMap}:${workspaceId}`,
featureFlagMap,
TTL_ONE_WEEK,
),
@@ -217,7 +223,7 @@ export class WorkspaceCacheStorageService {
getFeatureFlagsMap(workspaceId: string): Promise<FeatureFlagMap | undefined> {
return this.cacheStorageService.get<FeatureFlagMap>(
`${WorkspaceCacheKeys.FeatureFlagMap}:${workspaceId}`,
`${WORKSPACE_CACHE_KEYS.FeatureFlagMap}:${workspaceId}`,
);
}
@@ -229,7 +235,7 @@ export class WorkspaceCacheStorageService {
workspaceId: string;
}): Promise<void> {
await this.cacheStorageService.flushByPattern(
`${WorkspaceCacheKeys.GraphQLOperations}:${operationName}:${workspaceId}:*`,
`${WORKSPACE_CACHE_KEYS.GraphQLOperations}:${operationName}:${workspaceId}:*`,
);
}
@@ -241,48 +247,24 @@ export class WorkspaceCacheStorageService {
? `${metadataVersion}`
: '*';
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.MetadataObjectMetadataMaps}:${workspaceId}:${metadataVersionSuffix}`,
);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.MetadataVersion}:${workspaceId}:${metadataVersionSuffix}`,
);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.GraphQLTypeDefs}:${workspaceId}:${metadataVersionSuffix}`,
);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.GraphQLUsedScalarNames}:${workspaceId}:${metadataVersionSuffix}`,
);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.ORMEntitySchemas}:${workspaceId}:${metadataVersionSuffix}`,
await Promise.all(
Object.values(METADATA_VERSIONED_WORKSPACE_CACHE_KEY).map(
async (key) =>
await this.cacheStorageService.del(
`${key}:${workspaceId}:${metadataVersionSuffix}`,
),
),
);
}
async flush(workspaceId: string, metadataVersion?: number): Promise<void> {
await this.flushVersionedMetadata(workspaceId, metadataVersion);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.MetadataPermissionsRolesPermissions}:${workspaceId}`,
);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.MetadataPermissionsRolesPermissionsVersion}:${workspaceId}`,
);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.MetadataPermissionsUserWorkspaceRoleMap}:${workspaceId}`,
);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.MetadataPermissionsUserWorkspaceRoleMapVersion}:${workspaceId}`,
);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.FeatureFlagMap}:${workspaceId}`,
);
await this.cacheStorageService.del(
`${WorkspaceCacheKeys.FeatureFlagMapVersion}:${workspaceId}`,
await Promise.all(
Object.values(WORKSPACE_CACHE_KEYS).map(
async (key) =>
await this.cacheStorageService.del(`${key}:${workspaceId}`),
),
);
}
}