Perf investigation - Time qgl query (#18911)

Added a new IS_GRAPHQL_QUERY_TIMING_ENABLED feature flag that, when
activated per workspace, logs the execution time and operation name of
every GraphQL query across both the standard Yoga pipeline and the
direct execution fast path. The timing context propagates via
AsyncLocalStorage to also instrument buildColumnsToSelect and
formatResult — giving a breakdown of column selection and result
transformation costs without any caller changes.
This commit is contained in:
Etienne
2026-03-24 14:41:15 +01:00
committed by GitHub
parent d3c7b0131d
commit 56ea79d98c
13 changed files with 181 additions and 11 deletions
@@ -1,3 +1,5 @@
import { Logger } from '@nestjs/common';
import { type Request } from 'express';
import { DocumentNode, parse } from 'graphql';
import { type Plugin } from 'graphql-yoga';
@@ -5,12 +7,15 @@ import { FeatureFlagKey } from 'twenty-shared/types';
import { isNull } from '@sniptt/guards';
import { type DirectExecutionService } from 'src/engine/api/graphql/direct-execution/direct-execution.service';
import { queryTimingContextStorage } from 'src/engine/core-modules/graphql/storage/query-timing-context.storage';
import { computeSkipWorkspaceSchemaCreation } from 'src/engine/api/graphql/direct-execution/utils/compute-skip-workspace-schema-creation.util';
import { findOperationDefinition } from 'src/engine/api/graphql/direct-execution/utils/find-operation-definition.util';
import { hasOnlyGeneratedWorkspaceResolvers } from 'src/engine/api/graphql/direct-execution/utils/has-only-generated-workspace-resolvers.util';
import { isSubscriptionOperation } from 'src/engine/api/graphql/direct-execution/utils/is-subscription-operation.util';
import { type FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
const logger = new Logger('GraphQLQueryTiming');
export type DirectExecutionPluginConfig = {
directExecutionService: DirectExecutionService;
featureFlagService: FeatureFlagService;
@@ -27,12 +32,13 @@ export function useDirectExecution(
return;
}
const isEnabled = await config.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_DIRECT_GRAPHQL_EXECUTION_ENABLED,
req.workspace.id,
);
const isDirectExecutionEnabled =
await config.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_DIRECT_GRAPHQL_EXECUTION_ENABLED,
req.workspace.id,
);
if (!isEnabled) {
if (!isDirectExecutionEnabled) {
return;
}
@@ -83,6 +89,33 @@ export function useDirectExecution(
return;
}
const isTimingEnabled = await config.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_GRAPHQL_QUERY_TIMING_ENABLED,
req.workspace.id,
);
const resolvedOperationName = operationName ?? 'Anonymous';
if (isTimingEnabled) {
const startTime = performance.now();
const timedResult = await queryTimingContextStorage.run(true, () =>
config.directExecutionService.execute(req, document),
);
const durationMs = (performance.now() - startTime).toFixed(2);
logger.log(
`[direct-execution] ${resolvedOperationName}${durationMs}ms (workspace: ${req.workspace.id})`,
);
if (isNull(timedResult)) {
return;
}
return endResponse(Response.json(timedResult));
}
const result = await config.directExecutionService.execute(req, document);
if (isNull(result)) {