resque-mongo-scheduler 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .bundle/
2
+ pkg
3
+ nbproject
4
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,47 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ resque-mongo-scheduler (2.0.0.a)
5
+ mongo (>= 1.1)
6
+ resque-mongo (>= 1.11.0)
7
+ rufus-scheduler
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ bson (1.1.5)
13
+ json (1.4.6)
14
+ mocha (0.9.10)
15
+ rake
16
+ mongo (1.1.5)
17
+ bson (>= 1.1.5)
18
+ rack (1.2.1)
19
+ rack-test (0.5.6)
20
+ rack (>= 1.0)
21
+ rake (0.8.7)
22
+ resque-mongo (1.11.0)
23
+ json (~> 1.4.6)
24
+ mongo (>= 0.20)
25
+ sinatra (>= 0.9.2)
26
+ vegas (~> 0.1.2)
27
+ rufus-scheduler (2.0.7)
28
+ tzinfo
29
+ sinatra (1.1.0)
30
+ rack (~> 1.1)
31
+ tilt (~> 1.1)
32
+ tilt (1.1)
33
+ tzinfo (0.3.23)
34
+ vegas (0.1.8)
35
+ rack (>= 1.0.0)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ bundler (>= 1.0.0)
42
+ mocha
43
+ mongo (>= 1.1)
44
+ rack-test
45
+ resque-mongo (>= 1.11.0)
46
+ resque-mongo-scheduler!
47
+ rufus-scheduler
data/HISTORY.md ADDED
@@ -0,0 +1,88 @@
1
+ ## 2.0.0 (???)
2
+
3
+ * Dynamic schedule support (brianjlandau, davidyang)
4
+ * Now depends on redis >=1.3
5
+
6
+ ## 1.9.7 (2010-11-09)
7
+
8
+ * Support for rufus-scheduler "every" syntax (fallwith)
9
+ * Ability to pass a Time to handle_delayed_items for testing/staging (rcarver)
10
+
11
+ ## 1.9.6 (2010-10-08)
12
+
13
+ * Support for custom job classes (like resque-status) (mattetti)
14
+
15
+ ## 1.9.5 (2010-09-09)
16
+
17
+ * Updated scheduler rake task to allow for an alternate setup task
18
+ to avoid loading the entire stack. (chewbranca)
19
+ * Fixed sig issue on win32 (#25)
20
+
21
+ ## 1.9.4 (2010-07-29)
22
+
23
+ * Adding ability to remove jobs from delayed queue (joshsz)
24
+ * Fixing issue #23 (removing .present? reference)
25
+
26
+ ## 1.9.3 (2010-07-07)
27
+
28
+ * Bug fix (#19)
29
+
30
+ ## 1.9.2 (2010-06-16)
31
+
32
+ * Fixing issue with redis gem 2.0.1 and redis server 1.2.6 (dbackeus)
33
+
34
+ ## 1.9.1 (2010-06-04)
35
+
36
+ * Fixing issue with redis server 1.2.6 and redis gem 2.0.1
37
+
38
+ ## 1.9.0 (2010-06-04)
39
+
40
+ * Adding redis 2.0 support (bpo)
41
+
42
+ ## 1.8.2 (2010-06-04)
43
+
44
+ * Adding queue now functionality to delayed timestamps (daviddoan)
45
+
46
+ ## 1.8.1 (2010-05-19)
47
+
48
+ * Adding rails_env for scheduled jobs to support scoping jobs by
49
+ RAILS_ENV (gravis).
50
+ * Fixing ruby 1.8.6 compatibility issue.
51
+ * Adding gemspec for bundler support.
52
+
53
+ ## 1.8.0 (2010-04-14)
54
+
55
+ * Moving version to match corresponding resque version
56
+ * Sorting schedule on Scheduler tab
57
+ * Adding tests for resque-web (gravis)
58
+
59
+ ## 1.0.5 (2010-03-01)
60
+
61
+ * Fixed support for overriding queue from schedule config.
62
+ * Removed resque-web dependency on loading the job classes for "Queue Now",
63
+ provided "queue" is specified in the schedule.
64
+ * The queue is now stored with the job and arguments in the delayed queue so
65
+ there is no longer a need for the scheduler to load job classes to introspect
66
+ the queue.
67
+
68
+ ## 1.0.4 (2010-02-26)
69
+
70
+ * Added support for specifying the queue to put the job onto. This allows for
71
+ you to have one job that can go onto multiple queues and be able to schedule
72
+ jobs without having to load the job classes.
73
+
74
+ ## 1.0.3 (2010-02-11)
75
+
76
+ * Added support for scheduled jobs with empty crons. This is helpful to have
77
+ jobs that you don't want on a schedule, but do want to be able to queue by
78
+ clicking a button.
79
+
80
+ ## 1.0.2 (2010-02-?)
81
+
82
+ * Change Delayed Job tab to display job details if only 1 job exists
83
+ for a given timestamp
84
+
85
+ ## 1.0.1 (2010-01-?)
86
+
87
+ * Bugfix: delayed jobs close together resulted in a 5 second sleep
88
+
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2010 Ben VandenBos
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.markdown ADDED
@@ -0,0 +1,316 @@
1
+ resque-mongo-scheduler
2
+ ======================
3
+
4
+ Resque-mongo-scheduler is a port of [Resque-scheduler](https://github.com/bvandenbos/resque-scheduler)
5
+ that depends on [Resque-mongo](https://github.com/nfo/resque-mongo) instead of [Resque](http://github.com/defunkt/resque).
6
+
7
+ Requires mongo >= 1.6.
8
+
9
+ resque-scheduler
10
+ ===============
11
+
12
+ Resque-scheduler is an extension to [Resque](http://github.com/defunkt/resque)
13
+ that adds support for queueing items in the future.
14
+
15
+ <del>Requires redis >=1.3.</del>
16
+
17
+
18
+ Job scheduling is supported in two different way:
19
+
20
+ ### Recurring (scheduled)
21
+
22
+ Recurring (or scheduled) jobs are logically no different than a standard cron
23
+ job. They are jobs that run based on a fixed schedule which is set at startup.
24
+
25
+ The schedule is a list of Resque worker classes with arguments and a
26
+ schedule frequency (in crontab syntax). The schedule is just a hash, but
27
+ is most likely stored in a YAML like so:
28
+
29
+ queue_documents_for_indexing:
30
+ cron: "0 0 * * *"
31
+ class: QueueDocuments
32
+ args:
33
+ description: "This job queues all content for indexing in solr"
34
+
35
+ clear_leaderboards_contributors:
36
+ cron: "30 6 * * 1"
37
+ class: ClearLeaderboards
38
+ args: contributors
39
+ description: "This job resets the weekly leaderboard for contributions"
40
+
41
+ A queue option can also be specified. Then the job will go onto the specified
42
+ queue if it is available (Even if @queue is specified in the job class). When
43
+ the queue is given it is not necessary for the scheduler to load the class.
44
+
45
+ clear_leaderboards_moderator:
46
+ cron: "30 6 * * 1"
47
+ class: ClearLeaderboards
48
+ queue: scoring
49
+ args: moderators
50
+ description: "This job resets the weekly leaderboard for moderators"
51
+
52
+ And then set the schedule wherever you configure Resque, like so:
53
+
54
+ require 'resque_scheduler'
55
+ Resque.schedule = YAML.load_file(File.join(File.dirname(__FILE__), '../resque_schedule.yml'))
56
+
57
+ Keep in mind, scheduled jobs behave like crons: if your scheduler process (more
58
+ on that later) is not running when a particular job is supposed to be queued,
59
+ it will NOT be ran later when the scheduler process is started back up. In that
60
+ sense, you can sort of think of the scheduler process as crond. Delayed jobs,
61
+ however, are different.
62
+
63
+ A big shout out to [rufus-scheduler](http://github.com/jmettraux/rufus-scheduler)
64
+ for handling the heavy lifting of the actual scheduling engine.
65
+
66
+ ### Delayed jobs
67
+
68
+ Delayed jobs are one-off jobs that you want to be put into a queue at some point
69
+ in the future. The classic example is sending email:
70
+
71
+ Resque.enqueue_at(5.days.from_now, SendFollowUpEmail, :user_id => current_user.id)
72
+
73
+ This will store the job for 5 days in the resque delayed queue at which time the
74
+ scheduler process will pull it from the delayed queue and put it in the
75
+ appropriate work queue for the given job and it will be processed as soon as
76
+ a worker is available.
77
+
78
+ NOTE: The job does not fire **exactly** at the time supplied. Rather, once that
79
+ time is in the past, the job moves from the delayed queue to the actual resque
80
+ work queue and will be completed as workers as free to process it.
81
+
82
+ Also supported is `Resque.enqueue_in` which takes an amount of time in seconds
83
+ in which to queue the job.
84
+
85
+ The delayed queue is stored in redis and is persisted in the same way the
86
+ standard resque jobs are persisted (redis writing to disk). Delayed jobs differ
87
+ from scheduled jobs in that if your scheduler process is down or workers are
88
+ down when a particular job is supposed to be queue, they will simply "catch up"
89
+ once they are started again. Jobs are guaranteed to run (provided they make it
90
+ into the delayed queue) after their given queue_at time has passed.
91
+
92
+ One other thing to note is that insertion into the delayed queue is O(log(n))
93
+ since the jobs are stored in a redis sorted set (zset). I can't imagine this
94
+ being an issue for someone since redis is stupidly fast even at log(n), but full
95
+ disclosure is always best.
96
+
97
+ *Removing Delayed jobs*
98
+
99
+ If you have the need to cancel a delayed job, you can do so thusly:
100
+
101
+ # after you've enqueued a job like:
102
+ Resque.enqueue_at(5.days.from_now, SendFollowUpEmail, :user_id => current_user.id)
103
+ # remove the job with exactly the same parameters:
104
+ Resque.remove_delayed(SendFollowUpEmail, :user_id => current_user.id)
105
+
106
+
107
+ ### Dynamic Schedules
108
+
109
+ If needed you can also have recurring jobs (scheduled) that are dynamically
110
+ defined and updated inside of your application. A good example is if you want
111
+ to allow users to configured when a report is automatically generated. This
112
+ can be completed by loading the schedule initially wherever you configure
113
+ Resque and setting `Resque::Scheduler.dynamic` to `true`. Then subsequently
114
+ updating the "`schedules`" key in redis, namespaced to the Resque namespace.
115
+ The "`schedules`" key is expected to be a redis hash data type, where the key
116
+ is the name of the schedule and the value is a JSON encoded hash of the
117
+ schedule configuration. There are methods on Resque to make this easy (see
118
+ below).
119
+
120
+ When the scheduler loops it will look for differences between the existing
121
+ schedule and the current schedule in redis. If there are differences it will
122
+ make the necessary changes to the running schedule. The schedule names that
123
+ need to be changed are stored in the `schedules_changed` set in redis.
124
+
125
+ To force the scheduler to reload the schedule you just send it the `USR2`
126
+ signal. This will force a complete schedule reload (unscheduling and
127
+ rescheduling everything).
128
+
129
+ To add/update, delete, and retrieve individual schedule items you should
130
+ use the provided API methods:
131
+
132
+ * `Resque.set_schedule(name, config)`
133
+ * `Resque.get_schedule(name)`
134
+ * `Resque.remove_schedule(name)`
135
+
136
+ For example:
137
+
138
+ Resque.set_schedule("create_fake_leaderboards", {
139
+ :cron => "30 6 * * 1",
140
+ :class => "CreateFakeLeaderboards",
141
+ :queue => scoring
142
+ })
143
+
144
+ In this way, it's possible to completely configure your scheduled jobs from
145
+ inside your app if you so desire.
146
+
147
+ ### Support for customized Job classes
148
+
149
+ Some Resque extensions like
150
+ [resque-status](http://github.com/quirkey/resque-status) use custom job
151
+ classes with a slightly different API signature. Resque-scheduler isn't
152
+ trying to support all existing and future custom job classes, instead it
153
+ supports a schedule flag so you can extend your custom class and make it
154
+ support scheduled job.
155
+
156
+ Let's pretend we have a JobWithStatus class called FakeLeaderboard
157
+
158
+ class FakeLeaderboard < Resque::JobWithStatus
159
+ def perform
160
+ # do something and keep track of the status
161
+ end
162
+ end
163
+
164
+ create_fake_leaderboards:
165
+ cron: "30 6 * * 1"
166
+ queue: scoring
167
+ custom_job_class: FakeLeaderboard
168
+ args:
169
+ rails_env: demo
170
+ description: "This job will auto-create leaderboards for our online demo and the status will update as the worker makes progress"
171
+
172
+ If your extension doesn't support scheduled job, you would need to extend the
173
+ custom job class to support the #scheduled method:
174
+
175
+ module Resque
176
+ class JobWithStatus
177
+ # Wrapper API to forward a Resque::Job creation API call into
178
+ # a JobWithStatus call.
179
+ def self.scheduled(queue, klass, *args)
180
+ create(args)
181
+ end
182
+ end
183
+ end
184
+
185
+
186
+ ### Schedule jobs per environment
187
+
188
+ Resque-Scheduler allows to create schedule jobs for specific envs. The arg
189
+ `rails_env` (optional) can be used to determine which envs are concerned by the
190
+ job:
191
+
192
+ create_fake_leaderboards:
193
+ cron: "30 6 * * 1"
194
+ class: CreateFakeLeaderboards
195
+ queue: scoring
196
+ args:
197
+ rails_env: demo
198
+ description: "This job will auto-create leaderboards for our online demo"
199
+
200
+ The scheduled job create_fake_leaderboards will be created only if the
201
+ environment variable `RAILS_ENV` is set to demo:
202
+
203
+ $ RAILS_ENV=demo rake resque:scheduler
204
+
205
+ NOTE: If you have added the 2 lines bellow to your Rails Rakefile
206
+ (ie: lib/tasks/resque-scheduler.rake), the rails env is loaded automatically
207
+ and you don't have to specify RAILS_ENV if the var is correctly set in
208
+ environment.rb
209
+
210
+ Alternatively, you can use your resque initializer to avoid loading the entire
211
+ rails stack.
212
+
213
+ $ rake resque:scheduler INITIALIZER_PATH=config/initializers/resque.rb
214
+
215
+
216
+ Multiple envs are allowed, separated by commas:
217
+
218
+ create_fake_leaderboards:
219
+ cron: "30 6 * * 1"
220
+ class: CreateFakeLeaderboards
221
+ queue: scoring
222
+ args:
223
+ rails_env: demo, staging, production
224
+ description: "This job will auto-create leaderboards"
225
+
226
+ NOTE: If you specify the `rails_env` arg without setting RAILS_ENV as an
227
+ environment variable, the job won't be loaded.
228
+
229
+
230
+ Resque-web additions
231
+ --------------------
232
+
233
+ Resque-scheduler also adds to tabs to the resque-web UI. One is for viewing
234
+ (and manually queueing) the schedule and one is for viewing pending jobs in
235
+ the delayed queue.
236
+
237
+ The Schedule tab:
238
+
239
+ ![The Schedule Tab](http://img.skitch.com/20100111-km2f5gmtpbq23enpujbruj6mgk.png)
240
+
241
+ The Delayed tab:
242
+
243
+ ![The Delayed Tab](http://img.skitch.com/20100111-ne4fcqtc5emkcuwc5qtais2kwx.jpg)
244
+
245
+ Get get these to show up you need to pass a file to `resque-web` to tell it to
246
+ include the `resque-scheduler` plugin. You probably already have a file
247
+ somewhere where you configure `resque`. It probably looks something like this:
248
+
249
+ gem 'resque-mongo'
250
+ require 'resque' # include resque so we can configure it
251
+ Resque.mongo = 'localhost:27017' # tell Resque where MongoDB lives
252
+
253
+ Now, you want to add the following:
254
+
255
+ require 'resque_scheduler' # include the resque_scheduler (this makes the tabs show up)
256
+
257
+ As of resque-scheduler 2.0, it's no longer necessary to have the resque-web
258
+ process aware of the schedule because it reads it from redis. But prior to
259
+ 2.0, you'll want to make sure you load the schedule in this file as well.
260
+ Something like this:
261
+
262
+ Resque.schedule = YAML.load_file(File.join(RAILS_ROOT, 'config/resque_schedule.yml')) # load the schedule
263
+
264
+ Now make sure you're passing that file to resque-web like so:
265
+
266
+ resque-web ~/yourapp/config/resque_config.rb
267
+
268
+ That should make the scheduler tabs show up in `resque-web`.
269
+
270
+
271
+ Installation and the Scheduler process
272
+ --------------------------------------
273
+
274
+ To install:
275
+
276
+ gem install resque-scheduler
277
+
278
+ The unless you specify the `queue` for each scheduled job, the scheduler
279
+ needs to know about your job classes (so it can put them into the appropriate
280
+ queue). To do so, extend the "resque:scheduler_setup" to load your app's code.
281
+ In rails, it would look something like this:
282
+
283
+ require 'resque_scheduler/tasks'
284
+ task "resque:scheduler_setup" => :environment # load the env so we know about the job classes
285
+
286
+ By default, "resque:scheduler_setup" invokes "resque:setup".
287
+
288
+ The scheduler process is just a rake task which is responsible for both queueing
289
+ items from the schedule and polling the delayed queue for items ready to be
290
+ pushed on to the work queues. For obvious reasons, this process never exits.
291
+
292
+ $ rake resque:scheduler
293
+
294
+ Supported environment variables are `VERBOSE` and `MUTE`. If either is set to
295
+ any nonempty value, they will take effect. `VERBOSE` simply dumps more output
296
+ to stdout. `MUTE` does the opposite and silences all output. `MUTE` supersedes
297
+ `VERBOSE`.
298
+
299
+ NOTE: You DO NOT want to run >1 instance of the scheduler. Doing so will result
300
+ in the same job being queued more than once. You only need one instnace of the
301
+ scheduler running per resque instance (regardless of number of machines).
302
+
303
+
304
+ Plagurism alert
305
+ ---------------
306
+
307
+ This was intended to be an extension to resque and so resulted in a lot of the
308
+ code looking very similar to resque, particularly in resque-web and the views. I
309
+ wanted it to be similar enough that someone familiar with resque could easily
310
+ work on resque-scheduler.
311
+
312
+
313
+ Contributing
314
+ ------------
315
+
316
+ For bugs or suggestions, please just open an issue in github.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ $LOAD_PATH.unshift 'lib'
5
+
6
+ task :default => :test
7
+
8
+ desc "Run tests"
9
+ task :test do
10
+ Dir['test/*_test.rb'].each do |f|
11
+ require File.expand_path(f)
12
+ end
13
+ end