-
-
Notifications
You must be signed in to change notification settings - Fork 377
Handle timeouts more gracefully by allowing the application to shutdown #895
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
Open
Nyholm
wants to merge
9
commits into
brefphp:master
Choose a base branch
from
Nyholm:timeouts
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
e78d2c6
Handle timeouts more gracefully by throwing exception near the hard end
Nyholm 5a57044
Added documentation for Timeouts
Nyholm b89aa04
Adding a note about debugging
Nyholm 52f8d65
Make Timeout::init() private
Nyholm 37a5759
Make this feature opt-in
Nyholm a2e46ca
Make sure the shutdown process has at least 1 second.
Nyholm 0b6d7b2
Always base the timeout duration on the configured Lambda timeout (#3)
mnapoli 9356e4a
Do 2 alarms
Nyholm 2efd449
fixed tests
Nyholm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| --- | ||
| title: Timeouts | ||
| current_menu: timeouts | ||
| introduction: Configure and handle timeouts. | ||
| --- | ||
|
|
||
| When a Lambda function times out, it is like the power to the computer is suddenly | ||
| just turned off. This does not give the application a chance to shut down properly. | ||
| This leaves you without any logs and the problem could be hard to fix. | ||
|
|
||
| To allow your application to shut down properly and write logs, Bref can throw an exception just before the Lambda times out. | ||
|
|
||
| > Note, this feature is experimental and available since Bref 1.3. | ||
|
|
||
| To enable this feature **in `php-XX` layers**, set the environment variable `BREF_FEATURE_TIMEOUT`: | ||
|
|
||
| ```yaml | ||
| provider: | ||
| environment: | ||
| BREF_FEATURE_TIMEOUT: 1 | ||
| ``` | ||
|
|
||
| To enable this feature **in `php-XX-fpm` layers**, call `Timeout::enableInFpm()` in your application. | ||
| For example in `index.php`: | ||
|
|
||
| ```php | ||
| if (isset($_SERVER['LAMBDA_TASK_ROOT'])) { | ||
| \Bref\Timeout\Timeout::enableInFpm(); | ||
| } | ||
| ``` | ||
|
|
||
| Whenever a timeout happens, a full stack trace will be logged, including the line that was executing. | ||
|
|
||
| In most cases, it is an external call to a database, cache or API that is stuck waiting. | ||
| If you are using a RDS database, [you are encouraged to read this section](database.md#accessing-the-internet). | ||
|
|
||
| ## Catching the exception | ||
|
|
||
| You can catch the timeout exception to perform some cleanup, logs or even display a proper error page. | ||
|
|
||
| In `php-XX-fpm` layers, most frameworks will catch the `LambdaTimeout` exception automatically (like any other error). | ||
|
|
||
| In `php-XX` layers, you can catch it in your handlers. For example: | ||
|
|
||
| ```php | ||
| use Bref\Context\Context; | ||
| use Bref\Timeout\LambdaTimeout; | ||
|
|
||
| class Handler implements \Bref\Event\Handler | ||
| { | ||
| public function handle($event, Context $context) | ||
| { | ||
| try { | ||
| // your code here | ||
| // ... | ||
| } catch (LambdaTimeout $e) { | ||
| echo 'Oops, sorry. We spent too much time on this.'; | ||
| } catch (\Throwable $e) { | ||
| echo 'Some other unexpected error happened.'; | ||
| } | ||
| } | ||
| } | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <?php declare(strict_types=1); | ||
|
|
||
| namespace Bref\Timeout; | ||
|
|
||
| /** | ||
| * The application took too long to produce a response. This exception is thrown | ||
| * to give the application a chance to flush logs and shut it self down before | ||
| * the power to AWS Lambda is disconnected. | ||
| */ | ||
| class LambdaTimeout extends \RuntimeException | ||
| { | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| <?php declare(strict_types=1); | ||
|
|
||
| namespace Bref\Timeout; | ||
|
|
||
| /** | ||
| * Helper class to trigger an exception just before the Lambda times out. This | ||
| * will give the application a chance to shut down. | ||
| */ | ||
| final class Timeout | ||
| { | ||
| /** @var bool */ | ||
| private static $initialized = false; | ||
|
|
||
| /** | ||
| * Automatically setup a timeout (based on the AWS Lambda timeout). | ||
| * | ||
| * This method can only be called when running in PHP-FPM, i.e. when using a `php-XX-fpm` layer. | ||
| */ | ||
| public static function enableInFpm(): void | ||
| { | ||
| if (! isset($_SERVER['LAMBDA_INVOCATION_CONTEXT'])) { | ||
| throw new \LogicException('Could not find value for bref timeout. Are we running on Lambda?'); | ||
| } | ||
|
|
||
| $context = json_decode($_SERVER['LAMBDA_INVOCATION_CONTEXT'], true, 512, JSON_THROW_ON_ERROR); | ||
| $deadlineMs = $context['deadlineMs']; | ||
| $remainingTimeInMillis = $deadlineMs - intval(microtime(true) * 1000); | ||
|
|
||
| self::enable($remainingTimeInMillis); | ||
| } | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| public static function enable(int $remainingTimeInMillis): void | ||
| { | ||
| self::init(); | ||
|
|
||
| $remainingTimeInSeconds = (int) floor($remainingTimeInMillis / 1000); | ||
|
|
||
| // The script will timeout 1 second before the remaining time | ||
| // to allow some time for Bref/our app to recover and cleanup | ||
| $margin = 1; | ||
|
|
||
| $timeoutDelayInSeconds = max(1, $remainingTimeInSeconds - $margin); | ||
|
|
||
| // Trigger SIGALRM in X seconds | ||
| pcntl_alarm($timeoutDelayInSeconds); | ||
| } | ||
|
|
||
| /** | ||
| * Setup custom handler for SIGALRM. | ||
| */ | ||
| private static function init(): void | ||
| { | ||
| if (self::$initialized) { | ||
| return; | ||
| } | ||
|
|
||
| if (! function_exists('pcntl_async_signals')) { | ||
| trigger_error('Could not enable timeout exceptions because pcntl extension is not enabled.'); | ||
| return; | ||
| } | ||
|
|
||
| pcntl_async_signals(true); | ||
| // Setup a handler for SIGALRM that throws an exception | ||
| // This will interrupt any running PHP code, including `sleep()` or code stuck waiting for I/O. | ||
| pcntl_signal(SIGALRM, function (): void { | ||
| throw new LambdaTimeout('Maximum AWS Lambda execution time reached'); | ||
| }); | ||
|
|
||
| self::$initialized = true; | ||
| } | ||
|
|
||
| /** | ||
| * Reset timeout. | ||
| * | ||
| * @internal | ||
| */ | ||
| public static function reset(): void | ||
| { | ||
| if (self::$initialized) { | ||
| pcntl_alarm(0); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.