resque-admin-scheduler 1.0.2

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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS.md +81 -0
  3. data/CHANGELOG.md +456 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/CONTRIBUTING.md +6 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +23 -0
  8. data/README.md +691 -0
  9. data/Rakefile +21 -0
  10. data/exe/resque-scheduler +5 -0
  11. data/lib/resque/scheduler/cli.rb +147 -0
  12. data/lib/resque/scheduler/configuration.rb +73 -0
  13. data/lib/resque/scheduler/delaying_extensions.rb +324 -0
  14. data/lib/resque/scheduler/env.rb +89 -0
  15. data/lib/resque/scheduler/extension.rb +13 -0
  16. data/lib/resque/scheduler/failure_handler.rb +11 -0
  17. data/lib/resque/scheduler/lock/base.rb +61 -0
  18. data/lib/resque/scheduler/lock/basic.rb +27 -0
  19. data/lib/resque/scheduler/lock/resilient.rb +78 -0
  20. data/lib/resque/scheduler/lock.rb +4 -0
  21. data/lib/resque/scheduler/locking.rb +104 -0
  22. data/lib/resque/scheduler/logger_builder.rb +72 -0
  23. data/lib/resque/scheduler/plugin.rb +31 -0
  24. data/lib/resque/scheduler/scheduling_extensions.rb +141 -0
  25. data/lib/resque/scheduler/server/views/delayed.erb +63 -0
  26. data/lib/resque/scheduler/server/views/delayed_schedules.erb +20 -0
  27. data/lib/resque/scheduler/server/views/delayed_timestamp.erb +26 -0
  28. data/lib/resque/scheduler/server/views/requeue-params.erb +23 -0
  29. data/lib/resque/scheduler/server/views/scheduler.erb +58 -0
  30. data/lib/resque/scheduler/server/views/search.erb +72 -0
  31. data/lib/resque/scheduler/server/views/search_form.erb +8 -0
  32. data/lib/resque/scheduler/server.rb +268 -0
  33. data/lib/resque/scheduler/signal_handling.rb +40 -0
  34. data/lib/resque/scheduler/tasks.rb +25 -0
  35. data/lib/resque/scheduler/util.rb +39 -0
  36. data/lib/resque/scheduler/version.rb +7 -0
  37. data/lib/resque/scheduler.rb +447 -0
  38. data/lib/resque-scheduler.rb +4 -0
  39. data/resque-scheduler.gemspec +60 -0
  40. metadata +330 -0
data/README.md ADDED
@@ -0,0 +1,691 @@
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)