resque-admin-scheduler 1.0.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/migrate_to_timestamps_set.rb +16 -0
- data/exe/resque-scheduler +2 -2
- data/lib/resque/scheduler/cli.rb +6 -6
- data/lib/resque/scheduler/delaying_extensions.rb +14 -14
- data/lib/resque/scheduler/env.rb +7 -7
- data/lib/resque/scheduler/lock.rb +1 -1
- data/lib/resque/scheduler/locking.rb +7 -7
- data/lib/resque/scheduler/logger_builder.rb +4 -4
- data/lib/resque/scheduler/scheduling_extensions.rb +4 -4
- data/lib/resque/scheduler/server/views/delayed.erb +4 -4
- data/lib/resque/scheduler/server/views/search.erb +1 -1
- data/lib/resque/scheduler/server.rb +37 -37
- data/lib/resque/scheduler/signal_handling.rb +1 -1
- data/lib/resque/scheduler/tasks.rb +6 -6
- data/lib/resque/scheduler/util.rb +4 -4
- data/lib/resque/scheduler_admin/cli.rb +147 -0
- data/lib/resque/scheduler_admin/delaying_extensions.rb +324 -0
- data/lib/resque/scheduler_admin/env.rb +89 -0
- data/lib/resque/scheduler_admin/lock.rb +4 -0
- data/lib/resque/scheduler_admin/locking.rb +104 -0
- data/lib/resque/scheduler_admin/logger_builder.rb +72 -0
- data/lib/resque/scheduler_admin/scheduling_extensions.rb +141 -0
- data/lib/resque/scheduler_admin/server/views/delayed.erb +63 -0
- data/lib/resque/scheduler_admin/server/views/search.erb +72 -0
- data/lib/resque/scheduler_admin/signal_handling.rb +40 -0
- data/lib/resque/scheduler_admin/tasks.rb +25 -0
- data/lib/resque/scheduler_admin/util.rb +39 -0
- data/lib/resque-admin-scheduler.rb +4 -0
- data/lib/resque_admin/scheduler/cli.rb +147 -0
- data/lib/{resque → resque_admin}/scheduler/configuration.rb +1 -1
- data/lib/resque_admin/scheduler/delaying_extensions.rb +324 -0
- data/lib/resque_admin/scheduler/env.rb +89 -0
- data/lib/{resque → resque_admin}/scheduler/extension.rb +1 -1
- data/lib/{resque → resque_admin}/scheduler/failure_handler.rb +2 -2
- data/lib/{resque → resque_admin}/scheduler/lock/base.rb +3 -3
- data/lib/{resque → resque_admin}/scheduler/lock/basic.rb +4 -4
- data/lib/{resque → resque_admin}/scheduler/lock/resilient.rb +4 -4
- data/lib/resque_admin/scheduler/lock.rb +4 -0
- data/lib/resque_admin/scheduler/locking.rb +104 -0
- data/lib/resque_admin/scheduler/logger_builder.rb +72 -0
- data/lib/{resque → resque_admin}/scheduler/plugin.rb +1 -1
- data/lib/resque_admin/scheduler/scheduling_extensions.rb +141 -0
- data/lib/resque_admin/scheduler/server/views/delayed.erb +63 -0
- data/lib/{resque → resque_admin}/scheduler/server/views/delayed_schedules.erb +0 -0
- data/lib/{resque → resque_admin}/scheduler/server/views/delayed_timestamp.erb +2 -2
- data/lib/{resque → resque_admin}/scheduler/server/views/requeue-params.erb +0 -0
- data/lib/{resque → resque_admin}/scheduler/server/views/scheduler.erb +7 -7
- data/lib/resque_admin/scheduler/server/views/search.erb +72 -0
- data/lib/{resque → resque_admin}/scheduler/server/views/search_form.erb +0 -0
- data/lib/resque_admin/scheduler/server.rb +268 -0
- data/lib/resque_admin/scheduler/signal_handling.rb +40 -0
- data/lib/resque_admin/scheduler/tasks.rb +25 -0
- data/lib/resque_admin/scheduler/util.rb +39 -0
- data/lib/{resque → resque_admin}/scheduler/version.rb +2 -2
- data/lib/{resque → resque_admin}/scheduler.rb +44 -44
- data/tasks/resque_admin_scheduler.rake +2 -0
- metadata +47 -85
- data/AUTHORS.md +0 -81
- data/CHANGELOG.md +0 -456
- data/CODE_OF_CONDUCT.md +0 -74
- data/CONTRIBUTING.md +0 -6
- data/Gemfile +0 -4
- data/LICENSE +0 -23
- data/README.md +0 -691
- data/Rakefile +0 -21
- data/lib/resque-scheduler.rb +0 -4
- data/resque-scheduler.gemspec +0 -60
data/README.md
DELETED
@@ -1,691 +0,0 @@
|
|
1
|
-
resque-scheduler
|
2
|
-
================
|
3
|
-
|
4
|
-
|
5
|
-
[![Dependency Status](https://gemnasium.com/badges/github.com/resque/resque-scheduler.svg)](https://gemnasium.com/github.com/resque/resque-scheduler)
|
6
|
-
[![Gem Version](https://badge.fury.io/rb/resque-scheduler.svg)](https://badge.fury.io/rb/resque-scheduler)
|
7
|
-
[![Build Status](https://travis-ci.org/resque/resque-scheduler.svg?branch=master)](https://travis-ci.org/resque/resque-scheduler)
|
8
|
-
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/sxvf2086v5j0absb/branch/master?svg=true)](https://ci.appveyor.com/project/resque/resque-scheduler/branch/master)
|
9
|
-
[![Code Climate](https://codeclimate.com/github/resque/resque-scheduler/badges/gpa.svg)](https://codeclimate.com/github/resque/resque-scheduler)
|
10
|
-
|
11
|
-
### Description
|
12
|
-
|
13
|
-
Resque-scheduler is an extension to [Resque](http://github.com/resque/resque)
|
14
|
-
that adds support for queueing items in the future.
|
15
|
-
|
16
|
-
Job scheduling is supported in two different way: Recurring (scheduled) and
|
17
|
-
Delayed.
|
18
|
-
|
19
|
-
Scheduled jobs are like cron jobs, recurring on a regular basis. Delayed
|
20
|
-
jobs are resque jobs that you want to run at some point in the future.
|
21
|
-
The syntax is pretty explanatory:
|
22
|
-
|
23
|
-
```ruby
|
24
|
-
Resque.enqueue_in(5.days, SendFollowupEmail, argument) # runs a job in 5 days, calling SendFollowupEmail.perform(argument)
|
25
|
-
# or
|
26
|
-
Resque.enqueue_at(5.days.from_now, SomeJob, argument) # runs a job at a specific time, calling SomeJob.perform(argument)
|
27
|
-
```
|
28
|
-
|
29
|
-
### Documentation
|
30
|
-
|
31
|
-
This `README` covers what most people need to know. If you're looking
|
32
|
-
for details on individual methods, you might want to try the
|
33
|
-
[rdoc](http://rdoc.info/github/resque/resque-scheduler/master/frames).
|
34
|
-
|
35
|
-
### Installation
|
36
|
-
|
37
|
-
To install:
|
38
|
-
|
39
|
-
gem install resque-scheduler
|
40
|
-
|
41
|
-
If you use a Gemfile:
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
gem 'resque-scheduler'
|
45
|
-
```
|
46
|
-
|
47
|
-
Adding the resque:scheduler rake task:
|
48
|
-
|
49
|
-
```ruby
|
50
|
-
require 'resque/scheduler/tasks'
|
51
|
-
```
|
52
|
-
|
53
|
-
### Rake integration
|
54
|
-
|
55
|
-
By default, `resque-scheduler` depends on the "resque:setup" rake task.
|
56
|
-
Since you probably already have this task, lets just put our
|
57
|
-
configuration there. `resque-scheduler` pretty much needs to know
|
58
|
-
everything `resque` needs to know.
|
59
|
-
|
60
|
-
```ruby
|
61
|
-
# Resque tasks
|
62
|
-
require 'resque/tasks'
|
63
|
-
require 'resque/scheduler/tasks'
|
64
|
-
|
65
|
-
namespace :resque do
|
66
|
-
task :setup do
|
67
|
-
require 'resque'
|
68
|
-
|
69
|
-
# you probably already have this somewhere
|
70
|
-
Resque.redis = 'localhost:6379'
|
71
|
-
end
|
72
|
-
|
73
|
-
task :setup_schedule => :setup do
|
74
|
-
require 'resque-scheduler'
|
75
|
-
|
76
|
-
# If you want to be able to dynamically change the schedule,
|
77
|
-
# uncomment this line. A dynamic schedule can be updated via the
|
78
|
-
# Resque::Scheduler.set_schedule (and remove_schedule) methods.
|
79
|
-
# When dynamic is set to true, the scheduler process looks for
|
80
|
-
# schedule changes and applies them on the fly.
|
81
|
-
# Note: This feature is only available in >=2.0.0.
|
82
|
-
# Resque::Scheduler.dynamic = true
|
83
|
-
|
84
|
-
# The schedule doesn't need to be stored in a YAML, it just needs to
|
85
|
-
# be a hash. YAML is usually the easiest.
|
86
|
-
Resque.schedule = YAML.load_file('your_resque_schedule.yml')
|
87
|
-
|
88
|
-
# If your schedule already has +queue+ set for each job, you don't
|
89
|
-
# need to require your jobs. This can be an advantage since it's
|
90
|
-
# less code that resque-scheduler needs to know about. But in a small
|
91
|
-
# project, it's usually easier to just include you job classes here.
|
92
|
-
# So, something like this:
|
93
|
-
require 'jobs'
|
94
|
-
end
|
95
|
-
|
96
|
-
task :scheduler => :setup_schedule
|
97
|
-
end
|
98
|
-
```
|
99
|
-
|
100
|
-
The scheduler rake task is responsible for both queueing items from the
|
101
|
-
schedule and polling the delayed queue for items ready to be pushed on
|
102
|
-
to the work queues. For obvious reasons, this process never exits.
|
103
|
-
|
104
|
-
``` bash
|
105
|
-
rake resque:scheduler
|
106
|
-
```
|
107
|
-
|
108
|
-
or, if you want to load the environment first:
|
109
|
-
|
110
|
-
``` bash
|
111
|
-
rake environment resque:scheduler
|
112
|
-
```
|
113
|
-
|
114
|
-
|
115
|
-
### Standalone Executable
|
116
|
-
|
117
|
-
The scheduler may also be run via a standalone `resque-scheduler`
|
118
|
-
executable, which will be available once the gem is installed.
|
119
|
-
|
120
|
-
``` bash
|
121
|
-
# Get some help
|
122
|
-
resque-scheduler --help
|
123
|
-
```
|
124
|
-
|
125
|
-
The executable accepts options via option flags as well as via
|
126
|
-
[environment variables](#environment-variables).
|
127
|
-
|
128
|
-
### Environment Variables
|
129
|
-
|
130
|
-
Both the Rake task and standalone executable support the following
|
131
|
-
environment variables:
|
132
|
-
|
133
|
-
* `APP_NAME` - Application name used in procline (`$0`) (default empty)
|
134
|
-
* `BACKGROUND` - [Run in the background](#running-in-the-background) if
|
135
|
-
non-empty (via `Process.daemon`, if supported) (default `false`)
|
136
|
-
* `DYNAMIC_SCHEDULE` - Enables [dynamic scheduling](#dynamic-schedules)
|
137
|
-
if non-empty (default `false`)
|
138
|
-
* `RAILS_ENV` - Environment to use in procline (`$0`) (default empty)
|
139
|
-
* `INITIALIZER_PATH` - Path to a Ruby file that will be loaded *before*
|
140
|
-
requiring `resque` and `resque/scheduler` (default empty).
|
141
|
-
* `RESQUE_SCHEDULER_INTERVAL` - Interval in seconds for checking if a
|
142
|
-
scheduled job must run (coerced with `Kernel#Float()`) (default `5`)
|
143
|
-
* `LOGFILE` - Log file name (default empty, meaning `$stdout`)
|
144
|
-
* `LOGFORMAT` - Log output format to use (either `'text'` or `'json'`,
|
145
|
-
default `'text'`)
|
146
|
-
* `PIDFILE` - If non-empty, write process PID to file (default empty)
|
147
|
-
* `QUIET` - Silence most output if non-empty (equivalent to a level of
|
148
|
-
`MonoLogger::FATAL`, default `false`)
|
149
|
-
* `VERBOSE` - Maximize log verbosity if non-empty (equivalent to a level
|
150
|
-
of `MonoLogger::DEBUG`, default `false`)
|
151
|
-
|
152
|
-
|
153
|
-
### Resque Pool integration
|
154
|
-
|
155
|
-
For normal work with the
|
156
|
-
[resque-pool](https://github.com/nevans/resque-pool) gem, add the
|
157
|
-
following task to wherever tasks are kept, such as
|
158
|
-
`./lib/tasks/resque.rake`:
|
159
|
-
|
160
|
-
```ruby
|
161
|
-
task 'resque:pool:setup' do
|
162
|
-
Resque::Pool.after_prefork do |job|
|
163
|
-
Resque.redis.client.reconnect
|
164
|
-
end
|
165
|
-
end
|
166
|
-
```
|
167
|
-
|
168
|
-
|
169
|
-
### Delayed jobs
|
170
|
-
|
171
|
-
Delayed jobs are one-off jobs that you want to be put into a queue at some point
|
172
|
-
in the future. The classic example is sending email:
|
173
|
-
|
174
|
-
```ruby
|
175
|
-
Resque.enqueue_in(
|
176
|
-
5.days,
|
177
|
-
SendFollowUpEmail,
|
178
|
-
user_id: current_user.id
|
179
|
-
)
|
180
|
-
```
|
181
|
-
|
182
|
-
This will store the job for 5 days in the resque delayed queue at which time
|
183
|
-
the scheduler process will pull it from the delayed queue and put it in the
|
184
|
-
appropriate work queue for the given job and it will be processed as soon as
|
185
|
-
a worker is available (just like any other resque job).
|
186
|
-
|
187
|
-
**NOTE**: The job does not fire **exactly** at the time supplied. Rather, once that
|
188
|
-
time is in the past, the job moves from the delayed queue to the actual resque
|
189
|
-
work queue and will be completed as workers are free to process it.
|
190
|
-
|
191
|
-
Also supported is `Resque.enqueue_at` which takes a timestamp to queue the
|
192
|
-
job, and `Resque.enqueue_at_with_queue` which takes both a timestamp and a
|
193
|
-
queue name:
|
194
|
-
|
195
|
-
```ruby
|
196
|
-
Resque.enqueue_at_with_queue(
|
197
|
-
'queue_name',
|
198
|
-
5.days.from_now,
|
199
|
-
SendFollowUpEmail,
|
200
|
-
user_id: current_user.id
|
201
|
-
)
|
202
|
-
```
|
203
|
-
|
204
|
-
The delayed queue is stored in redis and is persisted in the same way the
|
205
|
-
standard resque jobs are persisted (redis writing to disk). Delayed jobs differ
|
206
|
-
from scheduled jobs in that if your scheduler process is down or workers are
|
207
|
-
down when a particular job is supposed to be queue, they will simply "catch up"
|
208
|
-
once they are started again. Jobs are guaranteed to run (provided they make it
|
209
|
-
into the delayed queue) after their given `queue_at` time has passed.
|
210
|
-
|
211
|
-
One other thing to note is that insertion into the delayed queue is O(log(n))
|
212
|
-
since the jobs are stored in a redis sorted set (zset). I can't imagine this
|
213
|
-
being an issue for someone since redis is stupidly fast even at log(n), but full
|
214
|
-
disclosure is always best.
|
215
|
-
|
216
|
-
#### Removing Delayed jobs
|
217
|
-
|
218
|
-
If you have the need to cancel a delayed job, you can do like so:
|
219
|
-
|
220
|
-
```ruby
|
221
|
-
# after you've enqueued a job like:
|
222
|
-
Resque.enqueue_at(5.days.from_now, SendFollowUpEmail, :user_id => current_user.id)
|
223
|
-
# remove the job with exactly the same parameters:
|
224
|
-
Resque.remove_delayed(SendFollowUpEmail, :user_id => current_user.id)
|
225
|
-
```
|
226
|
-
|
227
|
-
If you need to cancel a delayed job based on some matching arguments, but don't wish to specify each argument from when the job was created, you can do like so:
|
228
|
-
|
229
|
-
``` ruby
|
230
|
-
# after you've enqueued a job like:
|
231
|
-
Resque.enqueue_at(5.days.from_now, SendFollowUpEmail, :account_id => current_account.id, :user_id => current_user.id)
|
232
|
-
# remove jobs matching just the account:
|
233
|
-
Resque.remove_delayed_selection { |args| args[0]['account_id'] == current_account.id }
|
234
|
-
# or remove jobs matching just the user:
|
235
|
-
Resque.remove_delayed_selection { |args| args[0]['user_id'] == current_user.id }
|
236
|
-
```
|
237
|
-
|
238
|
-
If you need to enqueue immediately a delayed job based on some matching arguments, but don't wish to specify each argument from when the job was created, you can do like so:
|
239
|
-
|
240
|
-
``` ruby
|
241
|
-
# after you've enqueued a job like:
|
242
|
-
Resque.enqueue_at(5.days.from_now, SendFollowUpEmail, :account_id => current_account.id, :user_id => current_user.id)
|
243
|
-
# enqueue immediately jobs matching just the account:
|
244
|
-
Resque.enqueue_delayed_selection { |args| args[0]['account_id'] == current_account.id }
|
245
|
-
# or enqueue immediately jobs matching just the user:
|
246
|
-
Resque.enqueue_delayed_selection { |args| args[0]['user_id'] == current_user.id }
|
247
|
-
```
|
248
|
-
|
249
|
-
### Scheduled Jobs (Recurring Jobs)
|
250
|
-
|
251
|
-
Scheduled (or recurring) jobs are logically no different than a standard cron
|
252
|
-
job. They are jobs that run based on a schedule which can be static or dynamic.
|
253
|
-
|
254
|
-
#### Static schedules
|
255
|
-
|
256
|
-
Static schedules are set when `resque-scheduler` starts by passing a schedule file
|
257
|
-
to `resque-scheduler` initialization like this (see *Installation* above for a more complete example):
|
258
|
-
|
259
|
-
```ruby
|
260
|
-
Resque.schedule = YAML.load_file('your_resque_schedule.yml')
|
261
|
-
```
|
262
|
-
|
263
|
-
If a static schedule is not set `resque-scheduler` will issue a "Schedule empty!" warning on
|
264
|
-
startup, but despite that warning setting a static schedule is totally optional. It is possible
|
265
|
-
to use only dynamic schedules (see below).
|
266
|
-
|
267
|
-
The schedule file is a list of Resque job classes with arguments and a
|
268
|
-
schedule frequency (in crontab syntax). The schedule is just a hash, but
|
269
|
-
is usually stored in a YAML like this:
|
270
|
-
|
271
|
-
```yaml
|
272
|
-
CancelAbandonedOrders:
|
273
|
-
cron: "*/5 * * * *"
|
274
|
-
|
275
|
-
queue_documents_for_indexing:
|
276
|
-
cron: "0 0 * * *"
|
277
|
-
# you can use rufus-scheduler "every" syntax in place of cron if you prefer
|
278
|
-
# every: 1h
|
279
|
-
# By default the job name (hash key) will be taken as worker class name.
|
280
|
-
# If you want to have a different job name and class name, provide the 'class' option
|
281
|
-
class: "QueueDocuments"
|
282
|
-
queue: high
|
283
|
-
args:
|
284
|
-
description: "This job queues all content for indexing in solr"
|
285
|
-
|
286
|
-
clear_leaderboards_contributors:
|
287
|
-
cron: "30 6 * * 1"
|
288
|
-
class: "ClearLeaderboards"
|
289
|
-
queue: low
|
290
|
-
args: contributors
|
291
|
-
description: "This job resets the weekly leaderboard for contributions"
|
292
|
-
```
|
293
|
-
|
294
|
-
If you would like to setup a job that is executed manually you can configure like this in your YAML file.
|
295
|
-
|
296
|
-
```yaml
|
297
|
-
ImportOrdersManual:
|
298
|
-
description: 'Import Amazon Orders Manual'
|
299
|
-
custom_job_class: 'AmazonMws::ImportOrdersJob'
|
300
|
-
never: "* * * * *"
|
301
|
-
queue: high
|
302
|
-
description: "This is a manual job for importing orders."
|
303
|
-
args:
|
304
|
-
days_in_arrears: 7
|
305
|
-
```
|
306
|
-
|
307
|
-
The queue value is optional, but if left unspecified resque-scheduler will
|
308
|
-
attempt to get the queue from the job class, which means it needs to be
|
309
|
-
defined. If you're getting "uninitialized constant" errors, you probably
|
310
|
-
need to either set the queue in the schedule or require your jobs in your
|
311
|
-
"resque:setup" rake task.
|
312
|
-
|
313
|
-
You can provide options to "every" or "cron" via Array:
|
314
|
-
|
315
|
-
```yaml
|
316
|
-
clear_leaderboards_moderator:
|
317
|
-
every:
|
318
|
-
- "30s"
|
319
|
-
- :first_in: '120s'
|
320
|
-
class: "CheckDaemon"
|
321
|
-
queue: daemons
|
322
|
-
description: "This job will check Daemon every 30 seconds after 120 seconds after start"
|
323
|
-
```
|
324
|
-
|
325
|
-
IMPORTANT: Rufus `every` syntax will calculate jobs scheduling time starting from the moment of deploy,
|
326
|
-
resulting in resetting schedule time on every deploy, so it's probably a good idea to use it only for
|
327
|
-
frequent jobs (like every 10-30 minutes), otherwise - when you use something like `every 20h` and deploy once-twice per day -
|
328
|
-
it will schedule the job for 20 hours from deploy, resulting in a job to never be run.
|
329
|
-
|
330
|
-
**NOTE**: Six parameter cron's are also supported (as they supported by
|
331
|
-
rufus-scheduler which powers the resque-scheduler process). This allows you
|
332
|
-
to schedule jobs per second (ie: `"30 * * * * *"` would fire a job every 30
|
333
|
-
seconds past the minute).
|
334
|
-
|
335
|
-
A big shout out to [rufus-scheduler](http://github.com/jmettraux/rufus-scheduler)
|
336
|
-
for handling the heavy lifting of the actual scheduling engine.
|
337
|
-
|
338
|
-
#### Dynamic schedules
|
339
|
-
|
340
|
-
Dynamic schedules are programmatically set on a running `resque-scheduler`.
|
341
|
-
All [rufus-scheduler](http://github.com/jmettraux/rufus-scheduler) options are supported
|
342
|
-
when setting schedules.
|
343
|
-
|
344
|
-
Dynamic schedules are not enabled by default. To be able to dynamically set schedules, you
|
345
|
-
must pass the following to `resque-scheduler` initialization (see *Installation* above for a more complete example):
|
346
|
-
|
347
|
-
```ruby
|
348
|
-
Resque::Scheduler.dynamic = true
|
349
|
-
```
|
350
|
-
|
351
|
-
**NOTE**: In order to delete dynamic schedules via `resque-web` in the
|
352
|
-
"Schedule" tab, you must include the `Rack::MethodOverride` middleware (in
|
353
|
-
`config.ru` or equivalent).
|
354
|
-
|
355
|
-
Dynamic schedules allow for greater flexibility than static schedules as they can be set,
|
356
|
-
unset or changed without having to restart `resque-scheduler`. You can specify, if the schedule
|
357
|
-
must survive a resque-scheduler restart or not. This is done by setting the `persist` configuration
|
358
|
-
for the schedule: it is a boolean value, if set the schedule will persist a restart. By default,
|
359
|
-
a schedule will not be persisted.
|
360
|
-
|
361
|
-
The job to be scheduled must be a valid Resque job class.
|
362
|
-
|
363
|
-
For example, suppose you have a SendEmail job which sends emails. The `perform` method of the
|
364
|
-
job receives a string argument with the email subject. To run the SendEmail job every hour
|
365
|
-
starting five minutes from now, you can do:
|
366
|
-
|
367
|
-
```ruby
|
368
|
-
name = 'send_emails'
|
369
|
-
config = {}
|
370
|
-
config[:class] = 'SendEmail'
|
371
|
-
config[:args] = 'POC email subject'
|
372
|
-
config[:every] = ['1h', {first_in: 5.minutes}]
|
373
|
-
config[:persist] = true
|
374
|
-
Resque.set_schedule(name, config)
|
375
|
-
```
|
376
|
-
|
377
|
-
Schedules can later be removed by passing their name to the `remove_schedule` method:
|
378
|
-
|
379
|
-
```ruby
|
380
|
-
name = 'send_emails'
|
381
|
-
Resque.remove_schedule(name)
|
382
|
-
```
|
383
|
-
|
384
|
-
Schedule names are unique; i.e. two dynamic schedules cannot have the same name. If `set_schedule` is
|
385
|
-
passed the name of an existing schedule, that schedule is updated. E.g. if after setting the above schedule
|
386
|
-
we want the job to run every day instead of every hour from now on, we can do:
|
387
|
-
|
388
|
-
```ruby
|
389
|
-
name = 'send_emails'
|
390
|
-
config = {}
|
391
|
-
config[:class] = 'SendEmail'
|
392
|
-
config[:args] = 'POC email subject'
|
393
|
-
config[:every] = '1d'
|
394
|
-
Resque.set_schedule(name, config)
|
395
|
-
```
|
396
|
-
|
397
|
-
#### Time zones
|
398
|
-
|
399
|
-
Note that if you use the cron syntax, this will be interpreted as in the server time zone
|
400
|
-
rather than the `config.time_zone` specified in Rails.
|
401
|
-
|
402
|
-
You can explicitly specify the time zone that rufus-scheduler will use:
|
403
|
-
|
404
|
-
```yaml
|
405
|
-
cron: "30 6 * * 1 Europe/Stockholm"
|
406
|
-
```
|
407
|
-
|
408
|
-
Also note that `config.time_zone` in Rails allows for a shorthand (e.g. "Stockholm")
|
409
|
-
that rufus-scheduler does not accept. If you write code to set the scheduler time zone
|
410
|
-
from the `config.time_zone` value, make sure it's the right format, e.g. with:
|
411
|
-
|
412
|
-
```ruby
|
413
|
-
ActiveSupport::TimeZone.find_tzinfo(Rails.configuration.time_zone).name
|
414
|
-
```
|
415
|
-
|
416
|
-
A future version of resque-scheduler may do this for you.
|
417
|
-
|
418
|
-
#### Hooks
|
419
|
-
|
420
|
-
Similar to the `before_enqueue`- and `after_enqueue`-hooks provided in Resque
|
421
|
-
(>= 1.19.1), your jobs can specify one or more of the following hooks:
|
422
|
-
|
423
|
-
* `before_schedule`: Called with the job args before a job is placed on
|
424
|
-
the delayed queue. If the hook returns `false`, the job will not be placed on
|
425
|
-
the queue.
|
426
|
-
* `after_schedule`: Called with the job args after a job is placed on the
|
427
|
-
delayed queue. Any exception raised propagates up to the code with queued the
|
428
|
-
job.
|
429
|
-
* `before_delayed_enqueue`: Called with the job args after the job has been
|
430
|
-
removed from the delayed queue, but not yet put on a normal queue. It is
|
431
|
-
called before `before_enqueue`-hooks, and on the same job instance as the
|
432
|
-
`before_enqueue`-hooks will be invoked on. Return values are ignored.
|
433
|
-
* `on_enqueue_failure`: Called with the job args and the exception that was raised
|
434
|
-
while enqueueing a job to resque or external application fails. Return
|
435
|
-
values are ignored. For example:
|
436
|
-
|
437
|
-
```ruby
|
438
|
-
Resque::Scheduler.failure_handler = ExceptionHandlerClass
|
439
|
-
```
|
440
|
-
|
441
|
-
#### Support for resque-status (and other custom jobs)
|
442
|
-
|
443
|
-
Some Resque extensions like
|
444
|
-
[resque-status](http://github.com/quirkey/resque-status) use custom job
|
445
|
-
classes with a slightly different API signature. Resque-scheduler isn't
|
446
|
-
trying to support all existing and future custom job classes, instead it
|
447
|
-
supports a schedule flag so you can extend your custom class and make it
|
448
|
-
support scheduled job.
|
449
|
-
|
450
|
-
Let's pretend we have a `JobWithStatus` class called `FakeLeaderboard`
|
451
|
-
|
452
|
-
```ruby
|
453
|
-
class FakeLeaderboard < Resque::JobWithStatus
|
454
|
-
def perform
|
455
|
-
# do something and keep track of the status
|
456
|
-
end
|
457
|
-
end
|
458
|
-
```
|
459
|
-
|
460
|
-
And then a schedule:
|
461
|
-
|
462
|
-
```yaml
|
463
|
-
create_fake_leaderboards:
|
464
|
-
cron: "30 6 * * 1"
|
465
|
-
queue: scoring
|
466
|
-
custom_job_class: "FakeLeaderboard"
|
467
|
-
args:
|
468
|
-
rails_env: demo
|
469
|
-
description: "This job will auto-create leaderboards for our online demo and the status will update as the worker makes progress"
|
470
|
-
```
|
471
|
-
|
472
|
-
If your extension doesn't support scheduled job, you would need to extend the
|
473
|
-
custom job class to support the #scheduled method:
|
474
|
-
|
475
|
-
```ruby
|
476
|
-
module Resque
|
477
|
-
class JobWithStatus
|
478
|
-
# Wrapper API to forward a Resque::Job creation API call into
|
479
|
-
# a JobWithStatus call.
|
480
|
-
def self.scheduled(queue, klass, *args)
|
481
|
-
create(*args)
|
482
|
-
end
|
483
|
-
end
|
484
|
-
end
|
485
|
-
```
|
486
|
-
|
487
|
-
### Redundancy and Fail-Over
|
488
|
-
|
489
|
-
*>= 2.0.1 only. Prior to 2.0.1, it is not recommended to run multiple resque-scheduler processes and will result in duplicate jobs.*
|
490
|
-
|
491
|
-
You may want to have resque-scheduler running on multiple machines for
|
492
|
-
redundancy. Electing a master and failover is built in and default. Simply
|
493
|
-
run resque-scheduler on as many machine as you want pointing to the same
|
494
|
-
redis instance and schedule. The scheduler processes will use redis to
|
495
|
-
elect a master process and detect failover when the master dies. Precautions are
|
496
|
-
taken to prevent jobs from potentially being queued twice during failover even
|
497
|
-
when the clocks of the scheduler machines are slightly out of sync (or load affects
|
498
|
-
scheduled job firing time). If you want the gory details, look at Resque::Scheduler::Locking.
|
499
|
-
|
500
|
-
If the scheduler process(es) goes down for whatever reason, the delayed items
|
501
|
-
that should have fired during the outage will fire once the scheduler process
|
502
|
-
is started back up again (regardless of it being on a new machine). Missed
|
503
|
-
scheduled jobs, however, will not fire upon recovery of the scheduler process.
|
504
|
-
Think of scheduled (recurring) jobs as cron jobs - if you stop cron, it doesn't fire
|
505
|
-
missed jobs once it starts back up.
|
506
|
-
|
507
|
-
You might want to share a redis instance amongst multiple Rails applications with different
|
508
|
-
scheduler with different config yaml files. If this is the case, normally, only one will ever
|
509
|
-
run, leading to undesired behaviour. To allow different scheduler configs run at the same time
|
510
|
-
on one redis, you can either namespace your redis connections, or supply an environment variable
|
511
|
-
to split the shared lock key resque-scheduler uses thus:
|
512
|
-
|
513
|
-
``` bash
|
514
|
-
RESQUE_SCHEDULER_MASTER_LOCK_PREFIX=MyApp: rake resque:scheduler
|
515
|
-
```
|
516
|
-
|
517
|
-
### resque-web Additions
|
518
|
-
|
519
|
-
Resque-scheduler also adds to tabs to the resque-web UI. One is for viewing
|
520
|
-
(and manually queueing) the schedule and one is for viewing pending jobs in
|
521
|
-
the delayed queue.
|
522
|
-
|
523
|
-
The Schedule tab:
|
524
|
-
|
525
|
-
![The Schedule Tab](https://f.cloud.github.com/assets/45143/1178456/c99e5568-21b0-11e3-8c57-e1305d0ee8ef.png)
|
526
|
-
|
527
|
-
The Delayed tab:
|
528
|
-
|
529
|
-
![The Delayed Tab](http://img.skitch.com/20100111-ne4fcqtc5emkcuwc5qtais2kwx.jpg)
|
530
|
-
|
531
|
-
#### How do I get the schedule tabs to show up???
|
532
|
-
|
533
|
-
To get these to show up you need to pass a file to `resque-web` to tell it to
|
534
|
-
include the `resque-scheduler` plugin and the resque-schedule server extension
|
535
|
-
to the resque-web sinatra app. Unless you're running redis on localhost, you
|
536
|
-
probably already have this file. It probably looks something like this:
|
537
|
-
|
538
|
-
```ruby
|
539
|
-
require 'resque' # include resque so we can configure it
|
540
|
-
Resque.redis = "redis_server:6379" # tell Resque where redis lives
|
541
|
-
```
|
542
|
-
|
543
|
-
Now, you want to add the following:
|
544
|
-
|
545
|
-
```ruby
|
546
|
-
# This will make the tabs show up.
|
547
|
-
require 'resque-scheduler'
|
548
|
-
require 'resque/scheduler/server'
|
549
|
-
```
|
550
|
-
|
551
|
-
That should make the scheduler tabs show up in `resque-web`.
|
552
|
-
|
553
|
-
#### Changes as of 2.0.0
|
554
|
-
|
555
|
-
As of resque-scheduler 2.0.0, it's no longer necessary to have the resque-web
|
556
|
-
process aware of the schedule because it reads it from redis. But prior to
|
557
|
-
2.0, you'll want to make sure you load the schedule in this file as well.
|
558
|
-
Something like this:
|
559
|
-
|
560
|
-
```ruby
|
561
|
-
Resque.schedule = YAML.load_file(File.join(RAILS_ROOT, 'config/resque_schedule.yml')) # load the schedule
|
562
|
-
```
|
563
|
-
|
564
|
-
Now make sure you're passing that file to resque-web like so:
|
565
|
-
|
566
|
-
resque-web ~/yourapp/config/resque_config.rb
|
567
|
-
|
568
|
-
|
569
|
-
### Running in the background
|
570
|
-
|
571
|
-
(Only supported with ruby >= 1.9). There are scenarios where it's helpful for
|
572
|
-
the resque worker to run itself in the background (usually in combination with
|
573
|
-
PIDFILE). Use the BACKGROUND option so that rake will return as soon as the
|
574
|
-
worker is started.
|
575
|
-
|
576
|
-
$ PIDFILE=./resque-scheduler.pid BACKGROUND=yes \
|
577
|
-
rake resque:scheduler
|
578
|
-
|
579
|
-
|
580
|
-
### Logging
|
581
|
-
|
582
|
-
There are several options to toggle the way scheduler logs its actions. They
|
583
|
-
are toggled by environment variables:
|
584
|
-
|
585
|
-
- `QUIET` will stop logging anything. Completely silent.
|
586
|
-
- `VERBOSE` opposite of 'QUIET'; will log even debug information
|
587
|
-
- `LOGFILE` specifies the file to write logs to. (default standard output)
|
588
|
-
- `LOGFORMAT` specifies either "text" or "json" output format
|
589
|
-
(default "text")
|
590
|
-
|
591
|
-
All of these variables are optional and will be given the following default
|
592
|
-
values:
|
593
|
-
|
594
|
-
```ruby
|
595
|
-
Resque::Scheduler.configure do |c|
|
596
|
-
c.quiet = false
|
597
|
-
c.verbose = false
|
598
|
-
c.logfile = nil # meaning all messages go to $stdout
|
599
|
-
c.logformat = 'text'
|
600
|
-
end
|
601
|
-
```
|
602
|
-
|
603
|
-
### Polling frequency
|
604
|
-
|
605
|
-
You can pass a `RESQUE_SCHEDULER_INTERVAL` option which is an integer or
|
606
|
-
float representing the polling frequency. The default is 5 seconds, but
|
607
|
-
for a semi-active app you may want to use a smaller value.
|
608
|
-
|
609
|
-
$ RESQUE_SCHEDULER_INTERVAL=1 rake resque:scheduler
|
610
|
-
|
611
|
-
**NOTE** This value was previously `INTERVAL` but was renamed to
|
612
|
-
`RESQUE_SCHEDULER_INTERVAL` to avoid clashing with the interval Resque
|
613
|
-
uses for its jobs.
|
614
|
-
|
615
|
-
### Plagiarism alert
|
616
|
-
|
617
|
-
This was intended to be an extension to resque and so resulted in a lot
|
618
|
-
of the code looking very similar to resque, particularly in resque-web
|
619
|
-
and the views. I wanted it to be similar enough that someone familiar
|
620
|
-
with resque could easily work on resque-scheduler.
|
621
|
-
|
622
|
-
### Development
|
623
|
-
|
624
|
-
Working on resque-scheduler requires the following:
|
625
|
-
|
626
|
-
* A relatively modern Ruby interpreter
|
627
|
-
* bundler
|
628
|
-
|
629
|
-
The development setup looks like this, which is roughly the same thing
|
630
|
-
that happens on Travis CI and Appveyor:
|
631
|
-
|
632
|
-
``` bash
|
633
|
-
# Install everything
|
634
|
-
bundle install
|
635
|
-
|
636
|
-
# Make sure tests are green before you change stuff
|
637
|
-
bundle exec rake
|
638
|
-
# Change stuff
|
639
|
-
# Repeat
|
640
|
-
```
|
641
|
-
|
642
|
-
If you have [vagrant](http://www.vagrantup.com) installed, there is a
|
643
|
-
development box available that requires no plugins or external
|
644
|
-
provisioners:
|
645
|
-
|
646
|
-
``` bash
|
647
|
-
vagrant up
|
648
|
-
```
|
649
|
-
|
650
|
-
### Deployment Notes
|
651
|
-
|
652
|
-
It is recommended that a production deployment of `resque-scheduler` be hosted
|
653
|
-
on a dedicated Redis database. While making and managing scheduled tasks,
|
654
|
-
`resque-scheduler` currently scans the entire Redis keyspace, which may cause
|
655
|
-
latency and stability issues if `resque-scheduler` is hosted on a Redis instance
|
656
|
-
storing a large number of keys (such as those written by a different system
|
657
|
-
hosted on the same Redis instance).
|
658
|
-
|
659
|
-
#### Compatibility Notes
|
660
|
-
|
661
|
-
Different versions of the `redis` and `rufus-scheduler` gems are needed
|
662
|
-
depending on your version of `resque-scheduler`. This is typically not a
|
663
|
-
problem with `resque-scheduler` itself, but when mixing dependencies with an
|
664
|
-
existing application.
|
665
|
-
|
666
|
-
This table explains the version requirements for redis gem
|
667
|
-
|
668
|
-
| resque-scheduler | redis gem |
|
669
|
-
|:-----------------|-----------:|
|
670
|
-
| `~> 2.0` | `>= 3.0.0` |
|
671
|
-
| `>= 0.0.1` | `~> 1.3` |
|
672
|
-
|
673
|
-
This table explains the version requirements for rufus-scheduler
|
674
|
-
|
675
|
-
| resque-scheduler | rufus-scheduler |
|
676
|
-
|:-----------------|----------------:|
|
677
|
-
| `~> 4.0` | `~> 3.0` |
|
678
|
-
| `< 4.0` | `~> 2.0` |
|
679
|
-
|
680
|
-
|
681
|
-
### Contributing
|
682
|
-
|
683
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
684
|
-
|
685
|
-
### Authors
|
686
|
-
|
687
|
-
See [AUTHORS.md](AUTHORS.md)
|
688
|
-
|
689
|
-
### License
|
690
|
-
|
691
|
-
See [LICENSE](LICENSE)
|