Skip to content

Commit 62677f6

Browse files
committed
remove async storage from graphql
1 parent 07785fd commit 62677f6

File tree

5 files changed

+115
-111
lines changed

5 files changed

+115
-111
lines changed

packages/datadog-instrumentations/src/graphql.js

Lines changed: 65 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
const {
44
addHook,
5-
channel,
6-
AsyncResource
5+
channel
76
} = require('./helpers/instrument')
87
const shimmer = require('../../datadog-shimmer')
98

@@ -89,10 +88,8 @@ function wrapParse (parse) {
8988
return parse.apply(this, arguments)
9089
}
9190

92-
const asyncResource = new AsyncResource('bound-anonymous-fn')
93-
94-
return asyncResource.runInAsyncScope(() => {
95-
parseStartCh.publish()
91+
const ctx = {}
92+
return parseStartCh.runStores(ctx, () => {
9693
let document
9794
try {
9895
document = parse.apply(this, arguments)
@@ -107,11 +104,12 @@ function wrapParse (parse) {
107104
return document
108105
} catch (err) {
109106
err.stack
110-
parseErrorCh.publish(err)
107+
ctx.error = err
108+
parseErrorCh.publish(ctx)
111109

112110
throw err
113111
} finally {
114-
parseFinishCh.publish({ source, document, docSource: documentSources.get(document) })
112+
parseFinishCh.publish({ source, document, docSource: documentSources.get(document), ...ctx })
115113
}
116114
})
117115
}
@@ -123,25 +121,24 @@ function wrapValidate (validate) {
123121
return validate.apply(this, arguments)
124122
}
125123

126-
const asyncResource = new AsyncResource('bound-anonymous-fn')
127-
128-
return asyncResource.runInAsyncScope(() => {
129-
validateStartCh.publish({ docSource: documentSources.get(document), document })
130-
124+
const ctx = { docSource: documentSources.get(document), document }
125+
return validateStartCh.runStores(ctx, () => {
131126
let errors
132127
try {
133128
errors = validate.apply(this, arguments)
134129
if (errors && errors[0]) {
135-
validateErrorCh.publish(errors && errors[0])
130+
ctx.error = errors && errors[0]
131+
validateErrorCh.publish(ctx)
136132
}
137133
return errors
138134
} catch (err) {
139135
err.stack
140-
validateErrorCh.publish(err)
136+
ctx.error = err
137+
validateErrorCh.publish(ctx)
141138

142139
throw err
143140
} finally {
144-
validateFinishCh.publish({ document, errors })
141+
validateFinishCh.publish({ errors, ...ctx })
145142
}
146143
})
147144
}
@@ -155,15 +152,15 @@ function wrapExecute (execute) {
155152
return exe.apply(this, arguments)
156153
}
157154

158-
const asyncResource = new AsyncResource('bound-anonymous-fn')
159-
return asyncResource.runInAsyncScope(() => {
160-
const args = normalizeArgs(arguments, defaultFieldResolver)
161-
const schema = args.schema
162-
const document = args.document
163-
const source = documentSources.get(document)
164-
const contextValue = args.contextValue
165-
const operation = getOperation(document, args.operationName)
155+
const args = normalizeArgs(arguments, defaultFieldResolver)
156+
const schema = args.schema
157+
const document = args.document
158+
const source = documentSources.get(document)
159+
const contextValue = args.contextValue
160+
const operation = getOperation(document, args.operationName)
166161

162+
const ctx = { operation, args, docSource: documentSources.get(document) }
163+
return startExecuteCh.runStores(ctx, () => {
167164
if (contexts.has(contextValue)) {
168165
return exe.apply(this, arguments)
169166
}
@@ -173,26 +170,22 @@ function wrapExecute (execute) {
173170
wrapFields(schema._mutationType)
174171
}
175172

176-
startExecuteCh.publish({
177-
operation,
178-
args,
179-
docSource: documentSources.get(document)
180-
})
181-
182-
const context = { source, asyncResource, fields: {}, abortController: new AbortController() }
173+
const context = { source, fields: {}, abortController: new AbortController(), ...ctx }
183174

184175
contexts.set(contextValue, context)
185176

186-
return callInAsyncScope(exe, asyncResource, this, arguments, context.abortController, (err, res) => {
177+
return callInAsyncScope(exe, this, arguments, context.abortController, (err, res) => {
187178
if (finishResolveCh.hasSubscribers) finishResolvers(context)
188179

189180
const error = err || (res && res.errors && res.errors[0])
190181

191182
if (error) {
192-
executeErrorCh.publish(error)
183+
ctx.error = error
184+
executeErrorCh.publish(ctx)
193185
}
194186

195-
finishExecuteCh.publish({ res, args, context })
187+
ctx.res = res
188+
finishExecuteCh.publish(ctx)
196189
})
197190
})
198191
}
@@ -211,8 +204,8 @@ function wrapResolve (resolve) {
211204

212205
const field = assertField(context, info, args)
213206

214-
return callInAsyncScope(resolve, field.asyncResource, this, arguments, context.abortController, (err) => {
215-
updateFieldCh.publish({ field, info, err })
207+
return callInAsyncScope(resolve, this, arguments, context.abortController, (err) => {
208+
updateFieldCh.publish({ field, info, err, ...field.ctx })
216209
})
217210
}
218211

@@ -221,32 +214,30 @@ function wrapResolve (resolve) {
221214
return resolveAsync
222215
}
223216

224-
function callInAsyncScope (fn, aR, thisArg, args, abortController, cb) {
217+
function callInAsyncScope (fn, thisArg, args, abortController, cb) {
225218
cb = cb || (() => {})
226219

227-
return aR.runInAsyncScope(() => {
228-
if (abortController?.signal.aborted) {
229-
cb(null, null)
230-
throw new AbortError('Aborted')
231-
}
220+
if (abortController?.signal.aborted) {
221+
cb(null, null)
222+
throw new AbortError('Aborted')
223+
}
232224

233-
try {
234-
const result = fn.apply(thisArg, args)
235-
if (result && typeof result.then === 'function') {
236-
// bind callback to this scope
237-
result.then(
238-
aR.bind(res => cb(null, res)),
239-
aR.bind(err => cb(err))
240-
)
241-
} else {
242-
cb(null, result)
243-
}
244-
return result
245-
} catch (err) {
246-
cb(err)
247-
throw err
225+
try {
226+
const result = fn.apply(thisArg, args)
227+
if (result && typeof result.then === 'function') {
228+
// bind callback to this scope
229+
result.then(
230+
res => cb(null, res),
231+
err => cb(err)
232+
)
233+
} else {
234+
cb(null, result)
248235
}
249-
})
236+
return result
237+
} catch (err) {
238+
cb(err)
239+
throw err
240+
}
250241
}
251242

252243
function pathToArray (path) {
@@ -271,27 +262,15 @@ function assertField (context, info, args) {
271262

272263
if (!field) {
273264
const parent = getParentField(context, path)
274-
275-
// we want to spawn the new span off of the parent, not a new async resource
276-
parent.asyncResource.runInAsyncScope(() => {
277-
/* this child resource will run a branched scope off of the parent resource, which
278-
accesses the parent span from the storage unit in its own scope */
279-
const childResource = new AsyncResource('bound-anonymous-fn')
280-
281-
childResource.runInAsyncScope(() => {
282-
startResolveCh.publish({
283-
info,
284-
context,
285-
args
286-
})
287-
})
288-
289-
field = fields[pathString] = {
290-
parent,
291-
asyncResource: childResource,
292-
error: null
293-
}
294-
})
265+
// we need to pass the parent span to the field if it exists for correct span parenting
266+
// of nested fields
267+
const ctx = { info, context, args, childOf: parent?.ctx?.currentStore?.span }
268+
startResolveCh.publish(ctx)
269+
field = fields[pathString] = {
270+
parent,
271+
error: null,
272+
ctx
273+
}
295274
}
296275

297276
return field
@@ -349,20 +328,16 @@ function wrapFieldType (field) {
349328
function finishResolvers ({ fields }) {
350329
Object.keys(fields).reverse().forEach(key => {
351330
const field = fields[key]
352-
const asyncResource = field.asyncResource
353-
asyncResource.runInAsyncScope(() => {
354-
if (field.error) {
355-
resolveErrorCh.publish(field.error)
356-
}
357-
finishResolveCh.publish(field.finishTime)
358-
})
331+
const ctx = { field, finishTime: field.finishTime, ...field.ctx }
332+
if (field.error) {
333+
ctx.error = field.error
334+
resolveErrorCh.publish(ctx)
335+
}
336+
finishResolveCh.publish(ctx)
359337
})
360338
}
361339

362-
addHook({ name: '@graphql-tools/executor', file: 'cjs/execution/execute.js', versions: ['>=0.0.14'] }, execute => {
363-
shimmer.wrap(execute, 'execute', wrapExecute(execute))
364-
return execute
365-
})
340+
addHook({ name: '@graphql-tools/executor', file: 'cjs/execution/execute.js', versions: ['>=0.0.14'] }, execute => {})
366341

367342
addHook({ name: 'graphql', file: 'execution/execute.js', versions: ['>=0.10'] }, execute => {
368343
shimmer.wrap(execute, 'execute', wrapExecute(execute))

packages/datadog-plugin-graphql/src/execute.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ class GraphQLExecutePlugin extends TracingPlugin {
1111
static get type () { return 'graphql' }
1212
static get kind () { return 'server' }
1313

14-
start ({ operation, args, docSource }) {
14+
bindStart (ctx) {
15+
const { operation, args, docSource } = ctx
16+
1517
const type = operation && operation.operation
1618
const name = operation && operation.name && operation.name.value
1719
const document = args.document
@@ -27,20 +29,25 @@ class GraphQLExecutePlugin extends TracingPlugin {
2729
'graphql.operation.name': name,
2830
'graphql.source': source
2931
}
30-
})
32+
}, ctx)
3133

3234
addVariableTags(this.config, span, args.variableValues)
35+
36+
return ctx.currentStore
3337
}
3438

35-
finish ({ res, args }) {
36-
const span = this.activeSpan
39+
finish (ctx) {
40+
const { res, args } = ctx
41+
const span = ctx?.currentStore?.span || this.activeSpan
3742
this.config.hooks.execute(span, args, res)
3843
if (res?.errors) {
3944
for (const err of res.errors) {
4045
extractErrorIntoSpanEvent(this._tracerConfig, span, err)
4146
}
4247
}
43-
super.finish()
48+
super.finish(ctx)
49+
50+
return ctx.parentStore
4451
}
4552
}
4653

packages/datadog-plugin-graphql/src/parse.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,29 @@ class GraphQLParsePlugin extends TracingPlugin {
66
static get id () { return 'graphql' }
77
static get operation () { return 'parser' }
88

9-
start () {
9+
bindStart (ctx) {
1010
this.startSpan('graphql.parse', {
1111
service: this.config.service,
1212
type: 'graphql',
1313
meta: {}
14-
})
14+
}, ctx)
15+
16+
return ctx.currentStore
1517
}
1618

17-
finish ({ source, document, docSource }) {
18-
const span = this.activeSpan
19+
finish (ctx) {
20+
const { source, document, docSource } = ctx
21+
const span = ctx?.currentStore?.span || this.activeSpan
1922

2023
if (this.config.source && document) {
2124
span.setTag('graphql.source', docSource)
2225
}
2326

2427
this.config.hooks.parse(span, source, document)
2528

26-
super.finish()
29+
super.finish(ctx)
30+
31+
return ctx.parentStore
2732
}
2833
}
2934

packages/datadog-plugin-graphql/src/resolve.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class GraphQLResolvePlugin extends TracingPlugin {
99
static get id () { return 'graphql' }
1010
static get operation () { return 'resolve' }
1111

12-
start ({ info, context, args }) {
12+
start (ctx) {
13+
const { info, context, args, childOf } = ctx
1314
const path = getPath(info, this.config)
1415

1516
if (!shouldInstrument(this.config, path)) return
@@ -35,14 +36,15 @@ class GraphQLResolvePlugin extends TracingPlugin {
3536
const span = this.startSpan('graphql.resolve', {
3637
service: this.config.service,
3738
resource: `${info.fieldName}:${info.returnType}`,
39+
childOf,
3840
type: 'graphql',
3941
meta: {
4042
'graphql.field.name': info.fieldName,
4143
'graphql.field.path': computedPathString,
4244
'graphql.field.type': info.returnType.name,
4345
'graphql.source': source
4446
}
45-
})
47+
}, ctx)
4648

4749
if (fieldNode && this.config.variables && fieldNode.arguments) {
4850
const variables = this.config.variables(info.variableValues)
@@ -58,17 +60,21 @@ class GraphQLResolvePlugin extends TracingPlugin {
5860
if (this.resolverStartCh.hasSubscribers) {
5961
this.resolverStartCh.publish({ context, resolverInfo: getResolverInfo(info, args) })
6062
}
63+
64+
return ctx.currentStore
6165
}
6266

6367
constructor (...args) {
6468
super(...args)
6569

66-
this.addTraceSub('updateField', ({ field, info, err }) => {
70+
this.addTraceSub('updateField', (ctx) => {
71+
const { field, info, err } = ctx
72+
6773
const path = getPath(info, this.config)
6874

6975
if (!shouldInstrument(this.config, path)) return
7076

71-
const span = this.activeSpan
77+
const span = ctx?.currentStore?.span || this.activeSpan
7278
field.finishTime = span._getTime ? span._getTime() : 0
7379
field.error = field.error || err
7480
})
@@ -81,8 +87,13 @@ class GraphQLResolvePlugin extends TracingPlugin {
8187
super.configure(config.depth === 0 ? false : config)
8288
}
8389

84-
finish (finishTime) {
85-
this.activeSpan.finish(finishTime)
90+
bindFinish (ctx) {
91+
const { finishTime } = ctx
92+
93+
const span = ctx?.currentStore?.span || this.activeSpan
94+
span.finish(finishTime)
95+
96+
return ctx.parentStore
8697
}
8798
}
8899

0 commit comments

Comments
 (0)