sidekiq-status 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +1 -0
- data/README.md +23 -2
- data/lib/sidekiq-status.rb +1 -0
- data/lib/sidekiq-status/server_middleware.rb +8 -1
- data/lib/sidekiq-status/version.rb +1 -1
- data/lib/sidekiq-status/web.rb +46 -0
- data/spec/lib/sidekiq-status/server_middleware_spec.rb +22 -1
- data/spec/lib/sidekiq-status/worker_spec.rb +10 -1
- data/spec/lib/sidekiq-status_spec.rb +52 -12
- data/spec/spec_helper.rb +2 -2
- data/web/views/statuses.erb +36 -0
- metadata +14 -19
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 70899fafcf4e5aa712b2df57e34700cba6d001fe
|
4
|
+
data.tar.gz: cd4801e8d4678b515f3a597438953c3430066a30
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1669fb78f47cc77930d4b93f7c7a1537526d426cf0e99c4c5bb8b13e271ed66711e15276959a381216710aca8cfac60f90fc0790719db2c9a8f4c3faaa046a41
|
7
|
+
data.tar.gz: d7e697fdb8ee26e50f125b73b44abe2f5c796e74ad9242d76bc85964e526c33b9d81916fa49a3623c29bb55b2b9a5126b30db8e730c5668d34d1a6f7dbcfd024
|
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Version 0.4.0:WebUI added, per-worker expiration setting enabled
|
data/README.md
CHANGED
@@ -31,6 +31,9 @@ Sidekiq.configure_server do |config|
|
|
31
31
|
config.server_middleware do |chain|
|
32
32
|
chain.add Sidekiq::Status::ServerMiddleware, expiration: 30.minutes # default
|
33
33
|
end
|
34
|
+
config.client_middleware do |chain|
|
35
|
+
chain.add Sidekiq::Status::ClientMiddleware
|
36
|
+
end
|
34
37
|
end
|
35
38
|
```
|
36
39
|
|
@@ -46,6 +49,24 @@ class MyJob
|
|
46
49
|
end
|
47
50
|
```
|
48
51
|
|
52
|
+
To overwrite expiration on worker basis and don't use global expiration for all workers write a expiration method like this below:
|
53
|
+
|
54
|
+
``` ruby
|
55
|
+
class MyJob
|
56
|
+
include Sidekiq::Worker
|
57
|
+
|
58
|
+
def expiration
|
59
|
+
@expiration ||= 60*60*24*30 # 30 days
|
60
|
+
end
|
61
|
+
|
62
|
+
def perform(*args)
|
63
|
+
# your code goes here
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
But keep in mind that such thing will store details of job as long as expiration is set, so it may charm your Redis storage/memory consumption. Because Redis stores all data in RAM.
|
69
|
+
|
49
70
|
### Retrieving status
|
50
71
|
|
51
72
|
Query for job status any time later:
|
@@ -122,9 +143,9 @@ To use `sidekiq-status` inlining, require it too in your `{test,spec}_helper.rb`
|
|
122
143
|
|
123
144
|
### Features coming
|
124
145
|
* Stopping jobs by id
|
125
|
-
* Minimal web UI
|
126
146
|
|
127
147
|
## Thanks
|
148
|
+
* Clay Allsopp
|
128
149
|
* Andrew Korzhuev
|
129
150
|
* Jon Moses
|
130
151
|
* Wayne Hoover
|
@@ -132,4 +153,4 @@ To use `sidekiq-status` inlining, require it too in your `{test,spec}_helper.rb`
|
|
132
153
|
|
133
154
|
## License
|
134
155
|
MIT License , see LICENSE for more details.
|
135
|
-
© 2012 -
|
156
|
+
© 2012 - 2014 Evgeniy Tsvigun
|
data/lib/sidekiq-status.rb
CHANGED
@@ -24,7 +24,14 @@ module Sidekiq::Status
|
|
24
24
|
def call(worker, msg, queue)
|
25
25
|
# a way of overriding default expiration time,
|
26
26
|
# so worker wouldn't lose its data
|
27
|
-
|
27
|
+
# and it allows also to overwrite global expiration time on worker basis
|
28
|
+
if worker.respond_to? :expiration
|
29
|
+
if !worker.expiration && worker.respond_to?(:expiration=)
|
30
|
+
worker.expiration = @expiration
|
31
|
+
else
|
32
|
+
@expiration = worker.expiration
|
33
|
+
end
|
34
|
+
end
|
28
35
|
|
29
36
|
store_status worker.jid, :working, @expiration
|
30
37
|
yield
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# adapted from https://github.com/cryo28/sidekiq_status
|
2
|
+
|
3
|
+
module Sidekiq::Status
|
4
|
+
# Hook into *Sidekiq::Web* Sinatra app which adds a new "/statuses" page
|
5
|
+
module Web
|
6
|
+
# Location of Sidekiq::Status::Web view templates
|
7
|
+
VIEW_PATH = File.expand_path('../../../web/views', __FILE__)
|
8
|
+
|
9
|
+
# @param [Sidekiq::Web] app
|
10
|
+
def self.registered(app)
|
11
|
+
app.helpers do
|
12
|
+
def sidekiq_status_template(name)
|
13
|
+
path = File.join(VIEW_PATH, name.to_s) + ".erb"
|
14
|
+
File.open(path).read
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
app.get '/statuses' do
|
19
|
+
queue = Sidekiq::Workers.new
|
20
|
+
@statuses = []
|
21
|
+
|
22
|
+
queue.each do |name, work, started_at|
|
23
|
+
job = Struct.new(:jid, :klass, :args).new(work["payload"]["jid"], work["payload"]["class"], work["payload"]["args"])
|
24
|
+
status = Sidekiq::Status::get_all job.jid
|
25
|
+
next if !status || status.count <= 2
|
26
|
+
status["worker"] = job.klass
|
27
|
+
status["args"] = job.args
|
28
|
+
status["jid"] = job.jid
|
29
|
+
status["pct_complete"] = ((status["num"].to_f / status["total"].to_f) * 100).to_i
|
30
|
+
@statuses << OpenStruct.new(status)
|
31
|
+
end
|
32
|
+
|
33
|
+
erb(sidekiq_status_template(:statuses))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'sidekiq/web' unless defined?(Sidekiq::Web)
|
40
|
+
Sidekiq::Web.register(Sidekiq::Status::Web)
|
41
|
+
if Sidekiq::Web.tabs.is_a?(Array)
|
42
|
+
# For sidekiq < 2.5
|
43
|
+
Sidekiq::Web.tabs << "statuses"
|
44
|
+
else
|
45
|
+
Sidekiq::Web.tabs["Statuses"] = "statuses"
|
46
|
+
end
|
@@ -40,6 +40,27 @@ describe Sidekiq::Status::ServerMiddleware do
|
|
40
40
|
StubJob.perform_async(:arg1 => 'val1').should == job_id
|
41
41
|
(1..Sidekiq::Status::DEFAULT_EXPIRY).should cover redis.ttl(job_id)
|
42
42
|
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe ":expiration parameter" do
|
46
|
+
let(:huge_expiration) { Sidekiq::Status::DEFAULT_EXPIRY * 100 }
|
47
|
+
before do
|
48
|
+
SecureRandom.should_receive(:hex).once.and_return(job_id)
|
49
|
+
end
|
50
|
+
it "overwrites default expiry value" do
|
51
|
+
start_server(:expiration => huge_expiration) do
|
52
|
+
StubJob.perform_async(:arg1 => 'val1')
|
53
|
+
end
|
54
|
+
((Sidekiq::Status::DEFAULT_EXPIRY+1)..huge_expiration).should cover redis.ttl(job_id)
|
55
|
+
end
|
43
56
|
|
57
|
+
it "can be overwritten by worker expiration method" do
|
58
|
+
overwritten_expiration = huge_expiration * 100
|
59
|
+
StubJob.any_instance.stub(expiration: overwritten_expiration)
|
60
|
+
start_server(:expiration => huge_expiration) do
|
61
|
+
StubJob.perform_async(:arg1 => 'val1')
|
62
|
+
end
|
63
|
+
((huge_expiration+1)..overwritten_expiration).should cover redis.ttl(job_id)
|
64
|
+
end
|
44
65
|
end
|
45
|
-
end
|
66
|
+
end
|
@@ -10,4 +10,13 @@ describe Sidekiq::Status::Worker do
|
|
10
10
|
StubJob.perform_async().should == job_id
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
|
+
describe ".expiration" do
|
15
|
+
subject { StubJob.new }
|
16
|
+
it "allows to set/get expiration" do
|
17
|
+
expect(subject.expiration).to be_nil
|
18
|
+
subject.expiration = :val
|
19
|
+
expect(subject.expiration).to eq(:val)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -120,19 +120,13 @@ describe Sidekiq::Status do
|
|
120
120
|
end
|
121
121
|
|
122
122
|
context "keeps normal Sidekiq functionality" do
|
123
|
+
let(:expiration_param) { nil }
|
124
|
+
|
123
125
|
it "does jobs with and without included worker module" do
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
NoStatusConfirmationJob.perform_async(1)
|
129
|
-
StubJob.perform_async.should == job_id_1
|
130
|
-
NoStatusConfirmationJob.perform_async(2)
|
131
|
-
}.should =~ [plain_sidekiq_job_id, job_id_1] * 6
|
132
|
-
end
|
133
|
-
redis.mget('NoStatusConfirmationJob_1', 'NoStatusConfirmationJob_2').should == %w(done)*2
|
134
|
-
Sidekiq::Status.status(plain_sidekiq_job_id).should == :complete
|
135
|
-
Sidekiq::Status.status(job_id_1).should == :complete
|
126
|
+
seed_secure_random_with_job_ids
|
127
|
+
run_2_jobs!
|
128
|
+
expect_2_jobs_are_done_and_status_eq :complete
|
129
|
+
expect_2_jobs_ttl_covers 1..Sidekiq::Status::DEFAULT_EXPIRY
|
136
130
|
end
|
137
131
|
|
138
132
|
it "retries failed jobs" do
|
@@ -144,6 +138,52 @@ describe Sidekiq::Status do
|
|
144
138
|
end
|
145
139
|
Sidekiq::Status.status(retried_job_id).should == :complete
|
146
140
|
end
|
141
|
+
|
142
|
+
context ":expiration param" do
|
143
|
+
before { seed_secure_random_with_job_ids }
|
144
|
+
let(:expiration_param) { Sidekiq::Status::DEFAULT_EXPIRY * 100 }
|
145
|
+
|
146
|
+
it "allow to overwrite :expiration parameter" do
|
147
|
+
run_2_jobs!
|
148
|
+
expect_2_jobs_are_done_and_status_eq :complete
|
149
|
+
expect_2_jobs_ttl_covers (Sidekiq::Status::DEFAULT_EXPIRY+1)..expiration_param
|
150
|
+
end
|
151
|
+
|
152
|
+
it "allow to overwrite :expiration parameter by .expiration method from worker" do
|
153
|
+
overwritten_expiration = expiration_param * 100
|
154
|
+
NoStatusConfirmationJob.any_instance.stub(:expiration => overwritten_expiration)
|
155
|
+
StubJob.any_instance.stub(:expiration => overwritten_expiration)
|
156
|
+
run_2_jobs!
|
157
|
+
expect_2_jobs_are_done_and_status_eq :complete
|
158
|
+
expect_2_jobs_ttl_covers (expiration_param+1)..overwritten_expiration
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def seed_secure_random_with_job_ids
|
163
|
+
SecureRandom.should_receive(:hex).exactly(4).times.and_return(plain_sidekiq_job_id, plain_sidekiq_job_id, job_id_1, job_id_1)
|
164
|
+
end
|
165
|
+
|
166
|
+
def run_2_jobs!
|
167
|
+
start_server(:expiration => expiration_param) do
|
168
|
+
capture_status_updates(12) {
|
169
|
+
StubJob.perform_async.should == plain_sidekiq_job_id
|
170
|
+
NoStatusConfirmationJob.perform_async(1)
|
171
|
+
StubJob.perform_async.should == job_id_1
|
172
|
+
NoStatusConfirmationJob.perform_async(2)
|
173
|
+
}.should =~ [plain_sidekiq_job_id, job_id_1] * 6
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def expect_2_jobs_ttl_covers(range)
|
178
|
+
range.should cover redis.ttl(plain_sidekiq_job_id)
|
179
|
+
range.should cover redis.ttl(job_id_1)
|
180
|
+
end
|
181
|
+
|
182
|
+
def expect_2_jobs_are_done_and_status_eq(status)
|
183
|
+
redis.mget('NoStatusConfirmationJob_1', 'NoStatusConfirmationJob_2').should == %w(done)*2
|
184
|
+
Sidekiq::Status.status(plain_sidekiq_job_id).should == status
|
185
|
+
Sidekiq::Status.status(job_id_1).should == status
|
186
|
+
end
|
147
187
|
end
|
148
188
|
|
149
189
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -44,7 +44,7 @@ def capture_status_updates(n, &block)
|
|
44
44
|
confirmations_thread(n, "status_updates", &block).value
|
45
45
|
end
|
46
46
|
|
47
|
-
def start_server()
|
47
|
+
def start_server(server_middleware_options={})
|
48
48
|
pid = Process.fork do
|
49
49
|
$stdout.reopen File::NULL, 'w'
|
50
50
|
$stderr.reopen File::NULL, 'w'
|
@@ -53,7 +53,7 @@ def start_server()
|
|
53
53
|
Sidekiq.configure_server do |config|
|
54
54
|
config.redis = Sidekiq::RedisConnection.create
|
55
55
|
config.server_middleware do |chain|
|
56
|
-
chain.add Sidekiq::Status::ServerMiddleware
|
56
|
+
chain.add Sidekiq::Status::ServerMiddleware, server_middleware_options
|
57
57
|
end
|
58
58
|
end
|
59
59
|
Sidekiq::CLI.instance.run
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<h3 class="wi">Recent job statuses</h3>
|
2
|
+
|
3
|
+
|
4
|
+
<table class="table table-striped table-bordered">
|
5
|
+
<tr>
|
6
|
+
<th>Worker/jid</th>
|
7
|
+
<th>Status</th>
|
8
|
+
<th>Last Updated ↆ</th>
|
9
|
+
<th>Progress</th>
|
10
|
+
<th>Message</th>
|
11
|
+
</tr>
|
12
|
+
<% @statuses.each do |container| -%>
|
13
|
+
<tr>
|
14
|
+
<td>
|
15
|
+
<%= container.worker %>
|
16
|
+
<br />
|
17
|
+
<%= container.jid %>
|
18
|
+
</td>
|
19
|
+
<td><%= container.status %></td>
|
20
|
+
<td><%= Time.at(container.update_time.to_i) %></td>
|
21
|
+
<td>
|
22
|
+
<div class="progress progress-striped" style="margin-bottom: 0">
|
23
|
+
<div class="bar" style="width: <%= container.pct_complete %>%; text-shadow: 1px 1px 1px black; background-color: #AD003D;
|
24
|
+
color: white;">
|
25
|
+
<%= container.pct_complete %>%
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
<td><%= container.message %></td>
|
29
|
+
</tr>
|
30
|
+
<% end -%>
|
31
|
+
<% if @statuses.empty? -%>
|
32
|
+
<tr>
|
33
|
+
<td colspan="6"></td>
|
34
|
+
</tr>
|
35
|
+
<% end -%>
|
36
|
+
</table>
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-status
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.4.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Evgeniy Tsvigun
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-03-11 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: sidekiq
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
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
|
@@ -30,33 +27,29 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rake
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
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
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rspec
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - '>='
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - '>='
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
55
|
description:
|
@@ -69,6 +62,7 @@ files:
|
|
69
62
|
- .gitignore
|
70
63
|
- .rspec
|
71
64
|
- .travis.yml
|
65
|
+
- CHANGELOG
|
72
66
|
- Gemfile
|
73
67
|
- LICENSE
|
74
68
|
- README.md
|
@@ -79,6 +73,7 @@ files:
|
|
79
73
|
- lib/sidekiq-status/storage.rb
|
80
74
|
- lib/sidekiq-status/testing/inline.rb
|
81
75
|
- lib/sidekiq-status/version.rb
|
76
|
+
- lib/sidekiq-status/web.rb
|
82
77
|
- lib/sidekiq-status/worker.rb
|
83
78
|
- sidekiq-status.gemspec
|
84
79
|
- spec/lib/sidekiq-status/client_middleware_spec.rb
|
@@ -88,30 +83,30 @@ files:
|
|
88
83
|
- spec/lib/sidekiq-status_spec.rb
|
89
84
|
- spec/spec_helper.rb
|
90
85
|
- spec/support/test_jobs.rb
|
86
|
+
- web/views/statuses.erb
|
91
87
|
homepage: http://github.com/utgarda/sidekiq-status
|
92
88
|
licenses:
|
93
89
|
- MIT
|
90
|
+
metadata: {}
|
94
91
|
post_install_message:
|
95
92
|
rdoc_options: []
|
96
93
|
require_paths:
|
97
94
|
- lib
|
98
95
|
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
-
none: false
|
100
96
|
requirements:
|
101
|
-
- -
|
97
|
+
- - '>='
|
102
98
|
- !ruby/object:Gem::Version
|
103
99
|
version: '0'
|
104
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
101
|
requirements:
|
107
|
-
- -
|
102
|
+
- - '>='
|
108
103
|
- !ruby/object:Gem::Version
|
109
104
|
version: '0'
|
110
105
|
requirements: []
|
111
106
|
rubyforge_project:
|
112
|
-
rubygems_version:
|
107
|
+
rubygems_version: 2.0.3
|
113
108
|
signing_key:
|
114
|
-
specification_version:
|
109
|
+
specification_version: 4
|
115
110
|
summary: An extension to the sidekiq message processing to track your jobs
|
116
111
|
test_files:
|
117
112
|
- spec/lib/sidekiq-status/client_middleware_spec.rb
|