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

[DI] Workaround bug in AsyncLocalStorage which would otherwise throw #5290

Merged
merged 4 commits into from
Feb 19, 2025
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/system-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
uses: DataDog/system-tests/.github/workflows/compute-workflow-parameters.yml@main
with:
library: nodejs
scenarios_groups: essentials,appsec_rasp
scenarios_groups: essentials,appsec_rasp,debugger

system-tests:
runs-on: ${{ contains(fromJSON('["CROSSED_TRACING_LIBRARIES", "INTEGRATIONS"]'), matrix.scenario) && 'ubuntu-latest-16-cores' || 'ubuntu-latest' }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ async function addBreakpoint (probe) {
probe.nsBetweenSampling = BigInt(1 / snapshotsPerSecond * 1e9)
probe.lastCaptureNs = 0n

// TODO: Inbetween `await session.post('Debugger.enable')` and here, the scripts are parsed and cached.
// Maybe there's a race condition here or maybe we're guraenteed that `await session.post('Debugger.enable')` will
// not continue untill all scripts have been parsed?
// Warning: The code below relies on undocumented behavior of the inspector!
// It expects that `await session.post('Debugger.enable')` will wait for all loaded scripts to be emitted as
// `Debugger.scriptParsed` events. If this ever changes, we will have a race condition!
const script = findScriptFromPartialPath(file)
if (!script) throw new Error(`No loaded script found for ${file} (probe: ${probe.id}, version: ${probe.version})`)
const { url, scriptId, sourceMapURL, source } = script
Expand Down
26 changes: 20 additions & 6 deletions packages/dd-trace/src/debugger/devtools_client/source-maps.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const { SourceMapConsumer } = require('source-map')

const cache = new Map()
let cacheTimer = null
let cacheTimerLastSet = 0

const self = module.exports = {
async loadSourceMap (dir, url) {
Expand All @@ -33,13 +34,26 @@ const self = module.exports = {
}
}

// TODO: Remove if-statement around `setTimeout` below once it's safe to do so.
//
// This is a workaround for, what seems like a bug in Node.js core, that seems to trigger when, among other things, a
// lot of timers are being created very rapidly. This makes the call to `setTimeout` throw an error from within
// `AsyncLocalStorage._propagate` with the following error message:
//
// TypeError: Cannot read properties of undefined (reading 'Symbol(kResourceStore)')
//
// Source: https://github.com/nodejs/node/blob/v18.20.6/lib/async_hooks.js#L312
function cacheIt (key, value) {
clearTimeout(cacheTimer)
cacheTimer = setTimeout(function () {
// Optimize for app boot, where a lot of reads might happen
// Clear cache a few seconds after it was last used
cache.clear()
}, 10_000).unref()
const now = Date.now()
if (now > cacheTimerLastSet + 1_000) {
clearTimeout(cacheTimer)
cacheTimer = setTimeout(function () {
// Optimize for app boot, where a lot of reads might happen
// Clear cache a few seconds after it was last used
cache.clear()
}, 10_000).unref()
cacheTimerLastSet = now
}
cache.set(key, value)
return value
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ describe('source map utils', function () {
let clock

function setup () {
clock = sinon.useFakeTimers()
clock = sinon.useFakeTimers({
toFake: ['setTimeout']
})
readFileSync = sinon.stub().returns(rawSourceMap)
readFile = sinon.stub().resolves(rawSourceMap)

Expand Down
Loading