honeybadger 2.7.2 → 3.0.0.beta1
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 +4 -4
- data/CHANGELOG.md +58 -3
- data/README.md +8 -15
- data/lib/honeybadger.rb +9 -232
- data/lib/honeybadger/agent.rb +292 -134
- data/lib/honeybadger/backend.rb +6 -6
- data/lib/honeybadger/backend/base.rb +11 -0
- data/lib/honeybadger/backend/server.rb +2 -14
- data/lib/honeybadger/cli.rb +0 -2
- data/lib/honeybadger/cli/deploy.rb +42 -0
- data/lib/honeybadger/cli/exec.rb +138 -0
- data/lib/honeybadger/cli/heroku.rb +1 -22
- data/lib/honeybadger/cli/install.rb +74 -0
- data/lib/honeybadger/cli/main.rb +138 -153
- data/lib/honeybadger/cli/notify.rb +66 -0
- data/lib/honeybadger/cli/test.rb +266 -0
- data/lib/honeybadger/config.rb +178 -162
- data/lib/honeybadger/config/defaults.rb +5 -5
- data/lib/honeybadger/config/env.rb +8 -6
- data/lib/honeybadger/config/ruby.rb +100 -0
- data/lib/honeybadger/config/yaml.rb +18 -19
- data/lib/honeybadger/const.rb +3 -16
- data/lib/honeybadger/context_manager.rb +50 -0
- data/lib/honeybadger/init/rails.rb +9 -21
- data/lib/honeybadger/init/rake.rb +2 -0
- data/lib/honeybadger/init/ruby.rb +9 -0
- data/lib/honeybadger/init/sinatra.rb +13 -6
- data/lib/honeybadger/notice.rb +29 -14
- data/lib/honeybadger/plugins/delayed_job/plugin.rb +4 -5
- data/lib/honeybadger/plugins/passenger.rb +1 -2
- data/lib/honeybadger/plugins/rails.rb +0 -28
- data/lib/honeybadger/plugins/resque.rb +2 -5
- data/lib/honeybadger/plugins/shoryuken.rb +2 -2
- data/lib/honeybadger/plugins/sidekiq.rb +2 -2
- data/lib/honeybadger/plugins/sucker_punch.rb +1 -0
- data/lib/honeybadger/plugins/thor.rb +2 -2
- data/lib/honeybadger/plugins/warden.rb +1 -0
- data/lib/honeybadger/rack/error_notifier.rb +11 -9
- data/lib/honeybadger/rack/user_feedback.rb +6 -4
- data/lib/honeybadger/rack/user_informer.rb +6 -4
- data/lib/honeybadger/ruby.rb +2 -0
- data/lib/honeybadger/singleton.rb +26 -0
- data/lib/honeybadger/util/http.rb +12 -0
- data/lib/honeybadger/util/request_hash.rb +71 -0
- data/lib/honeybadger/util/sanitizer.rb +101 -64
- data/lib/honeybadger/version.rb +1 -1
- data/lib/honeybadger/worker.rb +246 -0
- metadata +17 -13
- data/lib/honeybadger/agent/batch.rb +0 -50
- data/lib/honeybadger/agent/null_worker.rb +0 -26
- data/lib/honeybadger/agent/worker.rb +0 -243
- data/lib/honeybadger/cli/helpers.rb +0 -160
- data/lib/honeybadger/config/callbacks.rb +0 -70
- data/lib/honeybadger/plugins/unicorn.rb +0 -27
- data/lib/honeybadger/rack/metrics_reporter.rb +0 -16
- data/lib/honeybadger/rack/request_hash.rb +0 -55
data/lib/honeybadger/version.rb
CHANGED
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
require 'honeybadger/logging'
|
5
|
+
|
6
|
+
module Honeybadger
|
7
|
+
# Internal: A concurrent queue to notify the backend.
|
8
|
+
class Worker
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
include Honeybadger::Logging::Helper
|
12
|
+
|
13
|
+
# Internal: Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
14
|
+
class Thread < ::Thread; end
|
15
|
+
|
16
|
+
# Internal: A queue which enforces a maximum size.
|
17
|
+
class Queue < ::Queue
|
18
|
+
attr_reader :max_size
|
19
|
+
|
20
|
+
def initialize(max_size)
|
21
|
+
@max_size = max_size
|
22
|
+
super()
|
23
|
+
end
|
24
|
+
|
25
|
+
def push(msg)
|
26
|
+
super unless size == max_size
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
SHUTDOWN = :__hb_worker_shutdown!
|
31
|
+
|
32
|
+
def initialize(config)
|
33
|
+
@config = config
|
34
|
+
@throttles = []
|
35
|
+
@mutex = Mutex.new
|
36
|
+
@marker = ConditionVariable.new
|
37
|
+
@queue = Queue.new(config.max_queue_size)
|
38
|
+
@shutdown = false
|
39
|
+
@start_at = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def push(msg)
|
43
|
+
return false unless start
|
44
|
+
queue.push(msg)
|
45
|
+
end
|
46
|
+
|
47
|
+
def send_now(msg)
|
48
|
+
handle_response(msg, notify_backend(msg))
|
49
|
+
end
|
50
|
+
|
51
|
+
# Internal: Shutdown the worker after sending remaining data.
|
52
|
+
#
|
53
|
+
# Returns true.
|
54
|
+
def shutdown
|
55
|
+
d { 'shutting down worker' }
|
56
|
+
|
57
|
+
mutex.synchronize do
|
58
|
+
@shutdown = true
|
59
|
+
@pid = nil
|
60
|
+
queue.push(SHUTDOWN)
|
61
|
+
end
|
62
|
+
|
63
|
+
return true unless thread
|
64
|
+
|
65
|
+
r = true
|
66
|
+
unless Thread.current.eql?(thread)
|
67
|
+
begin
|
68
|
+
r = !!thread.join
|
69
|
+
ensure
|
70
|
+
shutdown! unless r
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
r
|
75
|
+
end
|
76
|
+
|
77
|
+
def shutdown!
|
78
|
+
mutex.synchronize do
|
79
|
+
@shutdown = true
|
80
|
+
@pid = nil
|
81
|
+
queue.clear
|
82
|
+
end
|
83
|
+
|
84
|
+
d { 'killing worker thread' }
|
85
|
+
|
86
|
+
if thread
|
87
|
+
Thread.kill(thread)
|
88
|
+
thread.join # Allow ensure blocks to execute.
|
89
|
+
end
|
90
|
+
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
# Internal: Blocks until queue is processed up to this point in time.
|
95
|
+
#
|
96
|
+
# Returns nothing.
|
97
|
+
def flush
|
98
|
+
mutex.synchronize do
|
99
|
+
if thread && thread.alive?
|
100
|
+
queue.push(marker)
|
101
|
+
marker.wait(mutex)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def start
|
107
|
+
return false unless can_start?
|
108
|
+
|
109
|
+
mutex.synchronize do
|
110
|
+
@shutdown = false
|
111
|
+
@start_at = nil
|
112
|
+
|
113
|
+
return true if thread && thread.alive?
|
114
|
+
|
115
|
+
@pid = Process.pid
|
116
|
+
@thread = Thread.new { run }
|
117
|
+
end
|
118
|
+
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
attr_reader :config, :queue, :pid, :mutex, :marker,
|
125
|
+
:thread, :throttles
|
126
|
+
|
127
|
+
def_delegator :config, :backend
|
128
|
+
|
129
|
+
def can_start?
|
130
|
+
mutex.synchronize do
|
131
|
+
return true unless @shutdown
|
132
|
+
return false unless @start_at
|
133
|
+
Time.now.to_i >= @start_at
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def suspend(interval)
|
138
|
+
mutex.synchronize { @start_at = Time.now.to_i + interval }
|
139
|
+
|
140
|
+
# Must be performed last since this may kill the current thread.
|
141
|
+
shutdown!
|
142
|
+
end
|
143
|
+
|
144
|
+
def run
|
145
|
+
begin
|
146
|
+
d { 'worker started' }
|
147
|
+
loop do
|
148
|
+
case msg = queue.pop
|
149
|
+
when SHUTDOWN then break
|
150
|
+
when ConditionVariable then signal_marker(msg)
|
151
|
+
else work(msg)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
ensure
|
155
|
+
d { 'stopping worker' }
|
156
|
+
end
|
157
|
+
rescue Exception => e
|
158
|
+
error {
|
159
|
+
msg = "error in worker thread (shutting down) class=%s message=%s\n\t%s"
|
160
|
+
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
161
|
+
}
|
162
|
+
ensure
|
163
|
+
release_marker
|
164
|
+
end
|
165
|
+
|
166
|
+
def work(msg)
|
167
|
+
send_now(msg)
|
168
|
+
sleep(throttle_interval)
|
169
|
+
rescue StandardError => e
|
170
|
+
error {
|
171
|
+
msg = "Error in worker thread class=%s message=%s\n\t%s"
|
172
|
+
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
173
|
+
}
|
174
|
+
sleep(1)
|
175
|
+
end
|
176
|
+
|
177
|
+
def throttle_interval
|
178
|
+
return 0 unless throttles[0]
|
179
|
+
mutex.synchronize do
|
180
|
+
throttles.reduce(1) {|a,e| a*e }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def notify_backend(payload)
|
185
|
+
debug { sprintf('worker notifying backend id=%s', payload.id) }
|
186
|
+
backend.notify(:notices, payload)
|
187
|
+
end
|
188
|
+
|
189
|
+
def add_throttle(t)
|
190
|
+
mutex.synchronize do
|
191
|
+
throttles.push(t)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def del_throttle
|
196
|
+
mutex.synchronize do
|
197
|
+
throttles.shift
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def handle_response(msg, response)
|
202
|
+
debug { sprintf('worker response code=%s message=%s', response.code, response.message.to_s.dump) }
|
203
|
+
|
204
|
+
case response.code
|
205
|
+
when 429, 503
|
206
|
+
warn { sprintf('Error report failed: project is sending too many errors. id=%s code=%s throttle=1.25 interval=%s', msg.id, throttle_interval, response.code) }
|
207
|
+
add_throttle(1.25)
|
208
|
+
when 402
|
209
|
+
warn { sprintf('Error report failed: payment is required. id=%s code=%s', msg.id, response.code) }
|
210
|
+
suspend(3600)
|
211
|
+
when 403
|
212
|
+
warn { sprintf('Error report failed: API key is invalid. id=%s code=%s', msg.id, response.code) }
|
213
|
+
suspend(3600)
|
214
|
+
when 201
|
215
|
+
if throttle = del_throttle
|
216
|
+
info { sprintf('Success ⚡ https://app.honeybadger.io/notice/%s id=%s code=%s throttle=%s interval=%s', msg.id, msg.id, response.code, throttle_interval, response.code) }
|
217
|
+
else
|
218
|
+
info { sprintf('Success ⚡ https://app.honeybadger.io/notice/%s id=%s code=%s', msg.id, msg.id, response.code) }
|
219
|
+
end
|
220
|
+
when :error
|
221
|
+
warn { sprintf('Error report failed: an unknown error occurred. code=%s error=%s', response.code, response.message.to_s.dump) }
|
222
|
+
else
|
223
|
+
warn { sprintf('Error report failed: unknown response from server. code=%s', response.code) }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Internal: Release the marker. Important to perform during cleanup when
|
228
|
+
# shutting down, otherwise it could end up waiting indefinitely.
|
229
|
+
#
|
230
|
+
# Returns nothing.
|
231
|
+
def release_marker
|
232
|
+
signal_marker(marker)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Internal: Signal a marker.
|
236
|
+
#
|
237
|
+
# marker - The ConditionVariable marker to signal.
|
238
|
+
#
|
239
|
+
# Returns nothing.
|
240
|
+
def signal_marker(marker)
|
241
|
+
mutex.synchronize do
|
242
|
+
marker.signal
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: honeybadger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Honeybadger Industries LLC
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Make managing application errors a more pleasant experience.
|
14
14
|
email:
|
@@ -25,9 +25,6 @@ files:
|
|
25
25
|
- bin/honeybadger
|
26
26
|
- lib/honeybadger.rb
|
27
27
|
- lib/honeybadger/agent.rb
|
28
|
-
- lib/honeybadger/agent/batch.rb
|
29
|
-
- lib/honeybadger/agent/null_worker.rb
|
30
|
-
- lib/honeybadger/agent/worker.rb
|
31
28
|
- lib/honeybadger/backend.rb
|
32
29
|
- lib/honeybadger/backend/base.rb
|
33
30
|
- lib/honeybadger/backend/debug.rb
|
@@ -36,17 +33,23 @@ files:
|
|
36
33
|
- lib/honeybadger/backend/test.rb
|
37
34
|
- lib/honeybadger/backtrace.rb
|
38
35
|
- lib/honeybadger/cli.rb
|
39
|
-
- lib/honeybadger/cli/
|
36
|
+
- lib/honeybadger/cli/deploy.rb
|
37
|
+
- lib/honeybadger/cli/exec.rb
|
40
38
|
- lib/honeybadger/cli/heroku.rb
|
39
|
+
- lib/honeybadger/cli/install.rb
|
41
40
|
- lib/honeybadger/cli/main.rb
|
41
|
+
- lib/honeybadger/cli/notify.rb
|
42
|
+
- lib/honeybadger/cli/test.rb
|
42
43
|
- lib/honeybadger/config.rb
|
43
|
-
- lib/honeybadger/config/callbacks.rb
|
44
44
|
- lib/honeybadger/config/defaults.rb
|
45
45
|
- lib/honeybadger/config/env.rb
|
46
|
+
- lib/honeybadger/config/ruby.rb
|
46
47
|
- lib/honeybadger/config/yaml.rb
|
47
48
|
- lib/honeybadger/const.rb
|
49
|
+
- lib/honeybadger/context_manager.rb
|
48
50
|
- lib/honeybadger/init/rails.rb
|
49
51
|
- lib/honeybadger/init/rake.rb
|
52
|
+
- lib/honeybadger/init/ruby.rb
|
50
53
|
- lib/honeybadger/init/sinatra.rb
|
51
54
|
- lib/honeybadger/logging.rb
|
52
55
|
- lib/honeybadger/notice.rb
|
@@ -61,20 +64,21 @@ files:
|
|
61
64
|
- lib/honeybadger/plugins/sidekiq.rb
|
62
65
|
- lib/honeybadger/plugins/sucker_punch.rb
|
63
66
|
- lib/honeybadger/plugins/thor.rb
|
64
|
-
- lib/honeybadger/plugins/unicorn.rb
|
65
67
|
- lib/honeybadger/plugins/warden.rb
|
66
68
|
- lib/honeybadger/rack/error_notifier.rb
|
67
|
-
- lib/honeybadger/rack/metrics_reporter.rb
|
68
|
-
- lib/honeybadger/rack/request_hash.rb
|
69
69
|
- lib/honeybadger/rack/user_feedback.rb
|
70
70
|
- lib/honeybadger/rack/user_informer.rb
|
71
|
+
- lib/honeybadger/ruby.rb
|
72
|
+
- lib/honeybadger/singleton.rb
|
71
73
|
- lib/honeybadger/tasks.rb
|
72
74
|
- lib/honeybadger/templates/feedback_form.erb
|
73
75
|
- lib/honeybadger/util/http.rb
|
76
|
+
- lib/honeybadger/util/request_hash.rb
|
74
77
|
- lib/honeybadger/util/request_payload.rb
|
75
78
|
- lib/honeybadger/util/sanitizer.rb
|
76
79
|
- lib/honeybadger/util/stats.rb
|
77
80
|
- lib/honeybadger/version.rb
|
81
|
+
- lib/honeybadger/worker.rb
|
78
82
|
- resources/ca-bundle.crt
|
79
83
|
- vendor/capistrano-honeybadger/lib/capistrano/honeybadger.rb
|
80
84
|
- vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap
|
@@ -128,12 +132,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
132
|
requirements:
|
129
133
|
- - ">="
|
130
134
|
- !ruby/object:Gem::Version
|
131
|
-
version: 1.
|
135
|
+
version: 2.1.0
|
132
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
137
|
requirements:
|
134
|
-
- - "
|
138
|
+
- - ">"
|
135
139
|
- !ruby/object:Gem::Version
|
136
|
-
version:
|
140
|
+
version: 1.3.1
|
137
141
|
requirements: []
|
138
142
|
rubyforge_project:
|
139
143
|
rubygems_version: 2.5.1
|
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
|
3
|
-
module Honeybadger
|
4
|
-
class Agent
|
5
|
-
class Batch
|
6
|
-
def initialize(config, name, opts = {})
|
7
|
-
@id = SecureRandom.uuid
|
8
|
-
@config = config
|
9
|
-
@name = name
|
10
|
-
@max = opts.fetch(:max, 100)
|
11
|
-
@interval = opts.fetch(:interval, 60)
|
12
|
-
@future = opts.fetch(:now, now()) + interval
|
13
|
-
@values = opts.fetch(:collection, Array.new)
|
14
|
-
@mutex = Mutex.new
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_reader :id
|
18
|
-
|
19
|
-
def push(val)
|
20
|
-
mutex.synchronize { values.push(val) }
|
21
|
-
end
|
22
|
-
|
23
|
-
def empty?
|
24
|
-
mutex.synchronize { values.empty? }
|
25
|
-
end
|
26
|
-
|
27
|
-
def size
|
28
|
-
mutex.synchronize { values.size }
|
29
|
-
end
|
30
|
-
|
31
|
-
def flush?
|
32
|
-
size >= max || now >= future
|
33
|
-
end
|
34
|
-
|
35
|
-
def as_json(*args)
|
36
|
-
mutex.synchronize do
|
37
|
-
{ name => values.map(&:to_h), :environment => config[:env], :hostname => config[:hostname] }
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
attr_reader :config, :name, :max, :interval, :values, :future, :mutex
|
44
|
-
|
45
|
-
def now
|
46
|
-
Time.now.to_i
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module Honeybadger
|
2
|
-
class Agent
|
3
|
-
# Internal: A default worker which does nothing.
|
4
|
-
class NullWorker
|
5
|
-
def push(obj)
|
6
|
-
true
|
7
|
-
end
|
8
|
-
|
9
|
-
def shutdown
|
10
|
-
true
|
11
|
-
end
|
12
|
-
|
13
|
-
def shutdown!
|
14
|
-
true
|
15
|
-
end
|
16
|
-
|
17
|
-
def flush
|
18
|
-
true
|
19
|
-
end
|
20
|
-
|
21
|
-
def start
|
22
|
-
true
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,243 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'net/http'
|
3
|
-
|
4
|
-
require 'honeybadger/logging'
|
5
|
-
require 'honeybadger/agent/null_worker'
|
6
|
-
|
7
|
-
module Honeybadger
|
8
|
-
class Agent
|
9
|
-
# Internal: A concurrent queue to notify the backend.
|
10
|
-
class Worker
|
11
|
-
extend Forwardable
|
12
|
-
|
13
|
-
include Honeybadger::Logging::Helper
|
14
|
-
|
15
|
-
# Internal: Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
16
|
-
class Thread < ::Thread; end
|
17
|
-
|
18
|
-
# Internal: A queue which enforces a maximum size.
|
19
|
-
class Queue < ::Queue
|
20
|
-
attr_reader :max_size
|
21
|
-
|
22
|
-
def initialize(max_size)
|
23
|
-
@max_size = max_size
|
24
|
-
super()
|
25
|
-
end
|
26
|
-
|
27
|
-
def push(obj)
|
28
|
-
super unless size == max_size
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
SHUTDOWN = :__hb_worker_shutdown!
|
33
|
-
|
34
|
-
def initialize(config, feature)
|
35
|
-
@config = config
|
36
|
-
@feature = feature
|
37
|
-
@backend = config.backend
|
38
|
-
@throttles = []
|
39
|
-
@mutex = Mutex.new
|
40
|
-
@marker = ConditionVariable.new
|
41
|
-
@queue = Queue.new(config.max_queue_size)
|
42
|
-
@shutdown = false
|
43
|
-
@start_at = nil
|
44
|
-
end
|
45
|
-
|
46
|
-
def push(obj)
|
47
|
-
return false unless start
|
48
|
-
queue.push(obj)
|
49
|
-
end
|
50
|
-
|
51
|
-
# Internal: Shutdown the worker after sending remaining data.
|
52
|
-
#
|
53
|
-
# Returns true.
|
54
|
-
def shutdown
|
55
|
-
d { sprintf('shutting down worker feature=%s', feature) }
|
56
|
-
|
57
|
-
mutex.synchronize do
|
58
|
-
@shutdown = true
|
59
|
-
@pid = nil
|
60
|
-
queue.push(SHUTDOWN)
|
61
|
-
end
|
62
|
-
|
63
|
-
return true unless thread
|
64
|
-
|
65
|
-
r = true
|
66
|
-
unless Thread.current.eql?(thread)
|
67
|
-
begin
|
68
|
-
r = !!thread.join
|
69
|
-
ensure
|
70
|
-
shutdown! unless r
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
r
|
75
|
-
end
|
76
|
-
|
77
|
-
def shutdown!
|
78
|
-
mutex.synchronize do
|
79
|
-
@shutdown = true
|
80
|
-
@pid = nil
|
81
|
-
queue.clear
|
82
|
-
end
|
83
|
-
|
84
|
-
d { sprintf('killing worker thread feature=%s', feature) }
|
85
|
-
|
86
|
-
if thread
|
87
|
-
Thread.kill(thread)
|
88
|
-
thread.join # Allow ensure blocks to execute.
|
89
|
-
end
|
90
|
-
|
91
|
-
true
|
92
|
-
end
|
93
|
-
|
94
|
-
# Internal: Blocks until queue is processed up to this point in time.
|
95
|
-
#
|
96
|
-
# Returns nothing.
|
97
|
-
def flush
|
98
|
-
mutex.synchronize do
|
99
|
-
if thread && thread.alive?
|
100
|
-
queue.push(marker)
|
101
|
-
marker.wait(mutex)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def start
|
107
|
-
return false unless can_start?
|
108
|
-
|
109
|
-
mutex.synchronize do
|
110
|
-
@shutdown = false
|
111
|
-
@start_at = nil
|
112
|
-
|
113
|
-
return true if thread && thread.alive?
|
114
|
-
|
115
|
-
@pid = Process.pid
|
116
|
-
@thread = Thread.new { run }
|
117
|
-
end
|
118
|
-
|
119
|
-
true
|
120
|
-
end
|
121
|
-
|
122
|
-
private
|
123
|
-
|
124
|
-
attr_reader :config, :backend, :feature, :queue, :pid, :mutex, :marker,
|
125
|
-
:thread, :throttles
|
126
|
-
|
127
|
-
def can_start?
|
128
|
-
mutex.synchronize do
|
129
|
-
return true unless @shutdown
|
130
|
-
return false unless @start_at
|
131
|
-
Time.now.to_i >= @start_at
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def suspend(interval)
|
136
|
-
mutex.synchronize { @start_at = Time.now.to_i + interval }
|
137
|
-
|
138
|
-
# Must be performed last since this may kill the current thread.
|
139
|
-
shutdown!
|
140
|
-
end
|
141
|
-
|
142
|
-
def run
|
143
|
-
begin
|
144
|
-
d { sprintf('worker started feature=%s', feature) }
|
145
|
-
loop do
|
146
|
-
case msg = queue.pop
|
147
|
-
when SHUTDOWN then break
|
148
|
-
when ConditionVariable then signal_marker(msg)
|
149
|
-
else process(msg)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
ensure
|
153
|
-
d { sprintf('stopping worker feature=%s', feature) }
|
154
|
-
end
|
155
|
-
rescue Exception => e
|
156
|
-
error {
|
157
|
-
msg = "error in worker thread (shutting down) feature=%s class=%s message=%s\n\t%s"
|
158
|
-
sprintf(msg, feature, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
159
|
-
}
|
160
|
-
ensure
|
161
|
-
release_marker
|
162
|
-
end
|
163
|
-
|
164
|
-
def process(msg)
|
165
|
-
handle_response(notify_backend(msg))
|
166
|
-
sleep(throttle_interval)
|
167
|
-
rescue StandardError => e
|
168
|
-
error {
|
169
|
-
msg = "error in worker thread feature=%s class=%s message=%s\n\t%s"
|
170
|
-
sprintf(msg, feature, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
171
|
-
}
|
172
|
-
sleep(1)
|
173
|
-
end
|
174
|
-
|
175
|
-
def throttle_interval
|
176
|
-
return 0 unless throttles[0]
|
177
|
-
mutex.synchronize do
|
178
|
-
throttles.reduce(1) {|a,e| a*e }
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def notify_backend(payload)
|
183
|
-
debug { sprintf('worker notifying backend feature=%s id=%s', feature, payload.id) }
|
184
|
-
backend.notify(feature, payload)
|
185
|
-
end
|
186
|
-
|
187
|
-
def add_throttle(t)
|
188
|
-
mutex.synchronize do
|
189
|
-
throttles.push(t)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
def del_throttle
|
194
|
-
mutex.synchronize do
|
195
|
-
throttles.shift
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
def handle_response(response)
|
200
|
-
debug { sprintf('worker response feature=%s code=%s message=%s', feature, response.code, response.message.to_s.dump) }
|
201
|
-
|
202
|
-
case response.code
|
203
|
-
when 429, 503
|
204
|
-
add_throttle(1.25)
|
205
|
-
debug { sprintf('worker applying throttle=1.25 interval=%s feature=%s code=%s', throttle_interval, feature, response.code) }
|
206
|
-
when 402
|
207
|
-
warn { sprintf('data will not be reported (payment required) feature=%s code=%s', feature, response.code) }
|
208
|
-
suspend(3600)
|
209
|
-
when 403
|
210
|
-
warn { sprintf('data will not be reported feature=%s code=%s error=%s', feature, response.code, response.error.to_s.dump) }
|
211
|
-
suspend(3600)
|
212
|
-
when 201
|
213
|
-
if throttle = del_throttle
|
214
|
-
debug { sprintf('worker removing throttle=%s interval=%s feature=%s code=%s', throttle, throttle_interval, feature, response.code) }
|
215
|
-
end
|
216
|
-
when :error
|
217
|
-
# Error logged by backend.
|
218
|
-
else
|
219
|
-
warn { sprintf('worker unknown response feature=%s code=%s', feature, response.code) }
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
# Internal: Release the marker. Important to perform during cleanup when
|
224
|
-
# shutting down, otherwise it could end up waiting indefinitely.
|
225
|
-
#
|
226
|
-
# Returns nothing.
|
227
|
-
def release_marker
|
228
|
-
signal_marker(marker)
|
229
|
-
end
|
230
|
-
|
231
|
-
# Internal: Signal a marker.
|
232
|
-
#
|
233
|
-
# marker - The ConditionVariable marker to signal.
|
234
|
-
#
|
235
|
-
# Returns nothing.
|
236
|
-
def signal_marker(marker)
|
237
|
-
mutex.synchronize do
|
238
|
-
marker.signal
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|