resque-admin-scheduler 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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)