Skip to content

feat: Autoload Database Integrations in Node environment - #3483

Merged
kamilogorek merged 7 commits into
masterfrom
autoload-tracing-integrations
May 10, 2021
Merged

feat: Autoload Database Integrations in Node environment#3483
kamilogorek merged 7 commits into
masterfrom
autoload-tracing-integrations

Conversation

@kamilogorek

@kamilogorekkamilogorek commented Apr 29, 2021

Copy link
Copy Markdown
Contributor

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

@kamilogorek
kamilogorek requested review from a team, lobsterkatie and rhcarvalho and removed request for a teamApril 29, 2021 14:21
@kamilogorek
kamilogorekforce-pushed the autoload-tracing-integrations branch from 147a445 to 6035456CompareApril 29, 2021 14:22
@kamilogorek
kamilogorek requested a review from HazATApril 29, 2021 14:23
@github-actions

github-actionsBot commented Apr 29, 2021

Copy link
Copy Markdown
Contributor

size-limit report

PathSize
@sentry/browser - CDN Bundle (gzipped)20.65 KB (+0.09% 🔺)
@sentry/browser - Webpack21.52 KB (+0.1% 🔺)
@sentry/react - Webpack21.55 KB (+0.09% 🔺)
@sentry/browser + @sentry/tracing - CDN Bundle (gzipped)28.14 KB (+0.8% 🔺)

@HazAT

HazAT commented May 5, 2021

Copy link
Copy Markdown
Member

I am going to pick up the PR and carry it to the 🏁

@kamilogorek
kamilogorekforce-pushed the autoload-tracing-integrations branch from e12e808 to 5561844CompareMay 10, 2021 11:30

@HazATHazAT left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment threadpackages/hub/src/hub.ts

@rhcarvalhorhcarvalho left a comment

Copy link
Copy Markdown
Contributor

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?

Comment on lines +34 to +45
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
}

Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
ContributorAuthor

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}`)asT;}catch(e){// no-empty}returnundefined;

In which case the current solution is imo more readable.

Copy link
Copy Markdown
Contributor

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}`)asT;

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);returnmod;// NEW CODE}catch(e){// no-empty}try{const{ cwd }=dynamicRequire(module,'process');mod=dynamicRequire(module,`${cwd()}/node_modules/${moduleName}`)asT;returnmod;// NEW CODE}catch(e){// no-empty}returnundefined;

Copy link
Copy Markdown
ContributorAuthor

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.

Copy link
Copy Markdown
Contributor

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.

Suggested change
try{
mod=dynamicRequire(module,moduleName);
}catch(e){
// no-empty
}
try{
const{ cwd }=dynamicRequire(module,'process');
mod=dynamicRequire(module,`${cwd()}/node_modules/${moduleName}`)asT;
}catch(e){
// no-empty
}
try{
const{ cwd }=dynamicRequire(module,'process');
mod=dynamicRequire(module,`${cwd()}/node_modules/${moduleName}`)asT;
returnmod
}catch(e){
// ignore
}
try{
mod=dynamicRequire(module,moduleName);
returnmod
}catch(e){
// ignore
}

@rhcarvalhorhcarvalhoMay 10, 2021

Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
ContributorAuthor

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

Copy link
Copy Markdown
Contributor

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!

Comment threadpackages/tracing/src/integrations/postgres.ts
Comment threadpackages/tracing/src/hubextensions.ts Outdated
Comment threadpackages/tracing/src/hubextensions.ts Outdated
Comment threadpackages/tracing/src/hubextensions.ts
@kamilogorek

Copy link
Copy Markdown
ContributorAuthor

Is this going to work for all clients (the default one from init and custom clients)?

No, it won't work for custom clients, the same way as default integrations doesn't work for them.

Can users disable those DB integrations the same way they can disable other integrations?

Yes, with the same defaultIntegrations: false setting. Or filter them out as with regular integrations.

Comment threadpackages/tracing/src/hubextensions.ts Outdated
@kamilogorek
kamilogorek enabled auto-merge (squash) May 10, 2021 15:19
@kamilogorek
kamilogorek disabled auto-merge May 10, 2021 15:20
@kamilogorek
kamilogorek merged commit 117bc99 into masterMay 10, 2021
@kamilogorek
kamilogorek deleted the autoload-tracing-integrations branch May 10, 2021 15:41
@x5a

x5a commented May 18, 2021

Copy link
Copy Markdown

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.

@HazAT

Copy link
Copy Markdown
Member

@x5a Can you provide a bit more info; How does your stack look like / how can we repro this?

@x5a

x5a commented May 19, 2021

Copy link
Copy Markdown

@HazAT we're using postgres (pg@6.4.2) specifically in concert with pg-cursor@1.3.0. Because you monkeypatch Client.prototype.query and assume that the return value is thenable (it's not when working with https://node-postgres.com/api/cursor), we started getting an exception:

TypeError: orig.call(...).then is not a function\n' + ' at Client.query (/opt/app/node_modules/@sentry/tracing/dist/integrations/postgres.js:49:56)

which maps to: https://github.com/getsentry/sentry-javascript/blob/master/packages/tracing/src/integrations/postgres.ts#L63

@Kikobeats

Copy link
Copy Markdown

I opened a new issue to warn users the latest version is broken:

#3566

@elliotttf

Copy link
Copy Markdown

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:

conststream=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.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@kamilogorek@HazAT@x5a@Kikobeats@elliotttf@rhcarvalho