Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracing #1617

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Tracing #1617

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/angry-spies-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'graphql-modules': minor
---

Tracing of internals
3 changes: 3 additions & 0 deletions packages/graphql-modules/src/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { subscriptionCreator } from './subscription';
import { apolloSchemaCreator, apolloExecutorCreator } from './apollo';
import { operationControllerCreator } from './operation-controller';
import { Module } from '../module/types';
import { emptyTracing } from '../shared/tracing';

export type ModulesMap = Map<ID, ResolvedModule>;

Expand Down Expand Up @@ -62,6 +63,7 @@ export function createApplication(
applicationConfig: ApplicationConfig
): Application {
function applicationFactory(cfg?: ApplicationConfig): Application {
const tracing = applicationConfig.tracing ?? emptyTracing;
const config = cfg || applicationConfig;
const providers =
config.providers && typeof config.providers === 'function'
Expand Down Expand Up @@ -125,6 +127,7 @@ export function createApplication(
modulesMap: modulesMap,
singletonGlobalProvidersMap,
operationGlobalProvidersMap,
tracing,
});

const createOperationController = operationControllerCreator({
Expand Down
35 changes: 30 additions & 5 deletions packages/graphql-modules/src/application/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { InternalAppContext, ModulesMap } from './application';
import { attachGlobalProvidersMap } from './di';
import { CONTEXT } from './tokens';
import { executionContext, ExecutionContextPicker } from './execution-context';
import type { Tracing } from '../shared/tracing';

export type ExecutionContextBuilder<
TContext extends {
Expand All @@ -20,6 +21,7 @@ export type ExecutionContextBuilder<
};

export function createContextBuilder({
tracing,
appInjector,
modulesMap,
appLevelOperationProviders,
Expand All @@ -35,13 +37,18 @@ export function createContextBuilder({
[key: string]: string;
};
modulesMap: ModulesMap;
tracing: Tracing;
}) {
// This is very critical. It creates an execution context.
// It has to run on every operation.

const contextBuilder: ExecutionContextBuilder<GraphQLModules.GlobalContext> = (
context
) => {
const tracer = tracing.create({ session: tracing.session(context) });
const tracerContextEnd = tracer.onContext({
id: 'application',
});
// Cache for context per module
let contextCache: Record<ID, GraphQLModules.ModuleContext> = {};
// A list of providers with OnDestroy hooks
Expand All @@ -59,6 +66,11 @@ export function createContextBuilder({
});
}

const operationAppInjectorName = 'App (Operation Scope)';
const tracerInjectorEnd = tracer.onInjector({
name: operationAppInjectorName,
});

let appContext: GraphQLModules.AppContext;

attachGlobalProvidersMap({
Expand Down Expand Up @@ -99,7 +111,7 @@ export function createContextBuilder({
// Application level
// Operation scoped - means it's created and destroyed on every GraphQL Operation
const operationAppInjector = ReflectiveInjector.createFromResolved({
name: 'App (Operation Scope)',
name: operationAppInjectorName,
providers: appLevelOperationProviders.concat(
ReflectiveInjector.resolve([
{
Expand All @@ -110,27 +122,33 @@ export function createContextBuilder({
),
parent: appInjector,
});
// Track Providers with OnDestroy hooks
registerProvidersToDestroy(operationAppInjector);

// Create a context for application-level ExecutionContext
appContext = merge(context, {
injector: operationAppInjector,
});

// Track Providers with OnDestroy hooks
registerProvidersToDestroy(operationAppInjector);

function getModuleContext(
moduleId: string,
ctx: GraphQLModules.GlobalContext
): GraphQLModules.ModuleContext {
// Reuse a context or create if not available
if (!contextCache[moduleId]) {
const operationModuleInjectorName = `Module "${moduleId}" (Operation Scope)`;
const tracerModContextEnd = tracer.onContext({
id: moduleId,
});
const tracerModInjectorEnd = tracer.onInjector({
name: operationModuleInjectorName,
});
// We're interested in operation-scoped providers only
const providers = modulesMap.get(moduleId)?.operationProviders!;

// Create module-level Operation-scoped Injector
const operationModuleInjector = ReflectiveInjector.createFromResolved({
name: `Module "${moduleId}" (Operation Scope)`,
name: operationModuleInjectorName,
providers: providers.concat(
ReflectiveInjector.resolve([
{
Expand All @@ -149,11 +167,13 @@ export function createContextBuilder({

// Same as on application level, we need to collect providers with OnDestroy hooks
registerProvidersToDestroy(operationModuleInjector);
tracerModInjectorEnd();

contextCache[moduleId] = merge(ctx, {
injector: operationModuleInjector,
moduleId,
});
tracerModContextEnd();
}

return contextCache[moduleId];
Expand All @@ -177,8 +197,12 @@ export function createContextBuilder({
},
});

tracerInjectorEnd();
tracerContextEnd();

return {
ɵdestroy: once(() => {
const tracerOnDestroyEnd = tracer.onDestroy();
providersToDestroy.forEach(([injector, keyId]) => {
// If provider was instantiated
if (injector._isObjectDefinedByKeyId(keyId)) {
Expand All @@ -187,6 +211,7 @@ export function createContextBuilder({
}
});
contextCache = {};
tracerOnDestroyEnd();
}),
ɵinjector: operationAppInjector,
context: sharedContext,
Expand Down
2 changes: 2 additions & 0 deletions packages/graphql-modules/src/application/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { MiddlewareMap } from '../shared/middleware';
import type { ApolloRequestContext } from './apollo';
import type { Single, ValueOrPromise } from '../shared/types';
import type { InternalAppContext } from './application';
import type { Tracing } from '../shared/tracing';

type Execution = typeof execute;
type Subscription = typeof subscribe;
Expand Down Expand Up @@ -139,4 +140,5 @@ export interface ApplicationConfig {
typeDefs: DocumentNode[];
resolvers: Record<string, any>[];
}): GraphQLSchema;
tracing?: Tracing;
}
33 changes: 33 additions & 0 deletions packages/graphql-modules/src/shared/tracing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
type EndCallback = () => void;

export interface Tracer {
onContext(info: { id: string }): EndCallback;
onDestroy(): EndCallback;
onInjector(info: { name: string }): EndCallback;
}

export interface Tracing {
session(context: GraphQLModules.GlobalContext): string;
create(info: { session: string }): Tracer;
}

function noop() {}

export const emptyTracing: Tracing = {
session() {
return '';
},
create() {
return {
onContext() {
return noop;
},
onDestroy() {
return noop;
},
onInjector() {
return noop;
},
};
},
};