sidekiq-cron 0.2.0 → 0.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.
- checksums.yaml +7 -0
- data/.travis.yml +2 -3
- data/Changes.md +6 -1
- data/Gemfile +10 -9
- data/README.md +83 -9
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/lib/sidekiq/cron.rb +1 -18
- data/lib/sidekiq/cron/job.rb +71 -12
- data/lib/sidekiq/cron/views/cron.erb +5 -5
- data/lib/sidekiq/cron/views/cron.slim +7 -7
- data/lib/sidekiq/cron/web.rb +12 -0
- data/sidekiq-cron.gemspec +13 -8
- data/test/test_helper.rb +2 -7
- data/test/unit/job_test.rb +587 -462
- data/test/unit/poller_test.rb +123 -127
- data/test/unit/web_extesion_test.rb +63 -68
- metadata +62 -84
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0e24b22d1f58e374c9ea85f606f3a86e01c204c6
|
4
|
+
data.tar.gz: af7f0ea32e39568a6f04981703088cf9c5fb4692
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4283ebe1d86984162381e640bad0d447b7fd2b4b25fd24f0a5750381fcd2aa74ab1432c3918015789bd549635928afbe84a102956c52a0ba79e7383850154854
|
7
|
+
data.tar.gz: 38e0734ff2253e123fe012687153cad465a06b187cda5806ac05baac3984ef24fbb14b072f522d2e759b821b0989c6cd4ca538c249efb43067946635d70d2911
|
data/.travis.yml
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 1.9.3
|
4
3
|
- jruby-19mode
|
5
4
|
- rbx-19mode
|
6
5
|
- 2.0.0
|
7
|
-
- 2.
|
6
|
+
- 2.2.2
|
8
7
|
services:
|
9
8
|
- redis-server
|
10
9
|
branches:
|
@@ -19,4 +18,4 @@ env:
|
|
19
18
|
matrix:
|
20
19
|
allow_failures:
|
21
20
|
- rvm: jruby-19mode
|
22
|
-
- rvm: rbx-19mode
|
21
|
+
- rvm: rbx-19mode
|
data/Changes.md
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
v 0.3.0
|
2
|
+
-------
|
1
3
|
|
4
|
+
- suport for Active Job
|
5
|
+
- sidekiq cron web ui needs to be loaded by: require 'sidekiq/cron/web'
|
6
|
+
- add load_from_hash! and load_from_array! which cleanup jobs before adding new ones
|
2
7
|
|
3
8
|
v 0.1.1
|
4
9
|
-------
|
5
10
|
|
6
11
|
- add Web fontend with enabled/disable job, unqueue now, delete job
|
7
12
|
- add cron poller - enqueu cro jobs
|
8
|
-
- add cron job - save all needed data to redis
|
13
|
+
- add cron job - save all needed data to redis
|
data/Gemfile
CHANGED
@@ -1,30 +1,31 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
gem 'tilt' , '< 2.0.0'
|
4
|
-
gem
|
4
|
+
gem 'sidekiq', '>= 2.17.3'
|
5
5
|
gem 'rufus-scheduler', '>= 2.0.24'
|
6
6
|
|
7
7
|
group :development do
|
8
|
-
gem
|
9
|
-
gem
|
8
|
+
gem 'bundler'
|
9
|
+
gem 'simplecov'
|
10
10
|
|
11
11
|
gem 'shoulda-context'
|
12
|
-
gem "turn"
|
13
12
|
|
14
13
|
gem 'rack'
|
15
14
|
gem 'rack-test'
|
16
15
|
|
17
|
-
gem
|
16
|
+
gem 'jeweler', '~> 1.8.3'
|
18
17
|
|
19
|
-
gem
|
18
|
+
gem 'minitest'
|
19
|
+
gem 'test-unit'
|
20
|
+
gem 'sdoc' # sdoc -N .
|
20
21
|
|
21
|
-
gem
|
22
|
-
gem
|
22
|
+
gem 'slim'
|
23
|
+
gem 'sinatra'
|
23
24
|
|
24
25
|
gem 'mocha'
|
25
26
|
gem 'coveralls'
|
26
27
|
|
27
|
-
gem
|
28
|
+
gem 'shotgun'
|
28
29
|
|
29
30
|
# gem 'guard'
|
30
31
|
# gem 'guard-minitest'
|
data/README.md
CHANGED
@@ -2,16 +2,28 @@ Sidekiq-Cron [](http://
|
|
2
2
|
================================================================================================================================================================================================================================================================================================================================================================================================================================================
|
3
3
|
|
4
4
|
|
5
|
-
|
5
|
+
An scheduling add-on for [Sidekiq](http://sidekiq.org).
|
6
6
|
|
7
|
-
|
7
|
+
Runs a thread along side Sidekiq workers to schedule jobs at specified times (using cron notation `* * * * *` parsed by [Rufus-Scheduler](https://github.com/jmettraux/rufus-scheduler), more about [cron notation](http://www.nncron.ru/help/EN/working/cron-format.htm).
|
8
|
+
|
9
|
+
Checks for new jobs to schedule every 10 seconds and doesn't schedule the same job multiple times when more than one Sidekiq worker is running.
|
10
|
+
|
11
|
+
Scheduling jobs are added only when at least one sidekiq process is running.
|
12
|
+
|
13
|
+
If you want to know how scheduling work check [out under the hood](#under-the-hood)
|
14
|
+
|
15
|
+
Works with Active jobs (Rails 4.2+)
|
8
16
|
|
9
17
|
Requirements
|
10
18
|
-----------------
|
11
19
|
|
12
20
|
- Redis 2.4 or greater is required.
|
13
|
-
- Sidekiq 2.
|
21
|
+
- Sidekiq 2.17.3 or grater is required.
|
14
22
|
|
23
|
+
Change Log
|
24
|
+
----------
|
25
|
+
before upgrading to new version pls read:
|
26
|
+
[Change Log](https://github.com/ondrejbartas/sidekiq-cron/raw/master/Changes.md)
|
15
27
|
|
16
28
|
Installation
|
17
29
|
------------
|
@@ -20,7 +32,7 @@ Installation
|
|
20
32
|
|
21
33
|
or add to your Gemfile
|
22
34
|
|
23
|
-
gem "sidekiq-cron", "~> 0.
|
35
|
+
gem "sidekiq-cron", "~> 0.3.0"
|
24
36
|
|
25
37
|
|
26
38
|
Getting Started
|
@@ -42,6 +54,35 @@ _Job properties_:
|
|
42
54
|
}
|
43
55
|
```
|
44
56
|
|
57
|
+
### Time, cron and sidekiq-cron
|
58
|
+
|
59
|
+
Cron line is allways evaluated against UTC time. So if you are Prague (timezone +02:00) and you want job to be qneueued at 8:30 AM
|
60
|
+
You will need to adjust cronline to `30 6 * * *`.
|
61
|
+
|
62
|
+
### What objects/classes can be scheduled
|
63
|
+
#### Sidekiq Worker
|
64
|
+
In example we were using: `HardWorker` which loooks like:
|
65
|
+
```ruby
|
66
|
+
class HardWorker
|
67
|
+
include Sidekiq::Worker
|
68
|
+
def perform(*args)
|
69
|
+
# do something
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
#### Active Job Worker
|
75
|
+
You can schedule: `ExampleJob` which loooks like:
|
76
|
+
```ruby
|
77
|
+
class ExampleJob < ActiveJob::Base
|
78
|
+
queue_as :default
|
79
|
+
|
80
|
+
def perform(*args)
|
81
|
+
# Do something
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
45
86
|
#### Adding Cron job:
|
46
87
|
```ruby
|
47
88
|
|
@@ -111,9 +152,16 @@ array = [
|
|
111
152
|
Sidekiq::Cron::Job.load_from_array array
|
112
153
|
```
|
113
154
|
|
155
|
+
Bang methods will remove jobs that are not present in given hash/array
|
156
|
+
updates jobs with same names and create new ones.
|
157
|
+
```ruby
|
158
|
+
Sidekiq::Cron::Job#load_from_hash! hash
|
159
|
+
Sidekiq::Cron::Job#load_from_array! array
|
160
|
+
```
|
161
|
+
|
114
162
|
or from YML (same notation as Resque-scheduler)
|
115
163
|
```yaml
|
116
|
-
#config/
|
164
|
+
#config/schedule.yml
|
117
165
|
|
118
166
|
my_first_job:
|
119
167
|
cron: "*/5 * * * *"
|
@@ -124,7 +172,7 @@ second_job:
|
|
124
172
|
cron: "*/30 * * * *"
|
125
173
|
class: "HardWorker"
|
126
174
|
queue: hard_worker_long
|
127
|
-
args:
|
175
|
+
args:
|
128
176
|
hard: "stuff"
|
129
177
|
```
|
130
178
|
|
@@ -137,8 +185,6 @@ if File.exists?(schedule_file)
|
|
137
185
|
end
|
138
186
|
```
|
139
187
|
|
140
|
-
|
141
|
-
|
142
188
|
#### Finding jobs
|
143
189
|
```ruby
|
144
190
|
#return array of all jobs
|
@@ -191,10 +237,38 @@ Just start sidekiq workers by:
|
|
191
237
|
### Web Ui for Cron Jobs
|
192
238
|
|
193
239
|
If you are using sidekiq web ui and you would like to add cron josb to web too,
|
194
|
-
add `require 'sidekiq
|
240
|
+
add `require 'sidekiq/cron/web'` after `require 'sidekiq/web'`.
|
195
241
|
By this you will get:
|
196
242
|

|
197
243
|
|
244
|
+
### Forking Processes
|
245
|
+
|
246
|
+
If you're using a forking web server like Unicorn you may run into an issue where the Redis connection is used
|
247
|
+
before the process forks, causing the following exception
|
248
|
+
|
249
|
+
Redis::InheritedError: Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.
|
250
|
+
|
251
|
+
to occcur. To avoid this, wrap your job creation in the a call to `Sidekiq.configure_server`:
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
Sidekiq.configure_server do |config|
|
255
|
+
schedule_file = "config/schedule.yml"
|
256
|
+
|
257
|
+
if File.exists?(schedule_file)
|
258
|
+
Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
```
|
262
|
+
|
263
|
+
Note that this API is only available in Sidekiq 3.x.x.
|
264
|
+
|
265
|
+
## Under the hood
|
266
|
+
|
267
|
+
When you start sidekiq process it starts one thread with Sidekiq::Poller instance, which perform adding of scheduled jobs to queues, retryes etc.
|
268
|
+
|
269
|
+
Sidekiq-Cron add itself into this start procedure and start another thread with Sidekiq::Cron::Poler which checks all enabled sidekiq cron jobs evry 10 seconds,
|
270
|
+
if they should be added to queue (their cronline matches time of check).
|
271
|
+
|
198
272
|
|
199
273
|
|
200
274
|
## Contributing to sidekiq-cron
|
data/Rakefile
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
4
5
|
require 'bundler'
|
6
|
+
|
5
7
|
begin
|
6
8
|
Bundler.setup(:default, :development)
|
7
9
|
rescue Bundler::BundlerError => e
|
@@ -58,4 +60,4 @@ namespace :test do
|
|
58
60
|
t.warning = false
|
59
61
|
t.verbose = false
|
60
62
|
end
|
61
|
-
end
|
63
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/sidekiq/cron.rb
CHANGED
@@ -1,14 +1,8 @@
|
|
1
|
-
begin
|
2
|
-
require "sidekiq/web"
|
3
|
-
rescue LoadError
|
4
|
-
# client-only usage
|
5
|
-
end
|
6
|
-
|
7
1
|
require "sidekiq/cron/job"
|
8
|
-
require "sidekiq/cron/web_extension"
|
9
2
|
|
10
3
|
#require poller only if celluloid is defined
|
11
4
|
if defined?(Celluloid)
|
5
|
+
require 'celluloid/autostart'
|
12
6
|
require "sidekiq/cron/poller"
|
13
7
|
require "sidekiq/cron/launcher"
|
14
8
|
end
|
@@ -17,14 +11,3 @@ module Sidekiq
|
|
17
11
|
module Cron
|
18
12
|
end
|
19
13
|
end
|
20
|
-
|
21
|
-
if defined?(Sidekiq::Web)
|
22
|
-
Sidekiq::Web.register Sidekiq::Cron::WebExtension
|
23
|
-
|
24
|
-
if Sidekiq::Web.tabs.is_a?(Array)
|
25
|
-
# For sidekiq < 2.5
|
26
|
-
Sidekiq::Web.tabs << "cron"
|
27
|
-
else
|
28
|
-
Sidekiq::Web.tabs["Cron"] = "cron"
|
29
|
-
end
|
30
|
-
end
|
data/lib/sidekiq/cron/job.rb
CHANGED
@@ -11,7 +11,7 @@ module Sidekiq
|
|
11
11
|
extend Util
|
12
12
|
|
13
13
|
#how long we would like to store informations about previous enqueues
|
14
|
-
REMEMBER_THRESHOLD = 24 * 60 * 60
|
14
|
+
REMEMBER_THRESHOLD = 24 * 60 * 60
|
15
15
|
|
16
16
|
#crucial part of whole enquing job
|
17
17
|
def should_enque? time
|
@@ -45,12 +45,36 @@ module Sidekiq
|
|
45
45
|
def enque! time = Time.now
|
46
46
|
@last_enqueue_time = time
|
47
47
|
|
48
|
-
|
48
|
+
if defined?(ActiveJob::Base) && @klass.to_s.constantize < ActiveJob::Base
|
49
|
+
Sidekiq::Client.push(active_job_message)
|
50
|
+
else
|
51
|
+
Sidekiq::Client.push(sidekiq_worker_message)
|
52
|
+
end
|
49
53
|
|
50
54
|
save
|
51
55
|
logger.debug { "enqueued #{@name}: #{@message}" }
|
52
56
|
end
|
53
57
|
|
58
|
+
# siodekiq worker message
|
59
|
+
def sidekiq_worker_message
|
60
|
+
@message.is_a?(String) ? Sidekiq.load_json(@message) : @message
|
61
|
+
end
|
62
|
+
|
63
|
+
# active job has different structure how it is loading data from sidekiq
|
64
|
+
# queue, it createaswrapper arround job
|
65
|
+
def active_job_message
|
66
|
+
{
|
67
|
+
'class' => 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper',
|
68
|
+
'queue' => @queue,
|
69
|
+
'args' => [{
|
70
|
+
'job_class' => @klass,
|
71
|
+
'job_id' => SecureRandom.uuid,
|
72
|
+
'queue_name' => @queue,
|
73
|
+
'arguments' => @args
|
74
|
+
}]
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
54
78
|
# load cron jobs from Hash
|
55
79
|
# input structure should look like:
|
56
80
|
# {
|
@@ -73,6 +97,12 @@ module Sidekiq
|
|
73
97
|
load_from_array array
|
74
98
|
end
|
75
99
|
|
100
|
+
# like to {#load_from_hash}
|
101
|
+
# If exists old jobs in redis but removed from args, destroy old jobs
|
102
|
+
def self.load_from_hash! hash
|
103
|
+
destroy_removed_jobs(hash.keys)
|
104
|
+
load_from_hash(hash)
|
105
|
+
end
|
76
106
|
|
77
107
|
# load cron jobs from Array
|
78
108
|
# input structure should look like:
|
@@ -99,6 +129,13 @@ module Sidekiq
|
|
99
129
|
errors
|
100
130
|
end
|
101
131
|
|
132
|
+
# like to {#load_from_array}
|
133
|
+
# If exists old jobs in redis but removed from args, destroy old jobs
|
134
|
+
def self.load_from_array! array
|
135
|
+
job_names = array.map { |job| job["name"] }
|
136
|
+
destroy_removed_jobs(job_names)
|
137
|
+
load_from_array(array)
|
138
|
+
end
|
102
139
|
|
103
140
|
# get all cron jobs
|
104
141
|
def self.all
|
@@ -194,7 +231,7 @@ module Sidekiq
|
|
194
231
|
when String
|
195
232
|
begin
|
196
233
|
@klass.constantize.get_sidekiq_options.merge(message_data)
|
197
|
-
rescue
|
234
|
+
rescue
|
198
235
|
#Unknown class
|
199
236
|
message_data.merge("queue"=>"default")
|
200
237
|
end
|
@@ -203,7 +240,11 @@ module Sidekiq
|
|
203
240
|
|
204
241
|
#override queue if setted in config
|
205
242
|
#only if message is hash - can be string (dumped JSON)
|
206
|
-
|
243
|
+
if args['queue']
|
244
|
+
@queue = message_data['queue'] = args['queue']
|
245
|
+
else
|
246
|
+
@queue = message_data['queue'] || default
|
247
|
+
end
|
207
248
|
|
208
249
|
#dump message as json
|
209
250
|
@message = message_data
|
@@ -211,7 +252,7 @@ module Sidekiq
|
|
211
252
|
|
212
253
|
end
|
213
254
|
|
214
|
-
def status
|
255
|
+
def status
|
215
256
|
@status
|
216
257
|
end
|
217
258
|
|
@@ -258,7 +299,7 @@ module Sidekiq
|
|
258
299
|
}
|
259
300
|
end
|
260
301
|
|
261
|
-
def errors
|
302
|
+
def errors
|
262
303
|
@errors ||= []
|
263
304
|
end
|
264
305
|
|
@@ -268,9 +309,9 @@ module Sidekiq
|
|
268
309
|
|
269
310
|
errors << "'name' must be set" if @name.nil? || @name.size == 0
|
270
311
|
if @cron.nil? || @cron.size == 0
|
271
|
-
errors << "'cron' must be set"
|
312
|
+
errors << "'cron' must be set"
|
272
313
|
else
|
273
|
-
begin
|
314
|
+
begin
|
274
315
|
cron = Rufus::Scheduler::CronLine.new(@cron)
|
275
316
|
cron.next_time(Time.now)
|
276
317
|
rescue Exception => e
|
@@ -283,11 +324,21 @@ module Sidekiq
|
|
283
324
|
end
|
284
325
|
end
|
285
326
|
|
286
|
-
errors << "'klass' (or class) must be set"
|
327
|
+
errors << "'klass' (or class) must be set" unless klass_valid
|
287
328
|
|
288
329
|
!errors.any?
|
289
330
|
end
|
290
331
|
|
332
|
+
def klass_valid
|
333
|
+
case @klass
|
334
|
+
when Class
|
335
|
+
true
|
336
|
+
when String
|
337
|
+
@klass.size > 0
|
338
|
+
else
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
291
342
|
# add job to cron jobs
|
292
343
|
# input:
|
293
344
|
# name: (string) - name of job
|
@@ -315,7 +366,7 @@ module Sidekiq
|
|
315
366
|
end
|
316
367
|
logger.info { "Cron Jobs - add job with name: #{@name}" }
|
317
368
|
end
|
318
|
-
|
369
|
+
|
319
370
|
# remove job from cron jobs by name
|
320
371
|
# input:
|
321
372
|
# first arg: name (string) - name of job (must be same - case sensitive)
|
@@ -323,7 +374,7 @@ module Sidekiq
|
|
323
374
|
Sidekiq.redis do |conn|
|
324
375
|
#delete from set
|
325
376
|
conn.srem self.class.jobs_key, redis_key
|
326
|
-
|
377
|
+
|
327
378
|
#delete runned timestamps
|
328
379
|
conn.del job_enqueued_key
|
329
380
|
|
@@ -341,6 +392,14 @@ module Sidekiq
|
|
341
392
|
logger.info { "Cron Jobs - deleted all jobs" }
|
342
393
|
end
|
343
394
|
|
395
|
+
# remove "removed jobs" between current jobs and new jobs
|
396
|
+
def self.destroy_removed_jobs new_job_names
|
397
|
+
current_job_names = Sidekiq::Cron::Job.all.map(&:name)
|
398
|
+
removed_job_names = current_job_names - new_job_names
|
399
|
+
removed_job_names.each { |j| Sidekiq::Cron::Job.destroy(j) }
|
400
|
+
removed_job_names
|
401
|
+
end
|
402
|
+
|
344
403
|
# Parse cron specification '* * * * *' and returns
|
345
404
|
# time when last run should be performed
|
346
405
|
def last_time now = Time.now
|
@@ -372,7 +431,7 @@ module Sidekiq
|
|
372
431
|
def not_enqueued_after?(time)
|
373
432
|
@last_enqueue_time.nil? || @last_enqueue_time < last_time(time)
|
374
433
|
end
|
375
|
-
|
434
|
+
|
376
435
|
# Try parsing inbound args into an array.
|
377
436
|
# args from Redis will be encoded JSON;
|
378
437
|
# try to load JSON, then failover
|