sidekiq-status 0.3.0 → 0.3.1
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/README.md +9 -0
- data/lib/sidekiq-status.rb +6 -0
- data/lib/sidekiq-status/storage.rb +45 -0
- data/lib/sidekiq-status/version.rb +1 -1
- data/spec/lib/sidekiq-status_spec.rb +50 -8
- data/spec/spec_helper.rb +1 -0
- data/spec/support/test_jobs.rb +1 -0
- metadata +2 -3
data/README.md
CHANGED
@@ -91,6 +91,14 @@ Sidekiq::Status::total job_id #=> 100
|
|
91
91
|
Sidekiq::Status::message job_id #=> "Almost done"
|
92
92
|
Sidekiq::Status::pct_complete job_id #=> 5
|
93
93
|
```
|
94
|
+
### Unscheduling
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
scheduled_job_id = MyJob.perform_in 3600
|
98
|
+
Sidekiq::Status.cancel scheduled_job_id #=> true
|
99
|
+
#doesn't cancel running jobs, this is more like unscheduling, therefore an alias:
|
100
|
+
Sidekiq::Status.unschedule scheduled_job_id #=> true
|
101
|
+
```
|
94
102
|
|
95
103
|
### Features coming
|
96
104
|
* Stopping jobs by id
|
@@ -100,6 +108,7 @@ Sidekiq::Status::pct_complete job_id #=> 5
|
|
100
108
|
Andrew Korzhuev
|
101
109
|
Jon Moses
|
102
110
|
Wayne Hoover
|
111
|
+
Dylan Robinson
|
103
112
|
|
104
113
|
## License
|
105
114
|
MIT License , see LICENSE for more details.
|
data/lib/sidekiq-status.rb
CHANGED
@@ -29,6 +29,12 @@ module Sidekiq::Status
|
|
29
29
|
status.to_sym unless status.nil?
|
30
30
|
end
|
31
31
|
|
32
|
+
def cancel(job_id, job_unix_time = nil)
|
33
|
+
delete_and_unschedule(job_id, job_unix_time)
|
34
|
+
end
|
35
|
+
|
36
|
+
alias_method :unschedule, :cancel
|
37
|
+
|
32
38
|
STATUS.each do |name|
|
33
39
|
class_eval(<<-END, __FILE__, __LINE__)
|
34
40
|
def #{name}?(job_id)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Sidekiq::Status::Storage
|
2
2
|
RESERVED_FIELDS=%w(status stop update_time).freeze
|
3
|
+
BATCH_LIMIT = 500
|
3
4
|
|
4
5
|
protected
|
5
6
|
|
@@ -27,6 +28,26 @@ module Sidekiq::Status::Storage
|
|
27
28
|
store_for_id id, {status: status}, expiration
|
28
29
|
end
|
29
30
|
|
31
|
+
# Unschedules the job and deletes the Status
|
32
|
+
# @param [String] id job id
|
33
|
+
# @param [Num] job_unix_time, unix timestamp for the scheduled job
|
34
|
+
def delete_and_unschedule(job_id, job_unix_time = nil)
|
35
|
+
Sidekiq.redis do |conn|
|
36
|
+
scan_options = {offset: 0, conn: conn, start: (job_unix_time || '-inf'), end: (job_unix_time || '+inf')}
|
37
|
+
|
38
|
+
while not (jobs = schedule_batch(scan_options)).empty?
|
39
|
+
match = scan_scheduled_jobs_for_jid jobs, job_id
|
40
|
+
unless match.nil?
|
41
|
+
conn.zrem "schedule", match
|
42
|
+
conn.del job_id
|
43
|
+
return true # Done
|
44
|
+
end
|
45
|
+
scan_options[:offset] += BATCH_LIMIT
|
46
|
+
end
|
47
|
+
end
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
30
51
|
# Gets a single valued from job status hash
|
31
52
|
# @param [String] id job id
|
32
53
|
# @param [String] Symbol field fetched field name
|
@@ -45,4 +66,28 @@ module Sidekiq::Status::Storage
|
|
45
66
|
conn.hgetall id
|
46
67
|
end
|
47
68
|
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Gets the batch of scheduled jobs based on input options
|
73
|
+
# Uses Redis zrangebyscore for log(n) search, if unix-time is provided
|
74
|
+
# @param [Hash] options, options hash containing (REQUIRED) keys:
|
75
|
+
# - conn: Redis connection
|
76
|
+
# - start: start score (i.e. -inf or a unix timestamp)
|
77
|
+
# - end: end score (i.e. +inf or a unix timestamp)
|
78
|
+
# - offset: current progress through (all) jobs (e.g.: 100 if you want jobs from 100 to BATCH_LIMIT)
|
79
|
+
def schedule_batch(options)
|
80
|
+
options[:conn].zrangebyscore "schedule", options[:start], options[:end], {limit: [options[:offset], BATCH_LIMIT]}
|
81
|
+
end
|
82
|
+
|
83
|
+
# Searches the jobs Array for the job_id
|
84
|
+
# @param [Array] scheduled_jobs, results of Redis schedule key
|
85
|
+
# @param [String] id job id
|
86
|
+
def scan_scheduled_jobs_for_jid(scheduled_jobs, job_id)
|
87
|
+
# A Little skecthy, I know, but the structure of these internal JSON
|
88
|
+
# is predefined in such a way where this will not catch unintentional elements,
|
89
|
+
# and this is notably faster than performing JSON.parse() for every listing:
|
90
|
+
scheduled_jobs.each { |job_listing| (return job_listing) if job_listing.include?("\"jid\":\"#{job_id}") }
|
91
|
+
nil
|
92
|
+
end
|
48
93
|
end
|
@@ -5,6 +5,9 @@ describe Sidekiq::Status do
|
|
5
5
|
let!(:redis) { Sidekiq.redis { |conn| conn } }
|
6
6
|
let!(:job_id) { SecureRandom.hex(12) }
|
7
7
|
let!(:job_id_1) { SecureRandom.hex(12) }
|
8
|
+
let!(:unused_id) { SecureRandom.hex(12) }
|
9
|
+
let!(:plain_sidekiq_job_id) { SecureRandom.hex(12) }
|
10
|
+
let!(:retried_job_id) { SecureRandom.hex(12) }
|
8
11
|
|
9
12
|
# Clean Redis before each test
|
10
13
|
# Seems like flushall has no effect on recently published messages,
|
@@ -77,30 +80,69 @@ describe Sidekiq::Status do
|
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
83
|
+
describe ".cancel" do
|
84
|
+
it "cancels a job by id" do
|
85
|
+
SecureRandom.should_receive(:hex).twice.and_return(job_id, job_id_1)
|
86
|
+
start_server do
|
87
|
+
job = LongJob.perform_in(3600)
|
88
|
+
job.should == job_id
|
89
|
+
second_job = LongJob.perform_in(3600)
|
90
|
+
second_job.should == job_id_1
|
91
|
+
|
92
|
+
initial_schedule = redis.zrange "schedule", 0, -1, {withscores: true}
|
93
|
+
initial_schedule.size.should be 2
|
94
|
+
initial_schedule.select {|scheduled_job| JSON.parse(scheduled_job[0])["jid"] == job_id }.size.should be 1
|
95
|
+
|
96
|
+
Sidekiq::Status.unschedule(job_id).should be_true
|
97
|
+
Sidekiq::Status.cancel(unused_id).should be_false # Unused, therefore unfound => false
|
98
|
+
|
99
|
+
remaining_schedule = redis.zrange "schedule", 0, -1, {withscores: true}
|
100
|
+
remaining_schedule.size.should == (initial_schedule.size - 1)
|
101
|
+
remaining_schedule.select {|scheduled_job| JSON.parse(scheduled_job[0])["jid"] == job_id }.size.should be 0
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it "does not cancel a job with correct id but wrong time" do
|
106
|
+
SecureRandom.should_receive(:hex).once.and_return(job_id)
|
107
|
+
start_server do
|
108
|
+
scheduled_time = Time.now.to_i + 3600
|
109
|
+
returned_job_id = LongJob.perform_at(scheduled_time)
|
110
|
+
returned_job_id.should == job_id
|
111
|
+
|
112
|
+
initial_schedule = redis.zrange "schedule", 0, -1, {withscores: true}
|
113
|
+
initial_schedule.size.should == 1
|
114
|
+
Sidekiq::Status.cancel(returned_job_id, (scheduled_time + 1)).should be_false # wrong time, therefore unfound => false
|
115
|
+
(redis.zrange "schedule", 0, -1, {withscores: true}).size.should be 1
|
116
|
+
Sidekiq::Status.cancel(returned_job_id, (scheduled_time)).should be_true # same id, same time, deletes
|
117
|
+
(redis.zrange "schedule", 0, -1, {withscores: true}).size.should be_zero
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
80
122
|
context "keeps normal Sidekiq functionality" do
|
81
123
|
it "does jobs with and without included worker module" do
|
82
|
-
SecureRandom.should_receive(:hex).exactly(4).times.and_return(
|
124
|
+
SecureRandom.should_receive(:hex).exactly(4).times.and_return(plain_sidekiq_job_id, plain_sidekiq_job_id, job_id_1, job_id_1)
|
83
125
|
start_server do
|
84
126
|
capture_status_updates(12) {
|
85
|
-
StubJob.perform_async.should ==
|
127
|
+
StubJob.perform_async.should == plain_sidekiq_job_id
|
86
128
|
NoStatusConfirmationJob.perform_async(1)
|
87
129
|
StubJob.perform_async.should == job_id_1
|
88
130
|
NoStatusConfirmationJob.perform_async(2)
|
89
|
-
}.should =~ [
|
131
|
+
}.should =~ [plain_sidekiq_job_id, job_id_1] * 6
|
90
132
|
end
|
91
133
|
redis.mget('NoStatusConfirmationJob_1', 'NoStatusConfirmationJob_2').should == %w(done)*2
|
92
|
-
Sidekiq::Status.status(
|
134
|
+
Sidekiq::Status.status(plain_sidekiq_job_id).should == :complete
|
93
135
|
Sidekiq::Status.status(job_id_1).should == :complete
|
94
136
|
end
|
95
137
|
|
96
138
|
it "retries failed jobs" do
|
97
|
-
SecureRandom.should_receive(:hex).once.and_return(
|
139
|
+
SecureRandom.should_receive(:hex).once.and_return(retried_job_id)
|
98
140
|
start_server do
|
99
141
|
capture_status_updates(5) {
|
100
|
-
RetriedJob.perform_async().should ==
|
101
|
-
}.should == [
|
142
|
+
RetriedJob.perform_async().should == retried_job_id
|
143
|
+
}.should == [retried_job_id] * 5
|
102
144
|
end
|
103
|
-
Sidekiq::Status.status(
|
145
|
+
Sidekiq::Status.status(retried_job_id).should == :complete
|
104
146
|
end
|
105
147
|
end
|
106
148
|
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/test_jobs.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-status
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-07-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|
@@ -118,4 +118,3 @@ test_files:
|
|
118
118
|
- spec/lib/sidekiq-status_spec.rb
|
119
119
|
- spec/spec_helper.rb
|
120
120
|
- spec/support/test_jobs.rb
|
121
|
-
has_rdoc:
|