-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat: Autoload Database Integrations in Node environment #3483
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
Conversation
147a445
to
6035456
Compare
size-limit report
|
I am going to pick up the PR and carry it to the 🏁 |
e12e808
to
5561844
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this going to work for all clients (the default one from init
and custom clients)?
Can users disable those DB integrations the same way they can disable other integrations?
try { | ||
mod = dynamicRequire(module, moduleName); | ||
} catch (e) { | ||
// no-empty | ||
} | ||
|
||
try { | ||
const { cwd } = dynamicRequire(module, 'process'); | ||
mod = dynamicRequire(module, `${cwd()}/node_modules/${moduleName}`) as T; | ||
} catch (e) { | ||
// no-empty | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this try to assign mod
twice? If the first dynamicRequire
threw no exception, why don't we return mod
there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because T | undefined
return type requires us to return on every branch anyway, so the best we could do is
try {
mod = dynamicRequire(module, moduleName);
} catch (e) {
// no-empty
}
try {
const { cwd } = dynamicRequire(module, 'process');
mod = dynamicRequire(module, `${cwd()}/node_modules/${moduleName}`) as T;
} catch (e) {
// no-empty
}
return undefined;
In which case the current solution is imo more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm reading it wrong, but if there are no exceptions, this will execute linearly like this:
mod = dynamicRequire(module, moduleName);
const { cwd } = dynamicRequire(module, 'process');
mod = dynamicRequire(module, `${cwd()}/node_modules/${moduleName}`) as T;
In which case mod = dynamicRequire(module, moduleName);
is overwritten by mod = dynamicRequire(module,
${cwd()}/node_modules/${moduleName}) as T;
.
I'd understand it better if it was
try {
mod = dynamicRequire(module, moduleName);
return mod; // NEW CODE
} catch (e) {
// no-empty
}
try {
const { cwd } = dynamicRequire(module, 'process');
mod = dynamicRequire(module, `${cwd()}/node_modules/${moduleName}`) as T;
return mod; // NEW CODE
} catch (e) {
// no-empty
}
return undefined;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do want to override it, as if possible, we want the second value. This is to mimick 1:1 require.resolve
behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. So why don't we start with what we want first, and fallback later?
The code as is is non-obvious.
try { | |
mod = dynamicRequire(module, moduleName); | |
} catch (e) { | |
// no-empty | |
} | |
try { | |
const { cwd } = dynamicRequire(module, 'process'); | |
mod = dynamicRequire(module, `${cwd()}/node_modules/${moduleName}`) as T; | |
} catch (e) { | |
// no-empty | |
} | |
try { | |
const { cwd } = dynamicRequire(module, 'process'); | |
mod = dynamicRequire(module, `${cwd()}/node_modules/${moduleName}`) as T; | |
return mod | |
} catch (e) { | |
// ignore | |
} | |
try { | |
mod = dynamicRequire(module, moduleName); | |
return mod | |
} catch (e) { | |
// ignore | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to mimick 1:1
require.resolve
behavior.
Just saw this in the JSdoc; if we are trying to mimic also the side-effects of importing, then ignore my suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's stylistic thing already, and I don't want to run CI over and over again :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We’re changing in this same PR some unrelated code to return early, and here choose to do an import potentially twice :-)
The import is not only a style preference, it also contains potential side-effects. We are not profiling the code nor explicitly trying to make SDK startup as fast as possible, but we can also say that the double import also makes it overall slower.
No need to block on this, we've got 2 review approvals mine included!
No, it won't work for custom clients, the same way as default integrations doesn't work for them.
Yes, with the same |
Co-authored-by: Rodolfo Carvalho <[email protected]>
we're still investigating, but this change unexpectedly broke our apps in a very insidious and subtle way that caused a production outage - I'd encourage you to release this sort of monkey-patching-database-connectors change in a major version rather than a minor version. |
@x5a Can you provide a bit more info; How does your stack look like / how can we repro this? |
@HazAT we're using postgres (
which maps to: https://github.com/getsentry/sentry-javascript/blob/master/packages/tracing/src/integrations/postgres.ts#L63 |
I opened a new issue to warn users the latest version is broken: |
I can also confirm that this change has broken my application's postgres integration. In my case, the failure occurs when using a QueryStream via knex and pg-query-stream. The failure is due to the exact same reason that @x5a has pointed out. For example: const stream = knex('posts').select('*').stream();
stream.pipe(someWritableStream); The resulting query object is a QueryStream and just like the cursor is not thenable. The assumption in the sentry code about the original query method being thenable fails but the error is swallowed in a not-obvious way. |
This feature will auto-load DB tracing integrations whenever a related package is available in the environment.
Postgres testing snippets: #3064
Mongo testing snippets: #3072
Mysql testing snippets: #3088
Docs: getsentry/sentry-docs#3563