From 35e6b1332961aaa31d0408a9e331b43e109f8e0b Mon Sep 17 00:00:00 2001 From: Sonarly Claude Code Date: Fri, 6 Mar 2026 22:57:04 +0000 Subject: [PATCH] chore: improve monitoring for Race condition between cache invalidation of flat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Monitoring improvement:** Added structured logging to `WorkspaceSchemaFactory` that records key diagnostic context on every schema build: - Whether typeDefs were served from cache or freshly generated - The `metadataVersion` used for the cache key - The number of objects in `flatObjectMetadataMaps` This log line fires on every `POST /graphql` request and provides the exact data needed to diagnose future cache desync issues. When a "defined in resolvers, but not in schema" error occurs, the preceding log entry will show `typeDefs=cached` with a metadata version that doesn't match the object count — immediately revealing the stale cache condition. Added `Logger` from `@nestjs/common` (consistent with the pattern used in `WorkspaceResolverFactory` and other services in the codebase). --- .../api/graphql/workspace-schema.factory.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts index 6f8c8258acf..20d762cf340 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { GraphQLSchema, printSchema } from 'graphql'; @@ -27,6 +27,8 @@ import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty @Injectable() export class WorkspaceSchemaFactory { + private readonly logger = new Logger(WorkspaceSchemaFactory.name); + constructor( private readonly dataSourceService: DataSourceService, private readonly scalarsExplorerService: ScalarsExplorerService, @@ -146,7 +148,9 @@ export class WorkspaceSchemaFactory { applicationId, ); - if (!typeDefs || !usedScalarNames) { + const typeDefsFromCache = isDefined(typeDefs) && isDefined(usedScalarNames); + + if (!typeDefsFromCache) { const autoGeneratedSchema = await this.workspaceGraphQLSchemaGenerator.generateSchema({ flatObjectMetadataMaps, @@ -172,6 +176,16 @@ export class WorkspaceSchemaFactory { ); } + const objectCount = Object.keys( + flatObjectMetadataMaps.byUniversalIdentifier, + ).length; + + this.logger.log( + `Building schema for workspace ${workspace.id} ` + + `(metadataVersion=${metadataVersion}, objects=${objectCount}, ` + + `typeDefs=${typeDefsFromCache ? 'cached' : 'generated'})`, + ); + const autoGeneratedResolvers = await this.workspaceResolverFactory.create( flatObjectMetadataMaps, flatFieldMetadataMaps,