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

feat(core): Add async context abstraction #7753

Merged
merged 8 commits into from
Apr 5, 2023

Conversation

timfish
Copy link
Collaborator

@timfish timfish commented Apr 5, 2023

Adds AsyncContextStrategy abstraction to core.

I still have some major concerns over whether this should be in @sentry/core at all as it feels messy.

To cater for Node.js, the runWithHub signature in this PR does not actually suffice and would need to be something like below to cater for the existing domain code.

const local = domain.create();
local.add(req);
local.add(res);
local.run(() => {

function runWithHub<T>(callback: (hub: Hub) => T, domainAdd: EventEmitter[] = []): T;

EventEmitter doesn't exist outside of Node.js, so we'd need to create some matching types or do some any or unknown fudging.

If/when the async context proposal lands and runWithHub gets exported from @sentry/browser we almost certainly wont want to export this node specific signature.

If we export platform specific getCurrentHub() and runWithHub() from their respective packages, no changes are required in core apart from removing the existing platform specific code and the platform specific packages get one additional export (ie. runWithHub).

@timfish
Copy link
Collaborator Author

timfish commented Apr 5, 2023

Also, on the naming front, would it be better if runWithHub was called something like runWithAsyncContext to better describe what it's actually doing?

@AbhiPrasad
Copy link
Member

Can we make runWithHub just pass through it's arguments? We lose the type safety, but gain the flexibility.

function runWithHub<T, A extends unknown[]>(callback: (hub: Hub, ...args: A) => T,  ...args: A): T;

@AbhiPrasad
Copy link
Member

runWithAsyncContext

I like this - it's more backwards compat as well.

@lforst will be a fan :)

@lforst
Copy link
Member

lforst commented Apr 5, 2023

runWithAsyncContext

I like this - it's more backwards compat as well.

@lforst will be a fan :)

indeed <3

@timfish
Copy link
Collaborator Author

timfish commented Apr 5, 2023

Can we make runWithHub just pass through it's arguments?

Yeah possibly, and then just if (arg instanceof EventEmitter) before passing the args to add as a safety check.

@timfish
Copy link
Collaborator Author

timfish commented Apr 5, 2023

This is safer since we know all the args will be of the same type:

function runWithAsyncContext<T, A>(callback: (hub: Hub, ...args: A[]) => T, ...args: A[]): T;

@lforst
Copy link
Member

lforst commented Apr 5, 2023

This is safer since we know all the args will be of the same type:

function runWithAsyncContext<T, A>(callback: (hub: Hub, ...args: A[]) => T, ...args: A[]): T;
Maybe we'll need a default type of unknown for A 🤔

How about the following? I think this should work (at least as the user-facing type - internally we may need some casts):

function runWithAsyncContext<C extends (hub: Hub, ...args: any[]) => any>(callback: C): ReturnType<C>;

@timfish
Copy link
Collaborator Author

timfish commented Apr 5, 2023

How about the following? I think this should work (at least as the user-facing type - internally we may need some casts):

function runWithAsyncContext<C extends (hub: Hub, ...args: any[]) => any>(callback: C): ReturnType<C>;

We need to pass the callback args as parameters to the top level function so we can use them in our AsyncContextStrategy implementation like this.

I tried this PR in its current state and replaced some run and bind usages with runWithAsyncContext and the types look good. For the single usage where these parameters are used (ie. the node request handler) it needs to be called with types (runWithAsyncContext<void, EventEmitter>() since request and response are different types that both inherit EventEmitter. Everywhere else where there aren't args passed through the types are not needed.

In SDKs where these additional arguments are not required or a bit smelly, we could re-export runWithAsyncContext with these additional parameters removed. Since they're optional parameters, it wont matter if users import from core or the SDK.

@timfish timfish marked this pull request as ready for review April 5, 2023 15:52
packages/core/src/index.ts Show resolved Hide resolved
packages/core/src/hub.ts Outdated Show resolved Hide resolved
packages/core/src/hub.ts Show resolved Hide resolved
@timfish timfish marked this pull request as draft April 5, 2023 17:08
@timfish timfish marked this pull request as ready for review April 5, 2023 17:13
@AbhiPrasad AbhiPrasad merged commit 170ffc8 into getsentry:develop Apr 5, 2023
@timfish timfish deleted the async-context/add-abstraction branch April 5, 2023 17:36
Sign up for free to 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.

3 participants