unicorn-soft-timeout 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.travis.yml +18 -0
- data/README.md +14 -7
- data/Rakefile +16 -0
- data/lib/unicorn/soft_timeout.rb +2 -3
- data/lib/unicorn/soft_timeout/version.rb +1 -1
- data/test/test_helper.rb +65 -0
- data/test/unicorn_soft_timeout_test.rb +42 -0
- data/unicorn-soft-timeout.gemspec +3 -2
- metadata +39 -28
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1e8e7534c2f58736c14c45d873c8b789f758946f
|
4
|
+
data.tar.gz: 65be8d82059bca947dbfd2129fe077b5c33903ad
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 446843280c827b22e27f8a71c4f885f145c5d8afa4180009a74d8720471315270d861095681fcafc2738be416b68d9b9ee06057f601e7fea54591167c2518df6
|
7
|
+
data.tar.gz: 3f87ef723bb1c9664352596ef13adfabaddca50821b7dd19ff07d202526387a8d28595918ac2033118486a7b2d4e50571ddefe8644aad89a1300cb774eae11e2
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
before_install:
|
4
|
+
- gem install bundler
|
5
|
+
rvm:
|
6
|
+
- 2.3
|
7
|
+
- 2.4
|
8
|
+
- ruby-head
|
9
|
+
- jruby-23mode
|
10
|
+
- jruby-head
|
11
|
+
- rubinius-3
|
12
|
+
matrix:
|
13
|
+
allow_failures:
|
14
|
+
- rvm: ruby-head
|
15
|
+
- rvm: jruby-head
|
16
|
+
- rvm: jruby-23mode
|
17
|
+
- rvm: rubinius-3
|
18
|
+
fast_finish: true
|
data/README.md
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
-
#
|
1
|
+
# unicorn-soft-timeout
|
2
2
|
|
3
|
-
This gem adds support for soft timeout in Unicorn.
|
4
|
-
requests which are taking longer than configured
|
3
|
+
This gem adds support for **soft timeout** in [Unicorn](http://unicorn.bogomips.org/)
|
4
|
+
configurations, by default requests which are taking longer than configured `timeout` are
|
5
5
|
[SIGKILL-ed](http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-timeout).
|
6
|
-
In some cases we need to intercept those requests to display a
|
7
|
-
custom content instead of the error page.
|
8
6
|
|
7
|
+
In some cases we need to intercept requests which will reach `timeout` to display a
|
8
|
+
custom content instead of the error page. This extension will raise `Timeout::Error`
|
9
|
+
when reaching the soft timeout and will restart the worker sending a `SIGQUIT`
|
10
|
+
signal to it.
|
11
|
+
|
12
|
+
- [![Build Status](https://travis-ci.org/vitalie/unicorn-soft-timeout.svg?branch=master)](https://travis-ci.org/vitalie/unicorn-soft-timeout)
|
9
13
|
|
10
14
|
## Installation
|
11
15
|
|
@@ -30,10 +34,13 @@ Edit your config.ru file and load the Unicorn::SoftTimeout middleware:
|
|
30
34
|
|
31
35
|
# Specify your soft timeout (default 12 seconds), it should
|
32
36
|
# be a lower value than **timeout** specified in your unicorn config.
|
33
|
-
# This extension will raise Timeout::Error when reaching
|
34
|
-
# the **soft timeout**.
|
35
37
|
use Unicorn::SoftTimeout, 10
|
36
38
|
|
39
|
+
## Credits
|
40
|
+
|
41
|
+
* [Kazuki Ohta](https://github.com/kzk)
|
42
|
+
* [Pierre Baillet](https://github.com/octplane)
|
43
|
+
|
37
44
|
## Contributing
|
38
45
|
|
39
46
|
1. Fork it
|
data/Rakefile
CHANGED
@@ -1 +1,17 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require "bundler/gem_tasks"
|
3
|
+
require "rake/testtask"
|
4
|
+
require "rdoc/task"
|
5
|
+
|
6
|
+
desc "Default: run tests."
|
7
|
+
task default: :test
|
8
|
+
|
9
|
+
|
10
|
+
desc "Run unit tests."
|
11
|
+
Rake::TestTask.new do |t|
|
12
|
+
t.libs << "test"
|
13
|
+
t.libs << "lib"
|
14
|
+
t.test_files = Dir[ "test/*_test.rb" ]
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
data/lib/unicorn/soft_timeout.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'unicorn/soft_timeout/version'
|
2
2
|
|
3
3
|
module Unicorn
|
4
4
|
module SoftTimeout
|
@@ -18,7 +18,7 @@ module Unicorn
|
|
18
18
|
sleep(@_soft_timeout)
|
19
19
|
logger.warn "#{self}: worker (pid: #{worker_pid}) exceeds soft timeout (limit: #{@_soft_timeout})"
|
20
20
|
Process.kill :QUIT, worker_pid # graceful shutdown
|
21
|
-
current_thread.raise Timeout::Error.new(
|
21
|
+
current_thread.raise Timeout::Error.new('Soft timeout exceeded')
|
22
22
|
end
|
23
23
|
|
24
24
|
super(client) # Unicorn::HttpServer#process_client
|
@@ -26,4 +26,3 @@ module Unicorn
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'unicorn'
|
2
|
+
require 'unicorn/soft_timeout'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/unit'
|
5
|
+
require 'net/http'
|
6
|
+
|
7
|
+
STDIN.sync = STDOUT.sync = STDERR.sync = true
|
8
|
+
|
9
|
+
DEFAULT_TRIES = 100
|
10
|
+
DEFAULT_RES = 0.2
|
11
|
+
|
12
|
+
def redirect_test_io
|
13
|
+
orig_err = STDERR.dup
|
14
|
+
orig_out = STDOUT.dup
|
15
|
+
STDERR.reopen("test_stderr.#{$$}.log", "a")
|
16
|
+
STDOUT.reopen("test_stdout.#{$$}.log", "a")
|
17
|
+
STDERR.sync = STDOUT.sync = true
|
18
|
+
|
19
|
+
at_exit do
|
20
|
+
File.unlink("test_stderr.#{$$}.log") rescue nil
|
21
|
+
File.unlink("test_stdout.#{$$}.log") rescue nil
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
yield
|
26
|
+
ensure
|
27
|
+
STDERR.reopen(orig_err)
|
28
|
+
STDOUT.reopen(orig_out)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def wait_workers_ready(path, nr_workers)
|
33
|
+
tries = DEFAULT_TRIES
|
34
|
+
lines = []
|
35
|
+
while (tries -= 1) > 0
|
36
|
+
begin
|
37
|
+
lines = File.readlines(path).grep(/worker=\d+ ready/)
|
38
|
+
lines.size == nr_workers and return
|
39
|
+
rescue Errno::ENOENT
|
40
|
+
end
|
41
|
+
sleep DEFAULT_RES
|
42
|
+
end
|
43
|
+
raise "#{nr_workers} workers never became ready:" \
|
44
|
+
"\n\t#{lines.join("\n\t")}\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
def hit(uris)
|
48
|
+
results = []
|
49
|
+
uris.each do |u|
|
50
|
+
res = nil
|
51
|
+
|
52
|
+
if u.kind_of? String
|
53
|
+
u = 'http://127.0.0.1:8080/' if u == 'http://0.0.0.0:8080/'
|
54
|
+
res = Net::HTTP.get(URI.parse(u))
|
55
|
+
else
|
56
|
+
url = URI.parse(u[0])
|
57
|
+
res = Net::HTTP.new(url.host, url.port).start {|h| h.request(u[1]) }
|
58
|
+
end
|
59
|
+
|
60
|
+
assert res != nil, "Didn't get a response: #{u}"
|
61
|
+
results << res
|
62
|
+
end
|
63
|
+
|
64
|
+
return results
|
65
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
include Unicorn
|
4
|
+
|
5
|
+
class TestHandler
|
6
|
+
def call(env)
|
7
|
+
while env['rack.input'].read(4096)
|
8
|
+
end
|
9
|
+
sleep 5
|
10
|
+
[200, { 'Content-Type' => 'text/plain' }, ['hello!\n']]
|
11
|
+
rescue Timeout::Error
|
12
|
+
[200, { 'Content-Type' => 'text/plain' }, ['timeout!\n']]
|
13
|
+
rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e
|
14
|
+
$stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n")
|
15
|
+
raise e
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class UnicornSoftTimeoutTest < Minitest::Test
|
20
|
+
def setup
|
21
|
+
@port = 5000
|
22
|
+
@tester = TestHandler.new
|
23
|
+
redirect_test_io do
|
24
|
+
@server = Unicorn::HttpServer.new(@tester, listeners: ["127.0.0.1:#{@port}"])
|
25
|
+
@tester = Unicorn::SoftTimeout.new(@tester, 3)
|
26
|
+
@server.start
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def teardown
|
31
|
+
redirect_test_io do
|
32
|
+
wait_workers_ready("test_stderr.#$$.log", 1)
|
33
|
+
File.truncate("test_stderr.#$$.log", 0)
|
34
|
+
@server.stop(false)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_simple_server
|
39
|
+
results = hit(["http://localhost:#{@port}/test"])
|
40
|
+
assert_equal 'timeout!\n', results[0], "Handler didn't really run"
|
41
|
+
end
|
42
|
+
end
|
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "unicorn", "~>
|
21
|
+
spec.add_dependency "unicorn", "~> 5"
|
22
22
|
|
23
|
-
spec.add_development_dependency "
|
23
|
+
spec.add_development_dependency "minitest"
|
24
24
|
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "bump"
|
25
26
|
end
|
metadata
CHANGED
@@ -1,62 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn-soft-timeout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Vitalie Cherpec
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2017-07-11 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: unicorn
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- - ~>
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
19
|
+
version: '5'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- - ~>
|
24
|
+
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
26
|
+
version: '5'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
28
|
+
name: minitest
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
33
|
+
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
|
-
version: '
|
40
|
+
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rake
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bump
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
52
60
|
- !ruby/object:Gem::Version
|
53
61
|
version: '0'
|
54
62
|
type: :development
|
55
63
|
prerelease: false
|
56
64
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
65
|
requirements:
|
59
|
-
- -
|
66
|
+
- - ">="
|
60
67
|
- !ruby/object:Gem::Version
|
61
68
|
version: '0'
|
62
69
|
description: Graceful handling of requests which are reaching the timeout limit to
|
@@ -67,37 +74,41 @@ executables: []
|
|
67
74
|
extensions: []
|
68
75
|
extra_rdoc_files: []
|
69
76
|
files:
|
70
|
-
- .gitignore
|
77
|
+
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
71
79
|
- Gemfile
|
72
80
|
- LICENSE.txt
|
73
81
|
- README.md
|
74
82
|
- Rakefile
|
75
83
|
- lib/unicorn/soft_timeout.rb
|
76
84
|
- lib/unicorn/soft_timeout/version.rb
|
85
|
+
- test/test_helper.rb
|
86
|
+
- test/unicorn_soft_timeout_test.rb
|
77
87
|
- unicorn-soft-timeout.gemspec
|
78
88
|
homepage: https://github.com/vitalie/unicorn-soft-timeout
|
79
89
|
licenses:
|
80
90
|
- MIT
|
91
|
+
metadata: {}
|
81
92
|
post_install_message:
|
82
93
|
rdoc_options: []
|
83
94
|
require_paths:
|
84
95
|
- lib
|
85
96
|
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
-
none: false
|
87
97
|
requirements:
|
88
|
-
- -
|
98
|
+
- - ">="
|
89
99
|
- !ruby/object:Gem::Version
|
90
100
|
version: '0'
|
91
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
-
none: false
|
93
102
|
requirements:
|
94
|
-
- -
|
103
|
+
- - ">="
|
95
104
|
- !ruby/object:Gem::Version
|
96
105
|
version: '0'
|
97
106
|
requirements: []
|
98
107
|
rubyforge_project:
|
99
|
-
rubygems_version:
|
108
|
+
rubygems_version: 2.4.5.1
|
100
109
|
signing_key:
|
101
|
-
specification_version:
|
110
|
+
specification_version: 4
|
102
111
|
summary: Unicorn soft timeout
|
103
|
-
test_files:
|
112
|
+
test_files:
|
113
|
+
- test/test_helper.rb
|
114
|
+
- test/unicorn_soft_timeout_test.rb
|