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

Feature request: captureMethod decorator for sync/async methods #1204

Closed
2 tasks done
kennethwkz-mm opened this issue Dec 29, 2022 · 17 comments · Fixed by #1778
Closed
2 tasks done

Feature request: captureMethod decorator for sync/async methods #1204

kennethwkz-mm opened this issue Dec 29, 2022 · 17 comments · Fixed by #1778
Assignees
Labels
feature-request This item refers to a feature request for an existing or new utility rejected This is something we will not be working on. At least, not in the measurable future tracer This item relates to the Tracer Utility
Milestone

Comments

@kennethwkz-mm
Copy link

kennethwkz-mm commented Dec 29, 2022

Use case

Important

EDIT: see this comment for updated scope & context

For captureMethod - Method decorator able to support both async/sync function together

Solution/User Experience

We can add new option parameter - async to let user determine when the function need capture sync function.

class Something {
  tracer.captureMethod({ async: false })
  public myMethod {
     // do stuff
  }
}

Alternative solutions

const tracer = new Tracer();

const myFunction = () => { //do something  };

const handler = () => {
  const result = tracer.provider.captureFunc('my subsegment name', myFunction);
}

Acknowledgment

@kennethwkz-mm kennethwkz-mm added triage This item has not been triaged by a maintainer, please wait feature-request This item refers to a feature request for an existing or new utility labels Dec 29, 2022
@dreamorosi
Copy link
Contributor

Thank you for the feature request.

As discussed on Discord, I think the feature has merit. At the moment the decorator always assumes that the decorated method is async and as such it always returns a Promise and uses AWSXRay.captureAsyncFunc under the hood.

For most cases this is reasonable and/or harmless as it can be awaited by the caller, however I acknowledge that there are other cases in which this is not desirable.

One option to fix this would be the introduction of a parameter where the user specifies whether the decorated function is async or not (like the example in your post).

Another option, that should be investigated, would be to see if there's any way in which the decorator is able to automatically determine whether the method its decorating is sync or async, and based on that adjust its behavior and return type.

@dreamorosi dreamorosi added help-wanted We would really appreciate some support from community for this one tracer This item relates to the Tracer Utility discussing The issue needs to be discussed, elaborated, or refined need-more-information Requires more information before making any calls and removed triage This item has not been triaged by a maintainer, please wait labels Dec 30, 2022
@github-actions

This comment was marked as outdated.

@github-actions github-actions bot added the pending-close-response-required This issue will be closed soon unless the discussion moves forward label Feb 28, 2023
@dreamorosi dreamorosi removed pending-close-response-required This issue will be closed soon unless the discussion moves forward need-more-information Requires more information before making any calls labels Feb 28, 2023
@arnabrahman

This comment was marked as outdated.

@dreamorosi

This comment was marked as outdated.

@arnabrahman

This comment was marked as outdated.

@dreamorosi

This comment was marked as outdated.

@dreamorosi

This comment was marked as outdated.

@dreamorosi dreamorosi added this to the Version 2.0 milestone Jun 22, 2023
@paddymccarroll
Copy link

Hi - is this behaviour documented anywhere? If not, should it be added to the docs here? https://docs.powertools.aws.dev/lambda/typescript/main/core/tracer/#methods

@dreamorosi
Copy link
Contributor

Hi @paddymccarroll - sorry for the late reply.

You are right and this is not explicitly documented in the docs, and our API docs actually seem to suggest you can decorate synchronous methods.

We can definitely improve the docs & call out the limitation. Just out of curiosity: did you hit this limitation wanting to decorate a sync method?

@paddymccarroll
Copy link

Hi @dreamorosi - no worries, thanks for your reply.

Yep, I had a lot of the methods in my app decorated and only noticed this limitation when the changes were deployed and the app's functionality was broken due to those methods not being awaited. We have the tracing disabled locally, so it's not something I would have noticed there.

@arnabrahman
Copy link
Contributor

@dreamorosi Sorry, somehow I missed the notification. And about your suggestion, I am not sure. I think someone else who has a better understanding of javascript can take a look at this issue. Sorry couldn't be of much help here.

@dreamorosi
Copy link
Contributor

dreamorosi commented Sep 26, 2023

The previous iteration of the discussion around the issue focused on trying to find a way to use the same decorator to handle both synchronous and asynchronous methods.

With that effort not being successful, we should instead consider handling these two cases a separate and provide two decorators: captureMethod and captureMethodSync which can be used respectively on async and synchronous methods.

Ideally the bulk of the implementation for these decorators should be abstracted in a shared function and the each decorator should handle only the different signatures and await-ing of the decorated method.

@dreamorosi dreamorosi added confirmed The scope is clear, ready for implementation hacktoberfest and removed discussing The issue needs to be discussed, elaborated, or refined labels Sep 26, 2023
@dreamorosi dreamorosi changed the title Feature request: Enhance captureMethod options able to choose async/sync for non-promise function Feature request: captureMethod decorator for sync/async methods Oct 2, 2023
@shdq
Copy link
Contributor

shdq commented Oct 30, 2023

With that effort not being successful, we should instead consider handling these two cases separately and provide two decorators: captureMethod and captureMethodSync which can be used respectively on async and synchronous methods.

I read the conversation and looked at the decorator code. What if we use originalMethod.constructor.name === 'AsyncFunction' to check the original method and conditionally use captureFunc or captureAsyncFunc to make the decorator handle both sync and async functions? It looks like a quick fix, or am I missing something?

@dreamorosi
Copy link
Contributor

dreamorosi commented Oct 30, 2023

I read the conversation and looked at the decorator code. What if we use originalMethod.constructor.name === 'AsyncFunction' to check the original method and conditionally use captureFunc or captureAsyncFunc to make the decorator handle both sync and async functions? It looks like a quick fix, or am I missing something?

That was my original plan, however I wasn't able to find a way to make it work due to one branch of the function being sync and the other being async. For example, take this function:

const myFunc = async (method: Callable): Promise<X> => {
  if (method.constructor.name === 'AsyncFunction') {
    return await traceAsync()
  } else {
     return trace();
  }
}

Regardless of the branch evaluated at runtime, it will always be treated as asynchronous and return a promise because we are using the async keyword. Conversely, if we don't use the async keyword and instead write it synchronously like this:

const myFunc = (method: Callable): X => {
  if (method.constructor.name === 'AsyncFunction') {
    return traceAsync(); // this is still a pending promise that the caller has to await or .then()
  } else {
     return trace();
  }
}

Additionally, when it comes to typing this function, I didn't find any reliable way of making it apply the correct return type. TypeScript expects any function defined as async to return a Promise<T>, this means that regardless of what we write in it, the return type must be (and is) a promise.

In the past months, when working on the Batch Processing utility, which supports both sync & async record handlers, we ended up with discarding this idea (also because of what explained here) and instead be explicit about the type of operation. In that case we created a BatchProcessor (async) and a BatchProcessorSync.

We adopted async as default because most of modern code written in JavaScript is asynchronous, especially in Lambda & when interacting with AWS APIs. This way, even if this means some duplication on our side, customers immediately know what to expect and the types/implementation is also more straightforward.

@dreamorosi dreamorosi added on-hold This item is on-hold and will be revisited in the future and removed help-wanted We would really appreciate some support from community for this one confirmed The scope is clear, ready for implementation hacktoberfest labels Oct 31, 2023
@dreamorosi
Copy link
Contributor

After discussing this internally once again we have decided to not move forward with this feature for the time being, and instead improve our documentation to clarify that our decorators are currently compatible with asynchronous class methods only (#1778).

Before leaving this comment I have also discussed this with @shdq - who was looking into this topic - on Discord and we are aligned on the decision.

@dreamorosi dreamorosi closed this as not planned Won't fix, can't repro, duplicate, stale Nov 1, 2023
@dreamorosi dreamorosi added rejected This is something we will not be working on. At least, not in the measurable future and removed on-hold This item is on-hold and will be revisited in the future labels Nov 1, 2023
@dreamorosi dreamorosi self-assigned this Nov 1, 2023
Copy link
Contributor

github-actions bot commented Nov 1, 2023

⚠️ COMMENT VISIBILITY WARNING ⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Copy link
Contributor

github-actions bot commented Nov 2, 2023

⚠️ COMMENT VISIBILITY WARNING ⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@github-actions github-actions bot added pending-release This item has been merged and will be released soon and removed rejected This is something we will not be working on. At least, not in the measurable future labels Nov 2, 2023
@dreamorosi dreamorosi added rejected This is something we will not be working on. At least, not in the measurable future and removed pending-release This item has been merged and will be released soon labels Nov 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request This item refers to a feature request for an existing or new utility rejected This is something we will not be working on. At least, not in the measurable future tracer This item relates to the Tracer Utility
Projects
5 participants