From 984944e9bcd8a06b8013c9d1d79e9d32b30a906a Mon Sep 17 00:00:00 2001 From: Weiko Date: Wed, 22 Jul 2026 01:28:19 +0200 Subject: [PATCH] Fix workspace cache flush versioned metadata (#23121) ## Problem `WorkspaceCacheStorageService.flushVersionedMetadata` never deleted several of the keys it was supposed to flush: - When called without a `metadataVersion` (the dev seeder path), it built keys ending in `:*` and passed them to `cacheStorageService.del`. `del` is an exact-key delete with no glob support, so this branch deleted nothing at all. - `setGraphQLTypeDefs` and `setGraphQLUsedScalarNames` can write applicationId-suffixed keys (`{key}:{workspaceId}:{metadataVersion}:{applicationId}`), which the flush loop never matched. On workspace deletion these SDL and scalar entries survived in Redis until the 1-week TTL expired. - The `MetadataVersion` key is stored without a version suffix (`metadata:workspace-metadata-version:{workspaceId}`), but the flush loop appended `:{metadataVersion}` to every key, so it never matched either. ## Fix `flushVersionedMetadata` now targets the key shapes that are actually written: - The `MetadataVersion` key is deleted by its exact, unversioned shape. - With a known `metadataVersion`, each versioned key gets an exact `del` on `{key}:{workspaceId}:{metadataVersion}` plus a `flushByPattern` on `{key}:{workspaceId}:{metadataVersion}:*` to catch applicationId-suffixed entries. The pattern requires the trailing colon so flushing version 1 cannot match version 12. - Without a `metadataVersion`, each versioned key is flushed with `flushByPattern` on `{key}:{workspaceId}:*`. `flushByPattern` is Redis-only, which is safe here: the cache module factory hardcodes the Redis store, and `flushGraphQLOperation` in the same service already relies on it. Review in cubic --- .../workspace-cache-storage.service.ts | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/twenty-server/src/engine/workspace-cache-storage/workspace-cache-storage.service.ts b/packages/twenty-server/src/engine/workspace-cache-storage/workspace-cache-storage.service.ts index 8b1dfe79d6..e1f02b35b7 100644 --- a/packages/twenty-server/src/engine/workspace-cache-storage/workspace-cache-storage.service.ts +++ b/packages/twenty-server/src/engine/workspace-cache-storage/workspace-cache-storage.service.ts @@ -173,18 +173,28 @@ export class WorkspaceCacheStorageService { workspaceId: string, metadataVersion?: number, ): Promise { - const metadataVersionSuffix = isDefined(metadataVersion) - ? `${metadataVersion}` - : '*'; + const { MetadataVersion, ...versionedCacheKeys } = + METADATA_VERSIONED_WORKSPACE_CACHE_KEY; - await Promise.all( - Object.values(METADATA_VERSIONED_WORKSPACE_CACHE_KEY).map( - async (key) => - await this.cacheStorageService.del( - `${key}:${workspaceId}:${metadataVersionSuffix}`, - ), + await Promise.all([ + this.cacheStorageService.del(`${MetadataVersion}:${workspaceId}`), + ...Object.values(versionedCacheKeys).flatMap((key) => + isDefined(metadataVersion) + ? [ + this.cacheStorageService.del( + `${key}:${workspaceId}:${metadataVersion}`, + ), + this.cacheStorageService.flushByPattern( + `${key}:${workspaceId}:${metadataVersion}:*`, + ), + ] + : [ + this.cacheStorageService.flushByPattern( + `${key}:${workspaceId}:*`, + ), + ], ), - ); + ]); } async flush(workspaceId: string, metadataVersion?: number): Promise {