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
Conversation
@@ -116,6 +116,13 @@ def sentry_handler(event, context, *args, **kwargs): | |||
) | |||
hub.capture_event(event, hint=hint) | |||
reraise(*exc_info) | |||
finally: |
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.
@untitaker In this "finally" block, the code to stop the thread basically works in case of :-
- A successful run of lambda function.
- 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.
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 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, calltimer.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.)
Alternative to a Timer, Thread + Event to communicate the stop like in this example https://stackoverflow.com/a/325528/963287 |
if ( | ||
integration.timeout_warning | ||
and configured_time > TIMEOUT_WARNING_BUFFER | ||
): |
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.
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.
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.
Made this change in latest commit.
sentry_sdk/utils.py
Outdated
|
||
def stopped(self): | ||
# type: () -> Any | ||
return self._stop_event.is_set() | ||
|
||
def run(self): | ||
# type: () -> None | ||
|
||
time.sleep(self.waiting_time) |
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.
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
.
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.
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.
sentry_sdk/utils.py
Outdated
|
||
integer_configured_timeout = int(self.configured_timeout) | ||
if not self._stop_event.is_set(): |
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.
@rhcarvalho I made final changes, if these look okay then PR can be merged.
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.
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!
Fixes #846.