sidekiq-status 0.3.2 → 0.4.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.
- 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
|