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

Fix incorrect timeout warnings in AWS Lambda and GCP integrations #854

Merged
merged 6 commits into from Oct 14, 2020

Conversation

shantanu73
Copy link
Contributor

@shantanu73 shantanu73 commented Oct 12, 2020

  1. Added code to stop thread in aws_lambda.py & gcp.py.
  2. Modified logic of run() function of class TimeoutThread to stop the thread and raise ServerlessTimeoutWarning exception, conditionally.

Fixes #846.

1) Added code to stop thread in aws_lambda.py & gcp.py.
2) Modified logic of run() function of class TimeoutThread to stop the thread and raise ServerlessTimeoutWarning exception, conditionally.
@@ -116,6 +116,13 @@ def sentry_handler(event, context, *args, **kwargs):
)
hub.capture_event(event, hint=hint)
reraise(*exc_info)
finally:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@untitaker In this "finally" block, the code to stop the thread basically works in case of :-

  1. A successful run of lambda function.
  2. When an exception is there in the code.
    But, it doesn't stop the thread in case of timeout, which is an expected behavior.

By "stop the thread" I mean explicitly stopping a thread by sending a stop signal.

1) Improved the logic of run() function of class TimeoutThread to remove the dependency on time.sleep() method.
Copy link
Contributor

@rhcarvalho rhcarvalho left a comment

Choose a reason for hiding this comment

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

Maybe using a Timer would make things cleaner.

  • Every handler execution starts a new timer (unless we don't have enough "buffer"/threshold).
  • Whenever the original handler finishes running (the new finally block in this PR) and a timer has been started, call timer.cancel() -- no additional condition.
  • The Timer runs a function that raises the timeout exception, no conditions.

The above is my suggestion.


Side note
Because the runtime can abort/pause the handler at any time, I imagine there'd be room for the timer firing incorrectly into a future handler execution, but not sure if there's anything we could do about it. (I thought of an Event to mark the end of the handler and the timer could check it, but that doesn't change anything if the finally block of one execution runs into the next execution -- depends how the timeout is implemented by the runtime.)

sentry_sdk/integrations/aws_lambda.py Outdated Show resolved Hide resolved
sentry_sdk/utils.py Outdated Show resolved Hide resolved
sentry_sdk/utils.py Outdated Show resolved Hide resolved
@rhcarvalho
Copy link
Contributor

Alternative to a Timer, Thread + Event to communicate the stop like in this example https://stackoverflow.com/a/325528/963287
Event.wait(timeout) can be used instead of the loop that sleeps second by second.

Comment on lines 120 to 123
if (
integration.timeout_warning
and configured_time > TIMEOUT_WARNING_BUFFER
):
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that timeout_thread is only conditionally defined. That's why you need to repeat this condition here.

It would be clearer to always define timeout_thread, initially setting it to None.
That way, here it would become:

if timeout_thread is not None:
    timeout_thread.stop()

.stop() sets a "stop event" to true, unblocking and terminating the thread if still running, and doing nothing otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made this change in latest commit.

1) Removed CPU consuming while loop from logic of run method of TimeoutThread class.
2) Used Event class from threading module to send a stop signal for the thread.
3) Changed cancel() to stop() for stopping thread  for AWS and GCP integration files.

def stopped(self):
# type: () -> Any
return self._stop_event.is_set()

def run(self):
# type: () -> None

time.sleep(self.waiting_time)
Copy link
Contributor

Choose a reason for hiding this comment

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

The thread doesn't need to sleep for the full waiting_time. When the handler returns and we call timeout_thread.stop(), the thread should wake up and terminate.

That's why I suggested using self._stop_event.wait(self.waiting_time).

https://docs.python.org/2.7/library/threading.html#threading.Event.wait

You can either use the return value or wait or .is_set() to decide whether to raise ServerlessTimeoutWarning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I figured that while I did testing and found it was still waiting for complete waiting time.
I've made final changes accordingly now.

1) Replaced time.sleep() method with wait() method so that the thread terminates before completing the full waiting time.

integer_configured_timeout = int(self.configured_timeout)
if not self._stop_event.is_set():
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rhcarvalho I made final changes, if these look okay then PR can be merged.

@rhcarvalho rhcarvalho changed the title Fix for Bug #846 : Incorrect timeout warnings when using AWS Lambda integration Fix incorrect timeout warnings in AWS Lambda and GCP integrations Oct 14, 2020
Copy link
Contributor

@rhcarvalho rhcarvalho left a comment

Choose a reason for hiding this comment

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

Thanks @shantanu73 👍

I've made a small change to avoid increasing indentation.
Would be nice to have a test case to cover this bug and prevent regression in a follow up.

Let's get this in first though. Thanks again!

@rhcarvalho rhcarvalho merged commit e12a350 into getsentry:master Oct 14, 2020
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.

Incorrect timeout warnings when using AWS Lambda integration
2 participants