unicorn_wrangler 0.3.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/unicorn_wrangler.rb +63 -6
- data/lib/unicorn_wrangler/version.rb +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f027def0eaa75e21daf9efc322a1be988248b5cd117d12eb2ddfadc10494e4d7
|
4
|
+
data.tar.gz: a765f7d06d90f8ef448ae1ac87eeb0fc01dddac587dce8d1c69f00acbf6c0164
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91787cc20b2bafa1334af58dc5bc29985c3f7d165451cfd36fb45e5d63069ce36525f4d63f2a27fb09fdc29215aab0dcb62ad8cacf0e1cae067b5a20e4740188
|
7
|
+
data.tar.gz: 7b6ca2549873589cd9c837affafcc9a9fc07878476c7554f785b20c30b132a1d9e1f9ad12c24af9c16b27d40dae70cd47a7581f28642ea643e055bf9ff95d2ca
|
data/lib/unicorn_wrangler.rb
CHANGED
@@ -9,6 +9,7 @@ module UnicornWrangler
|
|
9
9
|
|
10
10
|
class << self
|
11
11
|
attr_reader :handlers
|
12
|
+
attr_accessor :sending_myself_term
|
12
13
|
|
13
14
|
# called from unicorn config (usually config/unicorn.rb)
|
14
15
|
# high level interface to keep setup consistent / simple
|
@@ -17,6 +18,7 @@ module UnicornWrangler
|
|
17
18
|
kill_after_requests: 10000,
|
18
19
|
gc_after_request_time: 10,
|
19
20
|
kill_on_too_much_memory: {},
|
21
|
+
map_term_to_quit: false,
|
20
22
|
logger:,
|
21
23
|
stats: nil # provide a statsd client with your apps namespace to collect stats
|
22
24
|
)
|
@@ -25,9 +27,40 @@ module UnicornWrangler
|
|
25
27
|
@handlers << RequestKiller.new(logger, stats, kill_after_requests) if kill_after_requests
|
26
28
|
@handlers << OutOfMemoryKiller.new(logger, stats, kill_on_too_much_memory) if kill_on_too_much_memory
|
27
29
|
@handlers << OutOfBandGC.new(logger, stats, gc_after_request_time) if gc_after_request_time
|
30
|
+
|
31
|
+
@hooks = {}
|
32
|
+
if map_term_to_quit
|
33
|
+
# - on heroku & kubernetes all processes get TERM, so we need to trap in master and worker
|
34
|
+
# - trapping has to be done in the before_fork since unicorn sets up it's own traps on start
|
35
|
+
# - we cannot write to logger inside of a trap, so need to spawn a new Thread
|
36
|
+
# - manual test: add a slow route + rails s + curl + pkill -TERM -f 'unicorn master' - request finished?
|
37
|
+
@hooks[:before_fork] = -> do
|
38
|
+
Signal.trap :TERM do
|
39
|
+
Thread.new { logger.info 'master intercepting TERM and sending myself QUIT instead' }
|
40
|
+
Process.kill :QUIT, Process.pid
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@hooks[:after_fork] = ->(*) do
|
45
|
+
# Signal.trap returns the trap that unicorn set, which is an exit!(0) and calls that when sending myself term
|
46
|
+
previous_trap = Signal.trap :TERM do
|
47
|
+
if sending_myself_term
|
48
|
+
previous_trap.call
|
49
|
+
else
|
50
|
+
Thread.new { logger.info 'worker intercepting TERM and doing nothing. Wait for master to send QUIT' }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
28
56
|
Unicorn::HttpServer.prepend UnicornExtension
|
29
57
|
end
|
30
58
|
|
59
|
+
def kill_worker
|
60
|
+
self.sending_myself_term = true # no need to clean up since we are dead after
|
61
|
+
Process.kill(:TERM, Process.pid)
|
62
|
+
end
|
63
|
+
|
31
64
|
# called from the unicorn server after each request
|
32
65
|
def perform_request
|
33
66
|
returned = nil
|
@@ -39,12 +72,37 @@ module UnicornWrangler
|
|
39
72
|
ensure
|
40
73
|
@handlers.each { |handler| handler.call(@requests, @request_time) }
|
41
74
|
end
|
75
|
+
|
76
|
+
def perform_hook(name)
|
77
|
+
if hook = @hooks[name]
|
78
|
+
hook.call
|
79
|
+
end
|
80
|
+
end
|
42
81
|
end
|
43
82
|
|
44
83
|
module UnicornExtension
|
84
|
+
# call our hook and the users hook since only a single hook can be configured at a time
|
85
|
+
# we need to call super so the @<hook> variables get set and unset properly in after_fork to not leak memory
|
86
|
+
[:after_fork, :before_fork].each do |hook|
|
87
|
+
define_method("#{hook}=") do |value|
|
88
|
+
super(->(*args) do
|
89
|
+
UnicornWrangler.perform_hook(hook)
|
90
|
+
value.call(*args)
|
91
|
+
end)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
45
95
|
def process_client(*)
|
46
96
|
UnicornWrangler.perform_request { super }
|
47
97
|
end
|
98
|
+
|
99
|
+
# run GC after we finished loading out app so forks inherit a clean GC environment
|
100
|
+
def build_app!
|
101
|
+
super
|
102
|
+
ensure
|
103
|
+
GC.start
|
104
|
+
GC.disable
|
105
|
+
end
|
48
106
|
end
|
49
107
|
|
50
108
|
class Killer
|
@@ -67,9 +125,9 @@ module UnicornWrangler
|
|
67
125
|
@stats.histogram("#{STATS_NAMESPACE}.kill.total_request_time", request_time)
|
68
126
|
end
|
69
127
|
|
70
|
-
report_status "Killing", reason, memory, requests, request_time
|
128
|
+
report_status "Killing", reason, memory, requests, request_time, :warn
|
71
129
|
|
72
|
-
|
130
|
+
UnicornWrangler.kill_worker
|
73
131
|
end
|
74
132
|
|
75
133
|
# expensive, do not run on every request
|
@@ -77,8 +135,8 @@ module UnicornWrangler
|
|
77
135
|
`ps -o rss= -p #{Process.pid}`.to_i / 1024
|
78
136
|
end
|
79
137
|
|
80
|
-
def report_status(status, reason, memory, requests, request_time)
|
81
|
-
@logger.
|
138
|
+
def report_status(status, reason, memory, requests, request_time, log_level = :debug)
|
139
|
+
@logger.send log_level, "#{status} unicorn worker ##{Process.pid} for #{reason}. Requests: #{requests}, Time: #{request_time}, Memory: #{memory}MB"
|
82
140
|
end
|
83
141
|
end
|
84
142
|
|
@@ -124,7 +182,6 @@ module UnicornWrangler
|
|
124
182
|
@logger = logger
|
125
183
|
@stats = stats
|
126
184
|
@max_request_time = max_request_time
|
127
|
-
GC.disable
|
128
185
|
@logger.info "Garbage collecting after #{@max_request_time}s of request processing time"
|
129
186
|
@gc_ran_at = 0
|
130
187
|
end
|
@@ -145,7 +202,7 @@ module UnicornWrangler
|
|
145
202
|
@stats.increment("#{STATS_NAMESPACE}.oobgc.runs")
|
146
203
|
@stats.timing("#{STATS_NAMESPACE}.oobgc.time", time)
|
147
204
|
end
|
148
|
-
@logger.
|
205
|
+
@logger.debug "Garbage collecting: took #{time}ms"
|
149
206
|
true
|
150
207
|
end
|
151
208
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn_wrangler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: michael@grosser.it
|
@@ -38,8 +38,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: '0'
|
40
40
|
requirements: []
|
41
|
-
|
42
|
-
rubygems_version: 2.5.1
|
41
|
+
rubygems_version: 3.1.4
|
43
42
|
signing_key:
|
44
43
|
specification_version: 4
|
45
44
|
summary: 'Unicorn: out of band GC / restart on max memory bloat / restart after X
|