diff --git a/packages/collector/src/immediate.js b/packages/collector/src/immediate.js index 0ac46511a7..9276ef14d8 100644 --- a/packages/collector/src/immediate.js +++ b/packages/collector/src/immediate.js @@ -30,12 +30,15 @@ const isExcludedFromInstrumentation = coreUtil.excludedFromInstrumentation && co // In case this is a child process of an instrumented parent process we might receive the agent uuid from the parent // process to be able to produce and collect spans immediately without waiting for a connection to the agent in this // process. +// TODO: This does not work because you would report spans with parent agent uuid and the child process pid - +// this is not compatible. Our codebase does not support this. const parentProcessAgentUuid = process.env.INSTANA_AGENT_UUID; if (!isExcludedFromInstrumentation) { if (parentProcessAgentUuid) { // @ts-ignore - Type 'string' is not assignable to type 'undefined' // Probably because exports.agentUuid is set to undefined and export values were not supposed to be changed + // TODO: This has no effect. Remove! See comment above. agentOpts.agentUuid = parentProcessAgentUuid; require('./index')({ tracing: { diff --git a/packages/collector/test/apps/agentStub.js b/packages/collector/test/apps/agentStub.js index 327a80eb68..f102919ca8 100644 --- a/packages/collector/test/apps/agentStub.js +++ b/packages/collector/test/apps/agentStub.js @@ -28,6 +28,7 @@ if (process.env.INSTANA_DEBUG === 'true') { // NOTE: we can leave the hardcoded port here as this file is not used in the test env! const port = process.env.AGENT_PORT || 42699; +const uniqueAgentUuids = process.env.AGENT_UNIQUE_UUIDS === 'true'; const extraHeaders = process.env.EXTRA_HEADERS ? process.env.EXTRA_HEADERS.split(',') : []; const secretsMatcher = process.env.SECRETS_MATCHER ? process.env.SECRETS_MATCHER : 'contains-ignore-case'; const secretsList = process.env.SECRETS_LIST ? process.env.SECRETS_LIST.split(',') : ['pass', 'secret', 'token']; @@ -44,6 +45,7 @@ const kafkaTraceCorrelation = process.env.KAFKA_TRACE_CORRELATION const ignoreEndpoints = process.env.IGNORE_ENDPOINTS && JSON.parse(process.env.IGNORE_ENDPOINTS); const disable = process.env.AGENT_DISABLE_TRACING && JSON.parse(process.env.AGENT_DISABLE_TRACING); +const uuids = {}; let discoveries = {}; let rejectAnnounceAttempts = 0; let requests = {}; @@ -81,8 +83,16 @@ app.put('/com.instana.plugin.nodejs.discovery', (req, res) => { } logger.debug('New discovery %s with params', pid, req.body); + let uuid; + + if (uniqueAgentUuids) { + uuid = uuids[pid] = `agent-stub-uuid-${pid}`; + } else { + uuid = 'agent-stub-uuid'; + } + const response = { - agentUuid: 'agent-stub-uuid', + agentUuid: uuid, pid, extraHeaders, secrets: { diff --git a/packages/collector/test/apps/agentStubControls.js b/packages/collector/test/apps/agentStubControls.js index d576ec0ccc..3606eeeef5 100644 --- a/packages/collector/test/apps/agentStubControls.js +++ b/packages/collector/test/apps/agentStubControls.js @@ -26,6 +26,8 @@ class AgentStubControls { env.SECRETS_MATCHER = opts.secretsMatcher || 'contains-ignore-case'; env.SECRETS_LIST = (opts.secretsList || []).join(','); env.SECRETS_LIST = (opts.secretsList || []).join(','); + env.AGENT_UNIQUE_UUIDS = opts.uniqueAgentUuids === true; + if (opts.rejectTraces) { env.REJECT_TRACES = 'true'; } diff --git a/packages/collector/test/tracing/messaging/bull/sender.js b/packages/collector/test/tracing/messaging/bull/sender.js index 56c5f3e0c8..b05b24f3cd 100644 --- a/packages/collector/test/tracing/messaging/bull/sender.js +++ b/packages/collector/test/tracing/messaging/bull/sender.js @@ -66,8 +66,11 @@ app.post('/send', (request, response) => { if (repeat && !bulk) { options.repeat = { - every: 50, - limit: 2 + // NOTE: DO not use a very small value here (e.g. 50ms), because bull is too smart + // and creates two processes + // instead of one, because the diff is too small. We don't want that for repeat! + every: 1000, // repeat every second + limit: 2 // two repeats }; } diff --git a/packages/collector/test/tracing/messaging/bull/sender.mjs b/packages/collector/test/tracing/messaging/bull/sender.mjs index 415da97e72..bac7e82626 100644 --- a/packages/collector/test/tracing/messaging/bull/sender.mjs +++ b/packages/collector/test/tracing/messaging/bull/sender.mjs @@ -68,7 +68,7 @@ app.post('/send', (request, response) => { if (repeat && !bulk) { options.repeat = { - every: 50, + every: 1000, limit: 2 }; } diff --git a/packages/collector/test/tracing/messaging/bull/test.js b/packages/collector/test/tracing/messaging/bull/test.js index 7d0f4b0ef0..529fe64b97 100644 --- a/packages/collector/test/tracing/messaging/bull/test.js +++ b/packages/collector/test/tracing/messaging/bull/test.js @@ -22,7 +22,7 @@ const { stringifyItems } = require('../../../../../core/test/test_util'); const ProcessControls = require('../../../test_util/ProcessControls'); -const globalAgent = require('../../../globalAgent'); +const { AgentStubControls } = require('../../../apps/agentStubControls'); /** * !!! In order to test Bull, Redis must be running. @@ -36,14 +36,29 @@ if (process.env.BULL_QUEUE_NAME) { } const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : describe.skip; - const retryTime = 1000; mochaSuiteFn('tracing/messaging/bull', function () { this.timeout(config.getTestTimeout() * 3); + const customAgentControls = new AgentStubControls(); - globalAgent.setUpCleanUpHooks(); - const agentControls = globalAgent.instance; + before(async () => { + const files = fs.readdirSync(__dirname); + files.forEach(f => { + if (f.startsWith('file-created-by-job') && f.endsWith('.json')) { + fs.unlinkSync(path.join(__dirname, f)); + } + }); + + await customAgentControls.startAgent({ + // To ensure parent and child process are getting unique uuids from the agent stub! + uniqueAgentUuids: true + }); + }); + + after(async () => { + await customAgentControls.stopAgent(); + }); describe('tracing enabled, no suppression', function () { let senderControls; @@ -51,7 +66,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { before(async () => { senderControls = new ProcessControls({ appPath: path.join(__dirname, 'sender'), - useGlobalAgent: true, + agentControls: customAgentControls, env: { REDIS_SERVER: 'redis://127.0.0.1:6379', BULL_QUEUE_NAME: queueName, @@ -63,7 +78,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { }); beforeEach(async () => { - await agentControls.clearReceivedTraceData(); + await customAgentControls.clearReceivedTraceData(); }); after(async () => { @@ -85,14 +100,13 @@ mochaSuiteFn('tracing/messaging/bull', function () { before(async () => { receiverControls = new ProcessControls({ appPath: path.join(__dirname, 'receiver'), - useGlobalAgent: true, + agentControls: customAgentControls, env: { REDIS_SERVER: 'redis://127.0.0.1:6379', BULL_QUEUE_NAME: queueName, BULL_RECEIVE_TYPE: receiveMethod, BULL_JOB_NAME: 'steve', - BULL_JOB_NAME_ENABLED: 'true', - BULL_CONCURRENCY_ENABLED: 'true' + BULL_JOB_NAME_ENABLED: 'true' } }); @@ -100,7 +114,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { }); beforeEach(async () => { - await agentControls.clearReceivedTraceData(); + await customAgentControls.clearReceivedTraceData(); }); after(async () => { @@ -288,7 +302,13 @@ mochaSuiteFn('tracing/messaging/bull', function () { apiPath, testId, withError, - spanLength: 11, + // 1 x http entry + // 2 x bull exit + // 2 x bull entry + // 2 x http exit + // 2 x redis + // 4 x otel (read + write for the job files) + spanLength: 13, isRepeatable: sendOption === 'repeat=true', isBulk: sendOption === 'bulk=true' }); @@ -315,7 +335,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { receiveMethod, response, apiPath, - spanLength: 17, + spanLength: 19, testId, withError, isRepeatable: sendOption === 'repeat=true', @@ -334,14 +354,13 @@ mochaSuiteFn('tracing/messaging/bull', function () { before(async () => { receiverControls = new ProcessControls({ appPath: path.join(__dirname, 'receiver'), - useGlobalAgent: true, + agentControls: customAgentControls, env: { REDIS_SERVER: 'redis://127.0.0.1:6379', BULL_QUEUE_NAME: queueName, BULL_RECEIVE_TYPE: receiveMethod, BULL_JOB_NAME: 'steve', - BULL_JOB_NAME_ENABLED: 'true', - BULL_CONCURRENCY_ENABLED: 'true' + BULL_JOB_NAME_ENABLED: 'true' } }); @@ -349,7 +368,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { }); beforeEach(async () => { - await agentControls.clearReceivedTraceData(); + await customAgentControls.clearReceivedTraceData(); }); after(async () => { @@ -531,7 +550,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { receiveMethod, response, apiPath, - spanLength: 11, + spanLength: 13, testId, withError, isRepeatable: sendOption === 'repeat=true', @@ -559,7 +578,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { receiverControls, receiveMethod, response, - spanLength: 17, + spanLength: 19, apiPath, testId, withError, @@ -594,9 +613,10 @@ mochaSuiteFn('tracing/messaging/bull', function () { throw new Error(ipcErrorMessage.error); } - await verifyResponseAndJobProcessing({ response, testId, isRepeatable, isBulk }); + await delay(1000 * 2); + const contentsCreatedByJob = await verifyResponseAndJobProcessing({ response, testId, isRepeatable, isBulk }); - return agentControls.getSpans().then(spans => { + return customAgentControls.getSpans().then(spans => { expect(spans.length).to.equal(spanLength); verifySpans({ @@ -606,24 +626,39 @@ mochaSuiteFn('tracing/messaging/bull', function () { apiPath, withError, isRepeatable, - isBulk + isBulk, + contentsCreatedByJob }); }); }, retryTime); } - function verifySpans({ receiverControls, receiveMethod, spans, apiPath, withError, isRepeatable, isBulk }) { + function verifySpans({ + receiverControls, + receiveMethod, + spans, + apiPath, + withError, + isRepeatable, + isBulk, + contentsCreatedByJob + }) { const httpEntry = verifyHttpEntry({ spans, apiPath, withError }); const bullExit = verifyBullExit({ spans, parent: httpEntry, withError, isRepeatable, isBulk }); + const bullEntry = verifyBullEntry({ spans, parent: bullExit, receiverControls, + contentsCreatedByJob, withError }); + + // This is the http exit from the forked bull to the agent! verifyHttpExit({ spans, parent: bullEntry, + contentsCreatedByJob, receiverControls, inProcess: receiveMethod === 'Process' ? 'child' : 'main' }); @@ -634,7 +669,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { span => expect(span.p).to.not.exist, span => expect(span.k).to.equal(constants.ENTRY), span => expect(span.f.e).to.equal(String(senderControls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.f.h).to.equal(`agent-stub-uuid-${senderControls.getPid()}`), span => expect(span.n).to.equal('node.http.server'), span => expect(`${span.data.http.url}?${span.data.http.params}`).to.equal( @@ -643,19 +678,22 @@ mochaSuiteFn('tracing/messaging/bull', function () { ]); } - function verifyHttpExit({ spans, parent, inProcess = 'main', receiverControls }) { + function verifyHttpExit({ spans, parent, inProcess = 'main', receiverControls, contentsCreatedByJob }) { const expectations = [ span => expect(span.t).to.equal(parent.t), span => expect(span.p).to.equal(parent.s), span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.h).to.equal('agent-stub-uuid'), span => expect(span.n).to.equal('node.http.client') ]; if (inProcess === 'main') { expectations.push(span => expect(span.f.e).to.equal(String(receiverControls.getPid()))); + expectations.push(span => expect(span.f.h).to.equal(`agent-stub-uuid-${receiverControls.getPid()}`)); } else if (inProcess === 'child') { - expectations.push(span => expect(span.f.e).to.not.equal(String(receiverControls.getPid()))); + expectations.push(span => expect(span.f.e).to.be.oneOf(contentsCreatedByJob.map(c => String(c.pid)))); + expectations.push(span => + expect(span.f.h).to.be.oneOf(contentsCreatedByJob.map(c => `agent-stub-uuid-${c.pid}`)) + ); } else { expectations.push(() => fail(`Invalid value for inProcess argument: ${inProcess}.`)); } @@ -672,7 +710,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { span => expect(span.t).to.equal(parent.t), span => expect(span.p).to.equal(parent.s), span => expect(span.f.e).to.equal(String(senderControls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.f.h).to.equal(`agent-stub-uuid-${senderControls.getPid()}`), span => expect(span.error).to.not.exist, span => expect(span.async).to.not.exist, span => expect(span.data).to.exist, @@ -688,7 +726,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { span => expect(span.t).to.equal(isRepeatable ? span.t : parent.t), span => expect(span.p).to.equal(isRepeatable ? undefined : parent.s), span => expect(span.f.e).to.equal(String(senderControls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.f.h).to.equal(`agent-stub-uuid-${senderControls.getPid()}`), span => expect(span.error).to.not.exist, span => expect(span.async).to.not.exist, span => expect(span.data).to.exist, @@ -699,14 +737,16 @@ mochaSuiteFn('tracing/messaging/bull', function () { } } - function verifyBullEntry({ receiverControls, spans, parent, withError = false }) { + function verifyBullEntry({ spans, parent, withError = false, receiverControls }) { return expectExactlyOneMatching(spans, [ span => expect(span.n).to.equal('bull'), span => expect(span.k).to.equal(constants.ENTRY), span => expect(span.t).to.equal(parent.t), span => expect(span.p).to.equal(parent.s), - span => expect(span.f.h).to.equal('agent-stub-uuid'), + // TODO: We forward INSTANA_AGENT_UUID in childProcess instrumentation, but the uuid is not used! + // TODO: We expect that the bull entry has the PID from the child forked bull process! span => expect(span.f.e).to.equal(String(receiverControls.getPid())), + span => expect(span.f.h).to.equal(`agent-stub-uuid-${receiverControls.getPid()}`), span => expect(span.error).to.not.exist, span => expect(span.ec).to.equal(withError ? 1 : 0), span => expect(span.async).to.not.exist, @@ -730,7 +770,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { before(async () => { senderControls = new ProcessControls({ appPath: path.join(__dirname, 'sender'), - useGlobalAgent: true, + agentControls: customAgentControls, tracingEnabled: false, env: { REDIS_SERVER: 'redis://127.0.0.1:6379', @@ -743,7 +783,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { }); beforeEach(async () => { - await agentControls.clearReceivedTraceData(); + await customAgentControls.clearReceivedTraceData(); }); after(async () => { @@ -761,15 +801,14 @@ mochaSuiteFn('tracing/messaging/bull', function () { before(async () => { receiverControls = new ProcessControls({ appPath: path.join(__dirname, 'receiver'), - useGlobalAgent: true, + agentControls: customAgentControls, tracingEnabled: false, env: { REDIS_SERVER: 'redis://127.0.0.1:6379', BULL_QUEUE_NAME: queueName, BULL_RECEIVE_TYPE: receiveMethod, BULL_JOB_NAME: 'steve', - BULL_JOB_NAME_ENABLED: 'true', - BULL_CONCURRENCY_ENABLED: 'true' + BULL_JOB_NAME_ENABLED: 'true' } }); @@ -777,7 +816,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { }); beforeEach(async () => { - await agentControls.clearReceivedTraceData(); + await customAgentControls.clearReceivedTraceData(); }); after(async () => { @@ -809,7 +848,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { retryTime ) .then(() => delay(1000)) - .then(() => agentControls.getSpans()) + .then(() => customAgentControls.getSpans()) .then(spans => { if (spans.length > 0) { fail(`Unexpected spans (Bull suppressed: ${stringifyItems(spans)}`); @@ -827,7 +866,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { before(async () => { senderControls = new ProcessControls({ appPath: path.join(__dirname, 'sender'), - useGlobalAgent: true, + agentControls: customAgentControls, env: { REDIS_SERVER: 'redis://127.0.0.1:6379', BULL_QUEUE_NAME: queueName, @@ -839,7 +878,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { }); beforeEach(async () => { - await agentControls.clearReceivedTraceData(); + await customAgentControls.clearReceivedTraceData(); }); after(async () => { @@ -857,14 +896,13 @@ mochaSuiteFn('tracing/messaging/bull', function () { before(async () => { receiverControls = new ProcessControls({ appPath: path.join(__dirname, 'receiver'), - useGlobalAgent: true, + agentControls: customAgentControls, env: { REDIS_SERVER: 'redis://127.0.0.1:6379', BULL_QUEUE_NAME: queueName, BULL_RECEIVE_TYPE: receiveMethod, BULL_JOB_NAME: 'steve', - BULL_JOB_NAME_ENABLED: 'true', - BULL_CONCURRENCY_ENABLED: 'true' + BULL_JOB_NAME_ENABLED: 'true' } }); @@ -872,7 +910,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { }); beforeEach(async () => { - await agentControls.clearReceivedTraceData(); + await customAgentControls.clearReceivedTraceData(); }); after(async () => { @@ -903,7 +941,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { await verifyResponseAndJobProcessing({ response, testId, isRepeatable, isBulk }); }, retryTime) .then(() => delay(1000)) - .then(() => agentControls.getSpans()) + .then(() => customAgentControls.getSpans()) .then(spans => { if (spans.length > 0) { fail(`Unexpected spans (Bull suppressed: ${stringifyItems(spans)}`); @@ -918,7 +956,7 @@ mochaSuiteFn('tracing/messaging/bull', function () { before(async () => { controls = new ProcessControls({ - useGlobalAgent: true, + agentControls: customAgentControls, appPath: path.join(__dirname, 'allowRootExitSpanApp'), env: { REDIS_SERVER: `redis://${process.env.REDIS}`, @@ -932,13 +970,13 @@ mochaSuiteFn('tracing/messaging/bull', function () { }); beforeEach(async () => { - await agentControls.clearReceivedTraceData(); + await customAgentControls.clearReceivedTraceData(); }); it('must trace', async function () { await retry(async () => { await delay(500); - const spans = await agentControls.getSpans(); + const spans = await customAgentControls.getSpans(); expect(spans.length).to.be.eql(2); @@ -966,10 +1004,19 @@ async function verifyResponseAndJobProcessing({ response, testId, isRepeatable, // verifies that the job has been processed. try { if (isBulk) { - await verifyJobCreatedAFile('file-created-by-job-1.json', testId); - await verifyJobCreatedAFile('file-created-by-job-2.json', testId); + const firstJobContent = await verifyJobCreatedAFile('file-created-by-job-1.json', testId); + const secondJobContent = await verifyJobCreatedAFile('file-created-by-job-2.json', testId); + return [firstJobContent, secondJobContent]; + } else if (isRepeatable) { + try { + const firstJobContent = await verifyJobCreatedAFile(`file-created-by-job-repeat-${testId}-1.json`, testId); + const secondJobContent = await verifyJobCreatedAFile(`file-created-by-job-repeat-${testId}-2.json`, testId); + return [firstJobContent, secondJobContent]; + } catch (e) { + throw new Error('Not Ready yet.'); + } } else { - await verifyJobCreatedAFile('file-created-by-job.json', testId); + return [await verifyJobCreatedAFile('file-created-by-job.json', testId)]; } } catch (ex) { fail(ex); @@ -998,4 +1045,6 @@ async function verifyJobCreatedAFile(filename, testId) { ); const contentCreatedByJob = JSON.parse(fileCreatedByJob.toString()); expect(contentCreatedByJob.data.testId).to.equal(testId); + + return contentCreatedByJob; } diff --git a/packages/collector/test/tracing/messaging/bull/util.js b/packages/collector/test/tracing/messaging/bull/util.js index 411fb9ca57..6014723b05 100644 --- a/packages/collector/test/tracing/messaging/bull/util.js +++ b/packages/collector/test/tracing/messaging/bull/util.js @@ -112,6 +112,8 @@ exports.buildReceiver = function (queue, processType, log, jobName, isConcurrent const asProcessArgs = [processorPath]; let currentTypeArgs; + log(`Process type is ${processType}`); + switch (processType) { case ProcessTypes.CALLBACK: currentTypeArgs = callbackArgs; @@ -126,18 +128,19 @@ exports.buildReceiver = function (queue, processType, log, jobName, isConcurrent throw new Error(`Option ${processType} is invalid`); } - if (jobName && isConcurrent) { - log(`Job named ${jobName} and concurrent`); - currentTypeArgs.unshift(jobName, NUMBER_OF_PROCESSES); - } else if (jobName && !isConcurrent) { + // You cannot set name + concurrency at the same time. The concurrency is ignored in that case. + // See https://github.com/OptimalBits/bull/blob/v4.16.5/lib/queue.js#L668-L684 + if (jobName) { log(`Job named ${jobName}, not concurrent`); currentTypeArgs.unshift(jobName); } else if (!jobName && isConcurrent) { + // TODO: We don't have a test for this yet. log('Job unnamed, concurrent'); currentTypeArgs.unshift(NUMBER_OF_PROCESSES); } else { log('Job unnamed, not concurrent'); } + queue.process.apply(queue, currentTypeArgs); }; @@ -154,12 +157,35 @@ function getJobData(job) { function writeToAFileToProveThatThisParticularJobHasBeenProcessed(jobData) { let fileCreatedByJob; + if (jobData.data.bulkIndex) { fileCreatedByJob = path.join(__dirname, `file-created-by-job-${jobData.data.bulkIndex}.json`); + } else if (jobData.opts.jobId?.includes('repeat')) { + const files = fs + .readdirSync(__dirname) + .filter(f => f.startsWith(`file-created-by-job-repeat-${jobData.data.testId}-`)); + + if (files.length === 0) { + fileCreatedByJob = path.join(__dirname, `file-created-by-job-repeat-${jobData.data.testId}-1.json`); + } else { + files.forEach(f => { + const match = f.match(/-(\d+)\.json$/); + if (match) { + const nextAttempt = Number(match[1]) + 1; + + fileCreatedByJob = path.join( + __dirname, + `file-created-by-job-repeat-${jobData.data.testId}-${nextAttempt}.json` + ); + } + }); + } } else { fileCreatedByJob = path.join(__dirname, 'file-created-by-job.json'); } + jobData.pid = process.pid; + return new Promise((resolve, reject) => { fs.writeFile(fileCreatedByJob, JSON.stringify(jobData, null, 2), (err, success) => { if (err) { diff --git a/packages/core/src/tracing/instrumentation/messaging/bull.js b/packages/core/src/tracing/instrumentation/messaging/bull.js index df19b549df..7a0dd12d71 100644 --- a/packages/core/src/tracing/instrumentation/messaging/bull.js +++ b/packages/core/src/tracing/instrumentation/messaging/bull.js @@ -237,6 +237,8 @@ function instrumentedProcessJob(ctx, originalProcessJob, originalArgs) { return originalProcessJob.apply(ctx, originalArgs); } + // TODO: The entry is CREATED BEFORE the child process is forked. Its created ON THE receiver process. + // This is not correct. const span = cls.startSpan({ spanName: exports.spanName, kind: ENTRY,