-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
360 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -126,6 +126,7 @@ | |
"graphql-v14": "npm:[email protected]", | ||
"graphql-v16": "npm:[email protected]", | ||
"graphql-ws": "^5.5.5", | ||
"heapdump": "^0.3.15", | ||
"husky": "^7.0.4", | ||
"ibm_db": "^2.8.2", | ||
"ioredis": "^4.28.2", | ||
|
57 changes: 57 additions & 0 deletions
57
packages/collector/test/tracing/sdk/memory_leak_reproducer/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
SDK Memory Leak Reproducer | ||
========================== | ||
|
||
This directory contains a couple of small test applications that try to reproduce a memory leak in the SDK, or rather, in our use of AsyncLocalStorage/cls-hooked. | ||
|
||
They all create an entry span in regular intervals via the SDK. They do not need to be triggered by an external request. | ||
|
||
Available Reproducers | ||
--------------------- | ||
|
||
There are currently six different reproducers, two for each SDK API style: | ||
* async, | ||
* promise, and | ||
* callback. | ||
|
||
One of each pair is using a *recursive* call pattern. That is, the next call is triggered from the context of the previous call. The call is triggerd via `setTimeout` but that does not matter with respect to AsyncLocalStorage (ALS for short)/cls-hooked, because the context is kept across `setTimeout` (or any async mechanism – this is precisely the point of ALS/cls-hooked). | ||
|
||
The other reproducer is scheduling regular calls via `setInterval`. The crucial difference is that calls are not triggered from the context of the preceding call, but from the root context. | ||
|
||
The current hypothesis is that all recursive reproducers are affected by the leak, no matter which API style the use or whether they use ALS or the legacy cls-hooked implementation. Additionally, some of the non-recursive reproducers might also be affected due to the failure of existing the context after Namespace#runPromise. | ||
|
||
Usage | ||
----- | ||
The SDK will only actually create spans when @instana/collector has established a connection to an agent. To run the examples, you therefore need to start an agent locally. | ||
|
||
If you are not interested in inspecting the reported data in Instana, you can start | ||
``` | ||
DROP_DATA=true node packages/collector/test/apps/agentStub | ||
``` | ||
|
||
in a separate shell. Otherwise, start an Instana agent locally. Be aware that the reproducers will create a lot of spans, though. | ||
|
||
Start a reproducer like this: | ||
|
||
node packages/collector/test/tracing/sdk/memory_leak_reproducer/async_recursive.js | ||
|
||
There are a couple of options to control the behavior: | ||
|
||
``` | ||
# Set a custom delay in milliseconds between individual calls. The default is currently 10 (!) milliseconds. | ||
DELAY=1000 node packages/collector/test/tracing/sdk/memory_leak_reproducer/async_recursive.js | ||
# Force @instana/collector to use the legacy cls-hooked library instead of AsyncLocalStorage. | ||
INSTANA_FORCE_LEGACY_CLS=true node packages/collector/test/tracing/sdk/memory_leak_reproducer/async_recursive.js | ||
# Print additional debug output | ||
DEBUG_CLS=true node packages/collector/test/tracing/sdk/memory_leak_reproducer/async_recursive.js | ||
``` | ||
|
||
### Creating a Heapdump | ||
|
||
All reproducers load the `heapdump` module. Execute `kill -USR2 $pid` to create a heapdump while the process is running. Heapdumps can be inspected via Chrome/Chromium for example (DevTools -> tab memory -> Load). | ||
|
||
Analysis | ||
-------- | ||
|
||
The detailed analysis is currently not available in publicly. The internal link is: https://www.notion.so/instana/SDK-Memory-Leak-a807a1b242c84976ac79d9a1a9037494 |
27 changes: 27 additions & 0 deletions
27
packages/collector/test/tracing/sdk/memory_leak_reproducer/async_recursive.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#!/usr/bin/env node | ||
/* | ||
* (c) Copyright IBM Corp. 2022 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const instana = require('../../../..')({ | ||
serviceName: require('path').basename(__filename) | ||
}); | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
const heapdump = require('heapdump'); | ||
const { delayBetweenCalls, simulateWork } = require('./util'); | ||
|
||
async function createSdkSpan() { | ||
await instana.sdk.async.startEntrySpan('span-name'); | ||
await simulateWork(); | ||
instana.sdk.async.completeEntrySpan(); | ||
} | ||
|
||
async function trigger() { | ||
await createSdkSpan(); | ||
setTimeout(trigger, delayBetweenCalls); | ||
} | ||
|
||
setTimeout(trigger, 3000); |
54 changes: 54 additions & 0 deletions
54
...lector/test/tracing/sdk/memory_leak_reproducer/async_recursive_entry_intermediate_exit.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#!/usr/bin/env node | ||
/* | ||
* (c) Copyright IBM Corp. 2022 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const instana = require('../../../..')({ | ||
serviceName: require('path').basename(__filename) | ||
}); | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
const heapdump = require('heapdump'); | ||
const { delayBetweenCalls, simulateWork } = require('./util'); | ||
|
||
async function createSdkSpan() { | ||
await instana.sdk.async.startEntrySpan('span-name'); | ||
await simulateWork(); | ||
for (let i = 0; i < 2; i++) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await createIntermediateSpanWithExitChildren(); | ||
// eslint-disable-next-line no-await-in-loop | ||
await simulateWork(); | ||
} | ||
instana.sdk.async.completeEntrySpan(); | ||
} | ||
|
||
async function createIntermediateSpanWithExitChildren() { | ||
await instana.sdk.async.startIntermediateSpan('span-name-intermediate'); | ||
await simulateWork(); | ||
for (let i = 0; i < 2; i++) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await createExitSpan(); | ||
// eslint-disable-next-line no-await-in-loop | ||
await simulateWork(); | ||
} | ||
instana.sdk.async.completeIntermediateSpan(); | ||
} | ||
|
||
async function createExitSpan() { | ||
await instana.sdk.async.startExitSpan('span-name-exit'); | ||
await simulateWork(); | ||
instana.sdk.async.completeExitSpan(); | ||
} | ||
|
||
async function trigger() { | ||
await createSdkSpan(); | ||
if (process.env.DEBUG_CLS) { | ||
process._rawDebug('----'); | ||
} | ||
setTimeout(trigger, delayBetweenCalls); | ||
} | ||
|
||
setTimeout(trigger, 3000); |
26 changes: 26 additions & 0 deletions
26
packages/collector/test/tracing/sdk/memory_leak_reproducer/async_set_interval.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#!/usr/bin/env node | ||
/* | ||
* (c) Copyright IBM Corp. 2022 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const instana = require('../../../..')({ | ||
serviceName: require('path').basename(__filename) | ||
}); | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
const heapdump = require('heapdump'); | ||
const { delayBetweenCalls, simulateWork } = require('./util'); | ||
|
||
async function createSdkSpan() { | ||
await instana.sdk.async.startEntrySpan('span-name'); | ||
await simulateWork(); | ||
instana.sdk.async.completeEntrySpan(); | ||
} | ||
|
||
async function trigger() { | ||
await createSdkSpan(); | ||
} | ||
|
||
setInterval(trigger, delayBetweenCalls); |
31 changes: 31 additions & 0 deletions
31
packages/collector/test/tracing/sdk/memory_leak_reproducer/callback_recursive.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#!/usr/bin/env node | ||
/* | ||
* (c) Copyright IBM Corp. 2022 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const instana = require('../../../..')({ | ||
serviceName: require('path').basename(__filename) | ||
}); | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
const heapdump = require('heapdump'); | ||
const { delayBetweenCalls, simulateWorkCallback } = require('./util'); | ||
|
||
function createSdkSpan(cb) { | ||
instana.sdk.callback.startEntrySpan('span-name', () => { | ||
simulateWorkCallback(() => { | ||
instana.sdk.callback.completeEntrySpan(); | ||
cb(); | ||
}); | ||
}); | ||
} | ||
|
||
async function trigger() { | ||
createSdkSpan(() => { | ||
setTimeout(trigger, delayBetweenCalls); | ||
}); | ||
} | ||
|
||
setTimeout(trigger, 3000); |
28 changes: 28 additions & 0 deletions
28
packages/collector/test/tracing/sdk/memory_leak_reproducer/callback_set_interval.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#!/usr/bin/env node | ||
/* | ||
* (c) Copyright IBM Corp. 2022 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const instana = require('../../../..')({ | ||
serviceName: require('path').basename(__filename) | ||
}); | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
const heapdump = require('heapdump'); | ||
const { delayBetweenCalls, simulateWorkCallback } = require('./util'); | ||
|
||
function createSdkSpan() { | ||
instana.sdk.callback.startEntrySpan('span-name', () => { | ||
simulateWorkCallback(() => { | ||
instana.sdk.callback.completeEntrySpan(); | ||
}); | ||
}); | ||
} | ||
|
||
async function trigger() { | ||
createSdkSpan(); | ||
} | ||
|
||
setInterval(trigger, delayBetweenCalls); |
30 changes: 30 additions & 0 deletions
30
packages/collector/test/tracing/sdk/memory_leak_reproducer/promise_recursive.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#!/usr/bin/env node | ||
/* | ||
* (c) Copyright IBM Corp. 2022 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const instana = require('../../../..')({ | ||
serviceName: require('path').basename(__filename) | ||
}); | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
const heapdump = require('heapdump'); | ||
const { delayBetweenCalls, simulateWork } = require('./util'); | ||
|
||
function createSdkSpan() { | ||
return instana.sdk.promise.startEntrySpan('span-name').then(() => { | ||
simulateWork().then(() => { | ||
instana.sdk.promise.completeEntrySpan(); | ||
}); | ||
}); | ||
} | ||
|
||
function trigger() { | ||
createSdkSpan().then(() => { | ||
setTimeout(trigger, delayBetweenCalls); | ||
}); | ||
} | ||
|
||
setTimeout(trigger, 3000); |
28 changes: 28 additions & 0 deletions
28
packages/collector/test/tracing/sdk/memory_leak_reproducer/promise_set_interval.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#!/usr/bin/env node | ||
/* | ||
* (c) Copyright IBM Corp. 2022 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const instana = require('../../../..')({ | ||
serviceName: require('path').basename(__filename) | ||
}); | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
const heapdump = require('heapdump'); | ||
const { delayBetweenCalls, simulateWork } = require('./util'); | ||
|
||
function createSdkSpan() { | ||
return instana.sdk.promise.startEntrySpan('span-name').then(() => { | ||
simulateWork().then(() => { | ||
instana.sdk.promise.completeEntrySpan(); | ||
}); | ||
}); | ||
} | ||
|
||
function trigger() { | ||
createSdkSpan(); | ||
} | ||
|
||
setInterval(trigger, delayBetweenCalls); |
26 changes: 26 additions & 0 deletions
26
packages/collector/test/tracing/sdk/memory_leak_reproducer/util.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* (c) Copyright IBM Corp. 2022 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const defaultDelayBetweenCalls = 10; | ||
|
||
exports.delayBetweenCalls = defaultDelayBetweenCalls; | ||
if (process.env.DELAY) { | ||
exports.delayBetweenCalls = parseInt(process.env.DELAY, 10); | ||
if (Number.isNaN(exports.delayBetweenCalls)) { | ||
exports.delayBetweenCalls = defaultDelayBetweenCalls; | ||
} | ||
} | ||
|
||
// eslint-disable-next-line no-console | ||
console.log(`delay between calls: ${exports.delayBetweenCalls}`); | ||
|
||
exports.simulateWork = function simulateWork(ms = 2) { | ||
return new Promise(resolve => setTimeout(resolve, ms)); | ||
}; | ||
|
||
exports.simulateWorkCallback = function simulateWork(cb, ms = 2) { | ||
setTimeout(cb, ms); | ||
}; |
Oops, something went wrong.