rack-timeout 0.5.1 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +120 -0
- data/README.md +118 -0
- data/UPGRADING.md +19 -0
- data/doc/risks.md +0 -1
- data/doc/settings.md +52 -0
- data/lib/rack/timeout/core.rb +30 -6
- data/lib/rack/timeout/logger.rb +0 -1
- data/lib/rack/timeout/logging-observer.rb +1 -1
- data/lib/rack/timeout/support/monotonic_time.rb +0 -1
- data/lib/rack/timeout/support/scheduler.rb +0 -1
- data/lib/rack/timeout/support/timeout.rb +0 -1
- data/lib/rack-timeout.rb +1 -1
- data/test/env_settings_test.rb +7 -0
- data/test/test_helper.rb +0 -1
- metadata +15 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fafa5a90f1af90fdcc6ce081752ba684aac2652c1fd31867274ab9fc01a7c778
|
4
|
+
data.tar.gz: 6b3f1a800e031218a1e026737614b9d087d037c9ae8407ef788b3f2d404cb50a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9390e4bb587d82e2ae0574397f9e02d61c5a13e162d980ad972d174f158d43a241949fdc77ae34d4206f01ec2dbd92d9c66d6bdb886ed4065dace963daf30b15
|
7
|
+
data.tar.gz: e4d838274207478d4ca0767bac9b702640aebdaada4ca2aa4f077fc79c23d4c34ab7744d2b3af44c58479cf5ad1a09e3c11e241add93eb6f5c906883c7dd4291
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
## 0.6.1
|
2
|
+
|
3
|
+
- RACK_TIMEOUT_TERM_ON_TIMEOUT can be set to zero to disable (https://github.com/sharpstone/rack-timeout/pull/161)
|
4
|
+
|
5
|
+
## 0.6.0
|
6
|
+
|
7
|
+
- Allow sending SIGTERM to workers on timeout (https://github.com/sharpstone/rack-timeout/pull/157)
|
8
|
+
|
9
|
+
0.5.2
|
10
|
+
=====
|
11
|
+
- Rails 6 support (#147)
|
12
|
+
|
13
|
+
0.5.1
|
14
|
+
=====
|
15
|
+
- Fixes setting ENV vars to false or 0 would not disable a timeout
|
16
|
+
(#133)
|
17
|
+
|
18
|
+
0.5.0.1
|
19
|
+
=======
|
20
|
+
- Fix 0600 permissions in gem pushed to rubygems
|
21
|
+
|
22
|
+
0.5.0
|
23
|
+
=====
|
24
|
+
|
25
|
+
Breaking Changes
|
26
|
+
|
27
|
+
- Remove Rollbar module (#124)
|
28
|
+
- Remove legacy class setters (#125)
|
29
|
+
|
30
|
+
Other
|
31
|
+
|
32
|
+
- Add support to configure via environment variables (#105)
|
33
|
+
- Adds support for ActionDispatch::RequestId generated request ids (#115)
|
34
|
+
- Changes uuid format to proper uuid (#115)
|
35
|
+
|
36
|
+
0.4.2
|
37
|
+
=====
|
38
|
+
- Ruby 2.0 compatible
|
39
|
+
|
40
|
+
0.4.1
|
41
|
+
=====
|
42
|
+
- Rails 5 support
|
43
|
+
- Remove deprecation warning on timeout setter for Rails apps
|
44
|
+
|
45
|
+
0.4.0
|
46
|
+
=====
|
47
|
+
- Using monotonic time instead of Time.now where available (/ht concurrent-ruby)
|
48
|
+
- Settings are now passable to the middleware initializer instead of class-level
|
49
|
+
- Rollbar module may take a custom fingerprint block
|
50
|
+
- Rollbar module considered final
|
51
|
+
- Fixed an issue where some heartbeats would live on forever (#103, /ht @0x0badc0de)
|
52
|
+
|
53
|
+
0.3.2
|
54
|
+
=====
|
55
|
+
- Fixes calling timeout with a value of 0 (issue #90)
|
56
|
+
|
57
|
+
0.3.1
|
58
|
+
=====
|
59
|
+
- Rollbar module improvements
|
60
|
+
|
61
|
+
0.3.0
|
62
|
+
=====
|
63
|
+
- use a single scheduler thread to manage timeouts, instead of one timeout thread per request
|
64
|
+
- instead of inserting middleware at position 0 for rails, insert before Rack::Runtime (which is right after Rack::Lock and the static file stuff)
|
65
|
+
- reshuffle error types: RequestExpiryError is again a RuntimeError, and timeouts raise a RequestTimeoutException, an Exception, and not descending from Rack::Timeout::Error (see README for more)
|
66
|
+
- don't insert middleware for rails in test environment
|
67
|
+
- add convenience module Rack::Timeout::Logger (see README for more)
|
68
|
+
- StageChangeLoggingObserver renamed to StateChangeLoggingObserver, works slightly differently too
|
69
|
+
- file layout reorganization (see 6e82c276 for details)
|
70
|
+
- CHANGELOG file is now in the gem (@dbackeus)
|
71
|
+
- add optional and experimental support for grouping errors by url under rollbar. see "rack/timeout/rollbar" for usage
|
72
|
+
|
73
|
+
0.2.4
|
74
|
+
=====
|
75
|
+
- Previous fix was borked.
|
76
|
+
|
77
|
+
0.2.3
|
78
|
+
=====
|
79
|
+
- Ignore Rack::NullLogger when picking a logger
|
80
|
+
|
81
|
+
0.2.1
|
82
|
+
=====
|
83
|
+
- Fix raised error messages
|
84
|
+
|
85
|
+
0.2.0
|
86
|
+
=====
|
87
|
+
- Added CHANGELOG
|
88
|
+
- Rack::Timeout::Error now inherits from Exception instead of StandardError, with the hope users won't rescue from it accidentally
|
89
|
+
|
90
|
+
0.1.2
|
91
|
+
=====
|
92
|
+
- improve RequestTimeoutError error string so @watsonian is happy
|
93
|
+
|
94
|
+
0.1.1
|
95
|
+
=====
|
96
|
+
- README updates
|
97
|
+
- fix that setting properties to false resulted in an error
|
98
|
+
|
99
|
+
0.1.0
|
100
|
+
=====
|
101
|
+
- Rewrote README
|
102
|
+
|
103
|
+
0.1.0beta4
|
104
|
+
==========
|
105
|
+
- Renamed `timeout` setting to `service_timeout`; `timeout=` still works for backwards compatibility
|
106
|
+
– `MAX_REQUEST_AGE` is gone, the `wait_timeout` setting more or less replaces it
|
107
|
+
- Renamed `overtime` setting to `wait_overtime`
|
108
|
+
- overtime setting should actually work (It had never made it to beta3)
|
109
|
+
- In the request info struct, renamed `age` to `wait`, `duration` to `service`
|
110
|
+
- Rack::Timeout::StageChangeLogger is gone, replaced by Rack::Timeout::StageChangeLoggingObserver, which is an observer class that composites with a logger, instead of inheriting from Logger. Anything logging related will likely be incompatible with previous beta release.
|
111
|
+
- Log level can no longer be set with env vars, has to be set in the logger being used. (Which can now be custom / user-provided.)
|
112
|
+
|
113
|
+
0.1.0beta1,2,3
|
114
|
+
==============
|
115
|
+
- Dropped ruby 1.8.x support
|
116
|
+
- Dropped rails 2 support
|
117
|
+
- Added rails 4 support
|
118
|
+
- Added much logging
|
119
|
+
– Added support for dropping requests that waited too long in the queue without ever handling them
|
120
|
+
- Other things I can't remember, see git logs :P
|
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
Rack::Timeout
|
2
|
+
=============
|
3
|
+
|
4
|
+
Abort requests that are taking too long; an exception is raised.
|
5
|
+
|
6
|
+
A timeout of 15s is the default. It's recommended to set the timeout as
|
7
|
+
low as realistically viable for your application. You can modify this by
|
8
|
+
setting the `RACK_TIMEOUT_SERVICE_TIMEOUT` environment variable.
|
9
|
+
|
10
|
+
There's a handful of other settings, read on for details.
|
11
|
+
|
12
|
+
Rack::Timeout is not a solution to the problem of long-running requests,
|
13
|
+
it's a debug and remediation tool. App developers should track
|
14
|
+
rack-timeout's data and address recurring instances of particular
|
15
|
+
timeouts, for example by refactoring code so it runs faster or
|
16
|
+
offsetting lengthy work to happen asynchronously.
|
17
|
+
|
18
|
+
Upgrading
|
19
|
+
---------
|
20
|
+
|
21
|
+
For fixing issues when upgrading, please see [UPGRADING](UPGRADING.md).
|
22
|
+
|
23
|
+
Basic Usage
|
24
|
+
-----------
|
25
|
+
|
26
|
+
The following covers currently supported versions of Rails, Rack, Ruby,
|
27
|
+
and Bundler. See the Compatibility section at the end for legacy
|
28
|
+
versions.
|
29
|
+
|
30
|
+
### Rails apps
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
# Gemfile
|
34
|
+
gem "rack-timeout"
|
35
|
+
```
|
36
|
+
|
37
|
+
This will load rack-timeout and set it up as a Rails middleware using
|
38
|
+
the default timeout of 15s. The middleware is not inserted for the test
|
39
|
+
environment. You can modify the timeout by setting a
|
40
|
+
`RACK_TIMEOUT_SERVICE_TIMEOUT` environment variable.
|
41
|
+
|
42
|
+
### Rails apps, manually
|
43
|
+
|
44
|
+
You'll need to do this if you removed `Rack::Runtime` from the
|
45
|
+
middleware stack, or if you want to determine yourself where in the
|
46
|
+
stack `Rack::Timeout` gets inserted.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# Gemfile
|
50
|
+
gem "rack-timeout", require: "rack/timeout/base"
|
51
|
+
```
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# config/initializers/rack_timeout.rb
|
55
|
+
|
56
|
+
# insert middleware wherever you want in the stack, optionally pass
|
57
|
+
# initialization arguments, or use environment variables
|
58
|
+
Rails.application.config.middleware.insert_before Rack::Runtime, Rack::Timeout, service_timeout: 15
|
59
|
+
```
|
60
|
+
|
61
|
+
### Sinatra and other Rack apps
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
# config.ru
|
65
|
+
require "rack-timeout"
|
66
|
+
|
67
|
+
# Call as early as possible so rack-timeout runs before all other middleware.
|
68
|
+
# Setting service_timeout or `RACK_TIMEOUT_SERVICE_TIMEOUT` environment
|
69
|
+
# variable is recommended. If omitted, defaults to 15 seconds.
|
70
|
+
use Rack::Timeout, service_timeout: 15
|
71
|
+
```
|
72
|
+
|
73
|
+
Configuring
|
74
|
+
-----------
|
75
|
+
|
76
|
+
Rack::Timeout takes the following settings, shown here with their
|
77
|
+
default values and associated environment variables.
|
78
|
+
|
79
|
+
```
|
80
|
+
service_timeout: 15 # RACK_TIMEOUT_SERVICE_TIMEOUT
|
81
|
+
wait_timeout: 30 # RACK_TIMEOUT_WAIT_TIMEOUT
|
82
|
+
wait_overtime: 60 # RACK_TIMEOUT_WAIT_OVERTIME
|
83
|
+
service_past_wait: false # RACK_TIMEOUT_SERVICE_PAST_WAIT
|
84
|
+
term_on_timeout: false # RACK_TIMEOUT_TERM_ON_TIMEOUT
|
85
|
+
```
|
86
|
+
|
87
|
+
These settings can be overriden during middleware initialization or
|
88
|
+
environment variables `RACK_TIMEOUT_*` mentioned above. Middleware
|
89
|
+
parameters take precedence:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
use Rack::Timeout, service_timeout: 15, wait_timeout: 30
|
93
|
+
```
|
94
|
+
|
95
|
+
For more on these settings, please see [doc/settings](doc/settings.md).
|
96
|
+
|
97
|
+
Further Documentation
|
98
|
+
---------------------
|
99
|
+
|
100
|
+
Please see the [doc](doc) folder for further documentation on:
|
101
|
+
|
102
|
+
* [Risks and shortcomings of using Rack::Timeout](doc/risks.md)
|
103
|
+
* [Understanding the request lifecycle](doc/request-lifecycle.md)
|
104
|
+
* [Exceptions raised by Rack::Timeout](doc/exceptions.md)
|
105
|
+
* [Rollbar fingerprinting](doc/rollbar.md)
|
106
|
+
* [Observers](doc/observers.md)
|
107
|
+
* [Logging](doc/logging.md)
|
108
|
+
|
109
|
+
Compatibility
|
110
|
+
-------------
|
111
|
+
|
112
|
+
This version of Rack::Timeout is compatible with Ruby 2.1 and up, and,
|
113
|
+
for Rails apps, Rails 3.x and up.
|
114
|
+
|
115
|
+
|
116
|
+
---
|
117
|
+
Copyright © 2010-2020 Caio Chassot, released under the MIT license
|
118
|
+
<http://github.com/sharpstone/rack-timeout>
|
data/UPGRADING.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Upgrading
|
2
|
+
=========
|
3
|
+
|
4
|
+
From 0.4 or older
|
5
|
+
-----------------
|
6
|
+
|
7
|
+
- Removal of the class setters, such as `Rack::Timeout.timeout = 5`, may
|
8
|
+
lead to an error when upgrading. To fix this, remove these setters and
|
9
|
+
instead use either the [environment variables][config-env],
|
10
|
+
`RACK_TIMEOUT_*`, or [insert the middleware manually][config-insert]
|
11
|
+
and configure the middleware as desired when inserting.
|
12
|
+
|
13
|
+
[config-env]: README.md#configuring
|
14
|
+
[config-insert]: README.md#rails-apps-manually
|
15
|
+
|
16
|
+
- The Rollbar support was removed; a deprecation warning will be emitted
|
17
|
+
if you are using this module. The recommendation is to use Rollbar's
|
18
|
+
custom fingerprinting. A recommendation is provided in
|
19
|
+
[doc/rollbar.md](doc/rollbar.md).
|
data/doc/risks.md
CHANGED
@@ -26,7 +26,6 @@ That said, it's something to be aware of, and may explain some eerie wonkiness s
|
|
26
26
|
[broken-timeout]: http://headius.blogspot.de/2008/02/rubys-threadraise-threadkill-timeoutrb.html
|
27
27
|
[handle-interrupt]: http://www.ruby-doc.org/core-2.1.3/Thread.html#method-c-handle_interrupt
|
28
28
|
|
29
|
-
|
30
29
|
### Time Out Early and Often
|
31
30
|
|
32
31
|
Because of the aforementioned issues, it's recommended you set library-specific timeouts and leave Rack::Timeout as a last resort measure. Library timeouts will generally take care of IO issues and abort the operation safely. See [The Ultimate Guide to Ruby Timeouts][ruby-timeouts].
|
data/doc/settings.md
CHANGED
@@ -47,3 +47,55 @@ This extra time is called *wait overtime* and can be set via `wait_overtime`. It
|
|
47
47
|
Keep in mind that Heroku [recommends][uploads] uploading large files directly to S3, so as to prevent the dyno from being blocked for too long and hence unable to handle further incoming requests.
|
48
48
|
|
49
49
|
[uploads]: https://devcenter.heroku.com/articles/s3#file-uploads
|
50
|
+
|
51
|
+
### Term on Timeout
|
52
|
+
|
53
|
+
If your application timeouts fire frequently then [they can cause your application to enter a corrupt state](https://www.schneems.com/2017/02/21/the-oldest-bug-in-ruby-why-racktimeout-might-hose-your-server/). One option for resetting that bad state is to restart the entire process. If you are running in an environment with multiple processes (such as `puma -w 2`) then when a process is sent a `SIGTERM` it will exit. The webserver then knows how to restart the process. For more information on process restart behavior see:
|
54
|
+
|
55
|
+
- [Ruby Application Restart Behavior](https://devcenter.heroku.com/articles/what-happens-to-ruby-apps-when-they-are-restarted)
|
56
|
+
- [License to SIGKILL](https://www.sitepoint.com/license-to-sigkill/)
|
57
|
+
|
58
|
+
**Puma SIGTERM behavior** When a Puma worker receives a `SIGTERM` it will begin to shut down, but not exit right away. It stops accepting new requests and waits for any existing requests to finish before fully shutting down. This means that only the request that experiences a timeout will be interupted, all other in-flight requests will be allowed to run until they return or also are timed out.
|
59
|
+
|
60
|
+
After the worker process exists will Puma's parent process know to boot a replacement worker. While one process is restarting, another can still serve requests (if you have more than 1 worker process per server/dyno). Between when a process exits and when a new process boots, there will be a reduction in throughput. If all processes are restarting, then incoming requests will be blocked while new processes boot.
|
61
|
+
|
62
|
+
**How to enable** To enable this behavior you can set `term_on_timeout: 1` to an integer value. If you set it to one, then the first time the process encounters a timeout, it will receive a SIGTERM.
|
63
|
+
|
64
|
+
To enable on Heroku run:
|
65
|
+
|
66
|
+
```
|
67
|
+
$ heroku config:set RACK_TIMEOUT_TERM_ON_TIMEOUT=1
|
68
|
+
```
|
69
|
+
|
70
|
+
**Caution** If you use this setting inside of a webserver without enabling multi-process mode, then it will exit the entire server when it fires:
|
71
|
+
|
72
|
+
- ✅ `puma -w 2 -t 5` This is OKAY
|
73
|
+
- ❌ `puma -t 5` This is NOT OKAY
|
74
|
+
|
75
|
+
If you're using a `config/puma.rb` file then make sure you are calling `workers` configuration DSL. You should see multiple workers when the server boots:
|
76
|
+
|
77
|
+
```
|
78
|
+
[3922] Puma starting in cluster mode...
|
79
|
+
[3922] * Version 4.3.0 (ruby 2.6.5-p114), codename: Mysterious Traveller
|
80
|
+
[3922] * Min threads: 0, max threads: 16
|
81
|
+
[3922] * Environment: development
|
82
|
+
[3922] * Process workers: 2
|
83
|
+
[3922] * Phased restart available
|
84
|
+
[3922] * Listening on tcp://0.0.0.0:9292
|
85
|
+
[3922] Use Ctrl-C to stop
|
86
|
+
[3922] - Worker 0 (pid: 3924) booted, phase: 0
|
87
|
+
[3922] - Worker 1 (pid: 3925) booted, phase: 0
|
88
|
+
```
|
89
|
+
|
90
|
+
> ✅ Notice how it says it is booting in "cluster mode" and how it gives PIDs for two worker processes at the bottom.
|
91
|
+
|
92
|
+
**How to decide the term_on_timeout value** If you set to a higher value such as `5` then rack-timeout will wait until the process has experienced five timeouts before restarting the process. Setting this value to a higher number means the application restarts processes less frequently, so throughput will be less impacted. If you set it to too high of a number, then the underlying issue of the application being put into a bad state will not be effectively mitigated.
|
93
|
+
|
94
|
+
|
95
|
+
**How do I know when a process is being restarted by rack-timeout?** This exception error should be visible in the logs:
|
96
|
+
|
97
|
+
```
|
98
|
+
Request ran for longer than 1000ms, sending SIGTERM to process 3925
|
99
|
+
```
|
100
|
+
|
101
|
+
> Note: Since the worker waits for all in-flight requests to finish (with puma) you may see multiple SIGTERMs to the same PID before it exits, this means that multiple requests timed out.
|
data/lib/rack/timeout/core.rb
CHANGED
@@ -30,6 +30,7 @@ module Rack
|
|
30
30
|
:service, # time rack spent processing the request (updated ~ every second)
|
31
31
|
:timeout, # the actual computed timeout to be used for this request
|
32
32
|
:state, # the request's current state, see VALID_STATES below
|
33
|
+
:term,
|
33
34
|
) {
|
34
35
|
def ms(k) # helper method used for formatting values in milliseconds
|
35
36
|
"%.fms" % (self[k] * 1000) if self[k]
|
@@ -62,13 +63,20 @@ module Rack
|
|
62
63
|
:service_timeout, # How long the application can take to complete handling the request once it's passed down to it.
|
63
64
|
:wait_timeout, # How long the request is allowed to have waited before reaching rack. If exceeded, the request is 'expired', i.e. dropped entirely without being passed down to the application.
|
64
65
|
:wait_overtime, # Additional time over @wait_timeout for requests with a body, like POST requests. These may take longer to be received by the server before being passed down to the application, but should not be expired.
|
65
|
-
:service_past_wait
|
66
|
+
:service_past_wait, # when false, reduces the request's computed timeout from the service_timeout value if the complete request lifetime (wait + service) would have been longer than wait_timeout (+ wait_overtime when applicable). When true, always uses the service_timeout value. we default to false under the assumption that the router would drop a request that's not responded within wait_timeout, thus being there no point in servicing beyond seconds_service_left (see code further down) up until service_timeout.
|
67
|
+
:term_on_timeout
|
66
68
|
|
67
|
-
def initialize(app, service_timeout:nil, wait_timeout:nil, wait_overtime:nil, service_past_wait:"not_specified")
|
69
|
+
def initialize(app, service_timeout:nil, wait_timeout:nil, wait_overtime:nil, service_past_wait:"not_specified", term_on_timeout: nil)
|
70
|
+
@term_on_timeout = read_timeout_property term_on_timeout, ENV.fetch("RACK_TIMEOUT_TERM_ON_TIMEOUT", 0).to_i
|
68
71
|
@service_timeout = read_timeout_property service_timeout, ENV.fetch("RACK_TIMEOUT_SERVICE_TIMEOUT", 15).to_i
|
69
72
|
@wait_timeout = read_timeout_property wait_timeout, ENV.fetch("RACK_TIMEOUT_WAIT_TIMEOUT", 30).to_i
|
70
73
|
@wait_overtime = read_timeout_property wait_overtime, ENV.fetch("RACK_TIMEOUT_WAIT_OVERTIME", 60).to_i
|
71
74
|
@service_past_wait = service_past_wait == "not_specified" ? ENV.fetch("RACK_TIMEOUT_SERVICE_PAST_WAIT", false).to_s != "false" : service_past_wait
|
75
|
+
|
76
|
+
Thread.main['RACK_TIMEOUT_COUNT'] ||= 0
|
77
|
+
if @term_on_timeout
|
78
|
+
raise "Current Runtime does not support processes" unless ::Process.respond_to?(:fork)
|
79
|
+
end
|
72
80
|
@app = app
|
73
81
|
end
|
74
82
|
|
@@ -90,7 +98,9 @@ module Rack
|
|
90
98
|
seconds_waited = 0 if seconds_waited < 0 # make up for potential time drift between the routing server and the application server
|
91
99
|
final_wait_timeout = wait_timeout + effective_overtime # how long the request will be allowed to have waited
|
92
100
|
seconds_service_left = final_wait_timeout - seconds_waited # first calculation of service timeout (relevant if request doesn't get expired, may be overriden later)
|
93
|
-
info.wait
|
101
|
+
info.wait = seconds_waited # updating the info properties; info.timeout will be the wait timeout at this point
|
102
|
+
info.timeout = final_wait_timeout
|
103
|
+
|
94
104
|
if seconds_service_left <= 0 # expire requests that have waited for too long in the queue (as they are assumed to have been dropped by the web server / routing layer at this point)
|
95
105
|
RT._set_state! env, :expired
|
96
106
|
raise RequestExpiryError.new(env), "Request older than #{info.ms(:timeout)}."
|
@@ -103,7 +113,7 @@ module Rack
|
|
103
113
|
# compute actual timeout to be used for this request; if service_past_wait is true, this is just service_timeout. If false (the default), and wait time was determined, we'll use the shortest value between seconds_service_left and service_timeout. See comment above at service_past_wait for justification.
|
104
114
|
info.timeout = service_timeout # nice and simple, when service_past_wait is true, not so much otherwise:
|
105
115
|
info.timeout = seconds_service_left if !service_past_wait && seconds_service_left && seconds_service_left > 0 && seconds_service_left < service_timeout
|
106
|
-
|
116
|
+
info.term = term_on_timeout
|
107
117
|
RT._set_state! env, :ready # we're good to go, but have done nothing yet
|
108
118
|
|
109
119
|
heartbeat_event = nil # init var so it's in scope for following proc
|
@@ -116,7 +126,22 @@ module Rack
|
|
116
126
|
|
117
127
|
timeout = RT::Scheduler::Timeout.new do |app_thread| # creates a timeout instance responsible for timing out the request. the given block runs if timed out
|
118
128
|
register_state_change.call :timed_out
|
119
|
-
|
129
|
+
|
130
|
+
message = "Request "
|
131
|
+
message << "waited #{info.ms(:wait)}, then " if info.wait
|
132
|
+
message << "ran for longer than #{info.ms(:timeout)} "
|
133
|
+
if term_on_timeout
|
134
|
+
Thread.main['RACK_TIMEOUT_COUNT'] += 1
|
135
|
+
|
136
|
+
if Thread.main['RACK_TIMEOUT_COUNT'] >= @term_on_timeout
|
137
|
+
message << ", sending SIGTERM to process #{Process.pid}"
|
138
|
+
Process.kill("SIGTERM", Process.pid)
|
139
|
+
else
|
140
|
+
message << ", #{Thread.main['RACK_TIMEOUT_COUNT']}/#{term_on_timeout} timeouts allowed before SIGTERM for process #{Process.pid}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
app_thread.raise(RequestTimeoutException.new(env), message)
|
120
145
|
end
|
121
146
|
|
122
147
|
response = timeout.timeout(info.timeout) do # perform request with timeout
|
@@ -191,6 +216,5 @@ module Rack
|
|
191
216
|
def self.notify_state_change_observers(env)
|
192
217
|
@state_change_observers.values.each { |observer| observer.call(env) }
|
193
218
|
end
|
194
|
-
|
195
219
|
end
|
196
220
|
end
|
data/lib/rack/timeout/logger.rb
CHANGED
@@ -48,9 +48,9 @@ class Rack::Timeout::StateChangeLoggingObserver
|
|
48
48
|
s << " wait=" << info.ms(:wait) if info.wait
|
49
49
|
s << " timeout=" << info.ms(:timeout) if info.timeout
|
50
50
|
s << " service=" << info.ms(:service) if info.service
|
51
|
+
s << " term_on_timeout=" << info.term.to_s if info.term
|
51
52
|
s << " state=" << info.state.to_s if info.state
|
52
53
|
s
|
53
54
|
end
|
54
55
|
end
|
55
|
-
|
56
56
|
end
|
data/lib/rack-timeout.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
require_relative "rack/timeout/base"
|
2
|
-
require_relative "rack/timeout/rails" if defined?(Rails) && [3,4,5].include?(Rails::VERSION::MAJOR)
|
2
|
+
require_relative "rack/timeout/rails" if defined?(Rails) && [3,4,5,6].include?(Rails::VERSION::MAJOR)
|
data/test/env_settings_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-timeout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Caio Chassot
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -59,9 +59,12 @@ executables: []
|
|
59
59
|
extensions: []
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
|
+
- CHANGELOG.md
|
62
63
|
- Gemfile
|
63
64
|
- MIT-LICENSE
|
65
|
+
- README.md
|
64
66
|
- Rakefile
|
67
|
+
- UPGRADING.md
|
65
68
|
- doc/exceptions.md
|
66
69
|
- doc/logging.md
|
67
70
|
- doc/observers.md
|
@@ -83,11 +86,15 @@ files:
|
|
83
86
|
- test/basic_test.rb
|
84
87
|
- test/env_settings_test.rb
|
85
88
|
- test/test_helper.rb
|
86
|
-
homepage:
|
89
|
+
homepage: https://github.com/sharpstone/rack-timeout
|
87
90
|
licenses:
|
88
91
|
- MIT
|
89
|
-
metadata:
|
90
|
-
|
92
|
+
metadata:
|
93
|
+
bug_tracker_uri: https://github.com/sharpstone/rack-timeout/issues
|
94
|
+
changelog_uri: https://github.com/sharpstone/rack-timeout/blob/v0.6.1/CHANGELOG.md
|
95
|
+
documentation_uri: https://rubydoc.info/gems/rack-timeout/0.6.1/
|
96
|
+
source_code_uri: https://github.com/sharpstone/rack-timeout
|
97
|
+
post_install_message:
|
91
98
|
rdoc_options: []
|
92
99
|
require_paths:
|
93
100
|
- lib
|
@@ -102,9 +109,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
109
|
- !ruby/object:Gem::Version
|
103
110
|
version: '0'
|
104
111
|
requirements: []
|
105
|
-
|
106
|
-
|
107
|
-
signing_key:
|
112
|
+
rubygems_version: 3.3.7
|
113
|
+
signing_key:
|
108
114
|
specification_version: 4
|
109
115
|
summary: Abort requests that are taking too long
|
110
116
|
test_files:
|