Skip to content

Commit 9ad8549

Browse files
committed
ref(perf): cache publish decision in excutor
1 parent ea6e2d5 commit 9ad8549

2 files changed

Lines changed: 21 additions & 7 deletions

File tree

src/diagnostics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export function enableDiagnosticsChannel(dc: MinimalDiagnosticsChannel): void {
139139
*
140140
* @internal
141141
*/
142-
function shouldTrace(
142+
export function shouldTrace(
143143
channel: MinimalTracingChannel | undefined,
144144
): channel is MinimalTracingChannel {
145145
// eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare

src/execution/Executor.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ import {
4444
} from '../type/definition.js';
4545
import type { GraphQLSchema } from '../type/schema.js';
4646

47-
import { maybeTraceMixed } from '../diagnostics.js';
47+
import type { MinimalTracingChannel } from '../diagnostics.js';
48+
import { getChannels, maybeTraceMixed, shouldTrace } from '../diagnostics.js';
4849

4950
import { AbortedGraphQLExecutionError } from './AbortedGraphQLExecutionError.js';
5051
import { withCancellation } from './cancellablePromise.js';
@@ -245,6 +246,12 @@ export class Executor<
245246
values: ReadonlyArray<PromiseOrValue<T>>,
246247
) => Promise<Array<T>>;
247248

249+
// Resolved once per Executor so the per-field gate in `executeField` is a
250+
// single member read + null check, not a `getChannels()?.resolve` walk +
251+
// `hasSubscribers` read on every resolution. Undefined when diagnostics
252+
// are off or nobody is listening at construction time.
253+
_resolveChannel: MinimalTracingChannel | undefined;
254+
248255
constructor(
249256
validatedExecutionArgs: ValidatedExecutionArgs,
250257
sharedExecutionContext?: SharedExecutionContext,
@@ -254,6 +261,11 @@ export class Executor<
254261
this.abortReason = defaultAbortReason;
255262
this.collectedErrors = new CollectedErrors();
256263

264+
const resolveChannel = getChannels()?.resolve;
265+
this._resolveChannel = shouldTrace(resolveChannel)
266+
? resolveChannel
267+
: undefined;
268+
257269
if (sharedExecutionContext === undefined) {
258270
this.resolverAbortController = new AbortController();
259271
this.sharedExecutionContext = createSharedExecutionContext(
@@ -615,11 +627,13 @@ export class Executor<
615627
// The resolve function's optional third argument is a context value that
616628
// is provided to every resolve function within an execution. It is commonly
617629
// used to represent an authenticated user, or request-specific caches.
618-
const result = maybeTraceMixed(
619-
'resolve',
620-
() => buildResolveCtx(info, args, fieldDef.resolve === undefined),
621-
() => resolveFn(source, args, contextValue, info),
622-
);
630+
const result = this._resolveChannel
631+
? maybeTraceMixed(
632+
'resolve',
633+
() => buildResolveCtx(info, args, fieldDef.resolve === undefined),
634+
() => resolveFn(source, args, contextValue, info),
635+
)
636+
: resolveFn(source, args, contextValue, info);
623637

624638
if (isPromiseLike(result)) {
625639
return this.completePromisedValue(

0 commit comments

Comments
 (0)