kthxbye 1.2.1 → 1.3.0
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.
- data/lib/kthxbye.rb +3 -1
- data/lib/kthxbye/failure.rb +4 -0
- data/lib/kthxbye/job.rb +12 -7
- data/lib/kthxbye/stats.rb +39 -0
- data/lib/kthxbye/version.rb +1 -1
- data/lib/kthxbye/web_interface.rb +10 -0
- data/lib/kthxbye/web_interface/public/style.css +10 -1
- data/lib/kthxbye/web_interface/views/failed.haml +1 -1
- data/lib/kthxbye/web_interface/views/queue.haml +14 -0
- data/lib/kthxbye/web_interface/views/queues.haml +26 -27
- data/lib/kthxbye/web_interface/views/stats.haml +1 -1
- data/lib/kthxbye/web_interface/views/workers.haml +11 -2
- data/lib/kthxbye/web_interface/views/working.haml +4 -2
- data/lib/kthxbye/worker.rb +33 -2
- data/test/test_kthxbye.rb +5 -5
- data/test/test_worker.rb +24 -1
- metadata +9 -4
data/lib/kthxbye.rb
CHANGED
@@ -11,6 +11,7 @@ require 'kthxbye/helper'
|
|
11
11
|
require 'kthxbye/job'
|
12
12
|
require 'kthxbye/worker'
|
13
13
|
require 'kthxbye/failure'
|
14
|
+
require 'kthxbye/stats'
|
14
15
|
|
15
16
|
require 'kthxbye/version'
|
16
17
|
|
@@ -213,7 +214,8 @@ module Kthxbye
|
|
213
214
|
:workers => workers.size,
|
214
215
|
:working => working.size,
|
215
216
|
:queues => queues.size,
|
216
|
-
:failed =>
|
217
|
+
:failed => Stats["failures"],
|
218
|
+
:jobs_processed => Stats["processed"],
|
217
219
|
:pending => queues.inject(0) {|m,o| m + size(o)}
|
218
220
|
}
|
219
221
|
end
|
data/lib/kthxbye/failure.rb
CHANGED
data/lib/kthxbye/job.rb
CHANGED
@@ -57,7 +57,7 @@ module Kthxbye
|
|
57
57
|
# remove the job's data as well
|
58
58
|
redis.hdel("data-store:#{queue}", id)
|
59
59
|
redis.hdel("result-store:#{queue}", id)
|
60
|
-
redis.hdel( :
|
60
|
+
redis.hdel( :failure, id )
|
61
61
|
|
62
62
|
return ret
|
63
63
|
end
|
@@ -69,7 +69,7 @@ module Kthxbye
|
|
69
69
|
@id = id.to_i
|
70
70
|
@queue = queue
|
71
71
|
@data = data
|
72
|
-
@failed_attempts =
|
72
|
+
@failed_attempts = Failure.fails_for_job(@id) # local tracking only, for rerun purposes
|
73
73
|
end
|
74
74
|
|
75
75
|
# Simply requeues the job to be rerun.
|
@@ -124,14 +124,19 @@ module Kthxbye
|
|
124
124
|
redis.hset( "result-store:#{@queue}", @id, encode( result ) )
|
125
125
|
return result
|
126
126
|
rescue Object => ex
|
127
|
-
|
128
|
-
|
129
|
-
redis.publish("job.failed", @id)
|
130
|
-
return Kthxbye::Failure.create( self, ex ) if @failed_attempts >= Kthxbye::Config.options[:attempts]
|
131
|
-
perform
|
127
|
+
# handled by worker
|
128
|
+
raise ex
|
132
129
|
end
|
133
130
|
end
|
134
131
|
|
132
|
+
def fail(ex)
|
133
|
+
@failed_attempts += 1
|
134
|
+
log "Error occured: #{ex.message}. Try: #{@failed_attempts}/#{Kthxbye::Config.options[:attempts]}"
|
135
|
+
redis.publish("job.failed", @id)
|
136
|
+
Stats.incr("failures")
|
137
|
+
Kthxbye::Failure.create( self, ex )
|
138
|
+
end
|
139
|
+
|
135
140
|
# Removes the job from the inactive queue.
|
136
141
|
def active
|
137
142
|
redis.srem("jobs:inactive", @id)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Simple library to aid in tracking statistics regarding Kthxbye and its workers.
|
2
|
+
# Submits jobs processed stats, total failures, total uptime, etc.
|
3
|
+
# Puts all stats in stats:x vars in Redis. Super flexible, made to allow tracking
|
4
|
+
# of any kind of count internally
|
5
|
+
#
|
6
|
+
# Built in stats (tracked by Kthxbye internals) include the following
|
7
|
+
#
|
8
|
+
# processed - the count of processed jobs
|
9
|
+
# <worker>:processed - the count a given worker has processed
|
10
|
+
# failures - the count of failed jobs
|
11
|
+
|
12
|
+
module Kthxbye
|
13
|
+
module Stats
|
14
|
+
extend self
|
15
|
+
extend Helper
|
16
|
+
|
17
|
+
# get count for a given stat (can also use [] method)
|
18
|
+
def get( stat )
|
19
|
+
redis.get( "stat:#{stat}" ).to_i
|
20
|
+
end
|
21
|
+
alias_method :[], :get
|
22
|
+
|
23
|
+
# increment a count by a given value (defaults to 1)
|
24
|
+
def incr( stat, by=1 )
|
25
|
+
redis.incrby("stat:#{stat}", by)
|
26
|
+
end
|
27
|
+
|
28
|
+
# decrement a count by a given value (defaults to 1)
|
29
|
+
def decr( stat, by=1 )
|
30
|
+
redis.decrby("stat:#{stat}", by)
|
31
|
+
end
|
32
|
+
|
33
|
+
# reset this stat to 0
|
34
|
+
def reset( stat )
|
35
|
+
redis.del( "stat:#{stat}" )
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/kthxbye/version.rb
CHANGED
@@ -99,6 +99,11 @@ module Kthxbye
|
|
99
99
|
redirect url(:overview)
|
100
100
|
end
|
101
101
|
|
102
|
+
get "/queues/:q" do
|
103
|
+
@queue = params[:q]
|
104
|
+
haml :queue, {:layout => :layout}
|
105
|
+
end
|
106
|
+
|
102
107
|
post "/failed/:job/rerun" do
|
103
108
|
Kthxbye::Failure.rerun(params[:job])
|
104
109
|
redirect url(:failed)
|
@@ -109,6 +114,11 @@ module Kthxbye
|
|
109
114
|
redirect url(:failed)
|
110
115
|
end
|
111
116
|
|
117
|
+
post "/failed/clear" do
|
118
|
+
Kthxbye::Failure.clear_all
|
119
|
+
redirect url(:queues)
|
120
|
+
end
|
121
|
+
|
112
122
|
post "/toggle_polling" do
|
113
123
|
redirect url(:overview)
|
114
124
|
end
|
@@ -5,6 +5,11 @@ body {
|
|
5
5
|
font-family: Helvetica, Arial, sans-serif;
|
6
6
|
}
|
7
7
|
|
8
|
+
h2 {
|
9
|
+
margin-bottom: 0;
|
10
|
+
color:#5d6f72;
|
11
|
+
}
|
12
|
+
|
8
13
|
#header {
|
9
14
|
/*height: 50px;*/
|
10
15
|
background-color: #b5d3d5;
|
@@ -37,7 +42,6 @@ body {
|
|
37
42
|
}
|
38
43
|
|
39
44
|
a {
|
40
|
-
text-decoration: none;
|
41
45
|
color: #619793;
|
42
46
|
}
|
43
47
|
|
@@ -126,3 +130,8 @@ table tr td.result, table tr th.result {
|
|
126
130
|
#backtrace {
|
127
131
|
padding:20px;
|
128
132
|
}
|
133
|
+
|
134
|
+
.t8 {
|
135
|
+
font-size: 8pt;
|
136
|
+
color: #5e5f5f;
|
137
|
+
}
|
@@ -17,7 +17,7 @@
|
|
17
17
|
%a{:href => "/view_backtrace?id=#{f['job']}"}
|
18
18
|
View Backtrace
|
19
19
|
%td #{f['time']}
|
20
|
-
%td
|
20
|
+
%td{:style => "white-space:nowrap;"}
|
21
21
|
%form{:action => "/failed/#{f['job']}/rerun", :method => "post", :style => "display:inline"}
|
22
22
|
%input{:class => "awesome button small", :value => "Retry", :type => "submit"}
|
23
23
|
%span.spacer
|
@@ -0,0 +1,14 @@
|
|
1
|
+
%h2 The #{params[:q]} queue
|
2
|
+
%table.queue_job
|
3
|
+
%tr
|
4
|
+
%th.id ID
|
5
|
+
%th.klass Class
|
6
|
+
%th.payload Data
|
7
|
+
%th.klass Status
|
8
|
+
- Kthxbye.data_peek(@queue).each do |id,job|
|
9
|
+
%tr
|
10
|
+
%td.id #{id}
|
11
|
+
%td.klass #{job['klass']}
|
12
|
+
%td.payload #{job['payload']}
|
13
|
+
%td.klass #{Kthxbye::Job.find(id,@queue).status}
|
14
|
+
|
@@ -1,31 +1,30 @@
|
|
1
|
-
%h2 Queues
|
1
|
+
%h2 Queues
|
2
|
+
.t8 The queue job count only shows jobs that are either waiting for processing or being processed
|
2
3
|
|
3
|
-
|
4
|
-
#queue{:style => "margin-top:20px"}
|
5
|
-
#queue_head
|
6
|
-
Pending jobs on
|
7
|
-
%b #{queue}
|
8
|
-
(#{Kthxbye.size(queue)})
|
9
|
-
%span.spacer
|
10
|
-
|
|
11
|
-
%form{:action => "/queues/#{queue}/remove", :style => "display:inline", :method => "post" }
|
12
|
-
%input{:type => "submit", :class => "awesome button small", :value => "Remove Queue"}
|
13
|
-
|
14
|
-
|
15
|
-
%br
|
16
|
-
|
17
|
-
%table.queue_job
|
18
|
-
%tr
|
19
|
-
%th.id ID
|
20
|
-
%th.klass Class
|
21
|
-
%th.payload Data
|
22
|
-
%th.klass Status
|
23
|
-
- Kthxbye.data_peek(queue).each do |id,job|
|
24
|
-
%tr
|
25
|
-
%td.id #{id}
|
26
|
-
%td.klass #{job['klass']}
|
27
|
-
%td.payload #{job['payload']}
|
28
|
-
%td.klass #{Kthxbye::Job.find(id,queue).status}
|
4
|
+
%br
|
29
5
|
|
6
|
+
%table
|
7
|
+
%tr
|
8
|
+
%th Name
|
9
|
+
%th Jobs
|
10
|
+
%th
|
11
|
+
- kthxbye.queues.each do |queue|
|
12
|
+
%tr
|
13
|
+
%td
|
14
|
+
%a{:href => "/queues/#{queue}"}
|
15
|
+
%b #{queue}
|
16
|
+
%td
|
17
|
+
= Kthxbye.size(queue)
|
18
|
+
%td
|
19
|
+
%form{:action => "/queues/#{queue}/remove", :style => "display:inline", :method => "post" }
|
20
|
+
%input{:type => "submit", :class => "awesome button small", :value => "Remove Queue"}
|
21
|
+
%tr{:style => "background-color:#F4A8AA"}
|
22
|
+
%td
|
23
|
+
%a{:href => "/failed"}
|
24
|
+
%b Failed
|
25
|
+
%td= Kthxbye::Failure.count
|
26
|
+
%td
|
27
|
+
%form{:action => "/failed/clear", :style => "display:inline", :method => "post" }
|
28
|
+
%input{:type => "submit", :class => "awesome button small", :value => "Clear"}
|
30
29
|
|
31
30
|
|
@@ -5,16 +5,25 @@
|
|
5
5
|
%th PID
|
6
6
|
%th Server
|
7
7
|
%th Queues
|
8
|
+
%th Uptime
|
9
|
+
%th Jobs Run
|
8
10
|
%th Working Job
|
9
11
|
- kthxbye.workers.each do |worker|
|
10
12
|
- host,pid,queues = worker.to_s.split(":")
|
11
13
|
%tr
|
12
14
|
%td #{pid}
|
13
15
|
%td #{host}
|
14
|
-
%td
|
16
|
+
%td
|
17
|
+
- queues.split(",").each do |queue|
|
18
|
+
%a{:href => "/queues/#{queue}"}
|
19
|
+
#{queues}
|
20
|
+
%td
|
21
|
+
= Time.parse(worker.started).strftime("%m/%d/%Y at %I:%M%p") if worker.started
|
22
|
+
%td
|
23
|
+
= worker.processed
|
15
24
|
%td
|
16
25
|
- job = Kthxbye::Worker.working_on(worker)
|
17
|
-
= job ? "<b>ID</b>: #{job['job_id']} - <b>Started</b>: #{job['started']}" : "***"
|
26
|
+
= job ? "<b>ID</b>: #{job['job_id']} - <b>Started</b>: #{Time.parse(job['started']).strftime("%I:%M%p")}" : "***"
|
18
27
|
|
19
28
|
- else
|
20
29
|
%span{:style => "color:red"}
|
@@ -1,4 +1,6 @@
|
|
1
|
-
%h2
|
1
|
+
%h2 #{Kthxbye.working.size} of #{Kthxbye.workers.size} workers active:
|
2
|
+
|
3
|
+
%br
|
2
4
|
|
3
5
|
- unless Kthxbye.working.empty?
|
4
6
|
%table
|
@@ -11,7 +13,7 @@
|
|
11
13
|
%td #{job['job_id']}
|
12
14
|
%td #{job['started']}
|
13
15
|
- else
|
14
|
-
%span{:style => "color
|
16
|
+
%span{:style => "color:#A6373A"}
|
15
17
|
Currently no active workers.
|
16
18
|
|
17
19
|
%p
|
data/lib/kthxbye/worker.rb
CHANGED
@@ -69,8 +69,7 @@ module Kthxbye
|
|
69
69
|
|
70
70
|
@child = fork {
|
71
71
|
log "Forking..."
|
72
|
-
|
73
|
-
yield job if block_given?
|
72
|
+
perform(job)
|
74
73
|
exit!
|
75
74
|
}
|
76
75
|
|
@@ -86,6 +85,19 @@ module Kthxbye
|
|
86
85
|
unregister_worker
|
87
86
|
end
|
88
87
|
|
88
|
+
def perform(job)
|
89
|
+
begin
|
90
|
+
result = job.perform
|
91
|
+
rescue Object => ex
|
92
|
+
job.fail(ex)
|
93
|
+
log "Error running job #{job}"
|
94
|
+
Stats.incr("failures:#{self}")
|
95
|
+
perform(job) if job.failed_attempts < Kthxbye::Config.options[:attempts]
|
96
|
+
ensure
|
97
|
+
yield job if block_given?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
89
101
|
# Returns the queues this worker is attached toin alphabetical order.
|
90
102
|
def queues
|
91
103
|
@queues.sort
|
@@ -117,6 +129,7 @@ module Kthxbye
|
|
117
129
|
def register_worker
|
118
130
|
log "Registered worker #{self}"
|
119
131
|
redis.sadd( :workers, self ) if !exists?
|
132
|
+
redis.set( "worker:#{self}:started", Time.now.to_s )
|
120
133
|
end
|
121
134
|
|
122
135
|
# Removes the worker from our worker registry
|
@@ -130,6 +143,10 @@ module Kthxbye
|
|
130
143
|
|
131
144
|
redis.del "worker:#{self}"
|
132
145
|
redis.srem :workers, self
|
146
|
+
redis.del "worker:#{self}:started"
|
147
|
+
|
148
|
+
Stats.reset("processed:#{self}")
|
149
|
+
Stats.reset("failures:#{self}")
|
133
150
|
end
|
134
151
|
|
135
152
|
# Gets the current job this worker is working.
|
@@ -163,9 +180,23 @@ module Kthxbye
|
|
163
180
|
redis.del( "worker:#{self}" )
|
164
181
|
log "Completed job #{@current_job}"
|
165
182
|
redis.publish("job.completed", @current_job.id)
|
183
|
+
Stats.incr("processed")
|
184
|
+
Stats.incr("processed:#{self}")
|
166
185
|
@current_job = nil
|
167
186
|
end
|
168
187
|
|
188
|
+
def processed
|
189
|
+
Stats["processed:#{self}"]
|
190
|
+
end
|
191
|
+
|
192
|
+
def failed
|
193
|
+
Stats["failures:#{self}"]
|
194
|
+
end
|
195
|
+
|
196
|
+
def started
|
197
|
+
redis.get( "worker:#{self}:started" )
|
198
|
+
end
|
199
|
+
|
169
200
|
#
|
170
201
|
# thanks to http://github.com/defunkt/resque/blob/master/lib/resque/worker.rb for these signals
|
171
202
|
#
|
data/test/test_kthxbye.rb
CHANGED
@@ -123,12 +123,12 @@ class TestKthxbye < Test::Unit::TestCase
|
|
123
123
|
|
124
124
|
should "retry a job if attempts setting is > 1" do
|
125
125
|
Kthxbye::Config.options[:attempts] = 2
|
126
|
+
worker = Kthxbye::Worker.new("test", 0)
|
126
127
|
id = Kthxbye.enqueue( "test", BadJob )
|
127
|
-
job = Kthxbye::Job.find(id, "test")
|
128
128
|
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
worker.run
|
130
|
+
job = Kthxbye::Job.find(id, "test")
|
131
|
+
assert_equal 2, job.failed_attempts
|
132
132
|
end
|
133
133
|
|
134
134
|
should "delete a job" do
|
@@ -191,8 +191,8 @@ class TestKthxbye < Test::Unit::TestCase
|
|
191
191
|
worker1.run
|
192
192
|
assert_equal :succeeded, job.status
|
193
193
|
|
194
|
-
job = Kthxbye::Job.find(bad_job, "bad")
|
195
194
|
worker2.run
|
195
|
+
job = Kthxbye::Job.find(bad_job, "bad")
|
196
196
|
|
197
197
|
assert_equal :failed, job.status
|
198
198
|
end
|
data/test/test_worker.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
class TestWorker < Test::Unit::TestCase
|
3
3
|
context "See Kthxbye::Worker" do
|
4
4
|
setup do
|
5
|
-
Kthxbye::Config.setup(:verbose => false, :redis_port => 9876)
|
5
|
+
Kthxbye::Config.setup(:verbose => false, :redis_port => 9876, :attempts => 1)
|
6
6
|
Kthxbye.redis.flushall
|
7
7
|
|
8
8
|
@id = Kthxbye.enqueue( "test", SimpleJob, 1, 2 )
|
@@ -130,6 +130,29 @@ class TestWorker < Test::Unit::TestCase
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
|
+
should "report system stats" do
|
134
|
+
Kthxbye.enqueue( "test", BadJob )
|
135
|
+
assert_equal 0, @worker.processed
|
136
|
+
assert_equal 0, @worker.failed
|
137
|
+
|
138
|
+
@worker.run
|
139
|
+
|
140
|
+
assert_equal 2, Kthxbye::Stats["processed"]
|
141
|
+
assert_equal 1, Kthxbye::Stats["failures"]
|
142
|
+
end
|
143
|
+
|
144
|
+
should "track failures" do
|
145
|
+
Kthxbye.enqueue( "test", BadJob )
|
146
|
+
Kthxbye.enqueue( "test", BadJob )
|
147
|
+
|
148
|
+
3.times do
|
149
|
+
job = @worker.grab_job
|
150
|
+
@worker.perform(job)
|
151
|
+
end
|
152
|
+
|
153
|
+
assert_equal 2, @worker.failed
|
154
|
+
end
|
155
|
+
|
133
156
|
should "requeue a job and report error if worker is killed mid-process" do
|
134
157
|
@worker.working( Kthxbye::Job.find( @id, "test" ) )
|
135
158
|
@worker.unregister_worker
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kthxbye
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 1
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 1.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 1.3.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Luke van der Hoeven
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-11 00:00:00 -04:00
|
18
19
|
default_executable: kthxbye-monitor
|
19
20
|
dependencies: []
|
20
21
|
|
@@ -42,6 +43,7 @@ files:
|
|
42
43
|
- lib/kthxbye/web_interface/views/overview.haml
|
43
44
|
- lib/kthxbye/web_interface/views/failed.haml
|
44
45
|
- lib/kthxbye/web_interface/views/set.haml
|
46
|
+
- lib/kthxbye/web_interface/views/queue.haml
|
45
47
|
- lib/kthxbye/web_interface/views/view_backtrace.haml
|
46
48
|
- lib/kthxbye/web_interface/views/layout.haml
|
47
49
|
- lib/kthxbye/web_interface/views/queues.haml
|
@@ -51,6 +53,7 @@ files:
|
|
51
53
|
- lib/kthxbye/failure.rb
|
52
54
|
- lib/kthxbye/config.rb
|
53
55
|
- lib/kthxbye/railtie.rb
|
56
|
+
- lib/kthxbye/stats.rb
|
54
57
|
- lib/kthxbye/helper.rb
|
55
58
|
- lib/kthxbye/worker.rb
|
56
59
|
- lib/kthxbye/railties/kthxbye.rake
|
@@ -85,6 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
88
|
requirements:
|
86
89
|
- - ">="
|
87
90
|
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
88
92
|
segments:
|
89
93
|
- 0
|
90
94
|
version: "0"
|
@@ -93,6 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
97
|
requirements:
|
94
98
|
- - ">="
|
95
99
|
- !ruby/object:Gem::Version
|
100
|
+
hash: 23
|
96
101
|
segments:
|
97
102
|
- 1
|
98
103
|
- 3
|