resque_manager 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README.markdown +417 -0
  4. data/Rakefile +41 -0
  5. data/app/assets/images/resque_manager/idle.png +0 -0
  6. data/app/assets/images/resque_manager/poll.png +0 -0
  7. data/app/assets/images/resque_manager/working.png +0 -0
  8. data/app/assets/javascripts/resque_manager/application.js +15 -0
  9. data/app/assets/javascripts/resque_manager/jquery-1.3.2.min.js +19 -0
  10. data/app/assets/javascripts/resque_manager/jquery.relatize_date.js +95 -0
  11. data/app/assets/javascripts/resque_manager/ranger.js +24 -0
  12. data/app/assets/stylesheets/resque_manager/application.css +14 -0
  13. data/app/assets/stylesheets/resque_manager/resque/resque.css +93 -0
  14. data/app/assets/stylesheets/resque_manager/resque/resque_reset.css +48 -0
  15. data/app/assets/stylesheets/resque_manager/resque_cleaner/cleaner.css +62 -0
  16. data/app/controllers/resque_manager/resque_controller.rb +313 -0
  17. data/app/helpers/resque_manager/application_helper.rb +4 -0
  18. data/app/helpers/resque_manager/resque_helper.rb +142 -0
  19. data/app/models/resque_manager/paginate.rb +54 -0
  20. data/app/views/layouts/resque_manager/application.html.erb +37 -0
  21. data/app/views/resque_manager/resque/_key.html.erb +17 -0
  22. data/app/views/resque_manager/resque/_limiter.html.erb +12 -0
  23. data/app/views/resque_manager/resque/_next_more.html.erb +10 -0
  24. data/app/views/resque_manager/resque/_paginate.html.erb +53 -0
  25. data/app/views/resque_manager/resque/_queues.html.erb +59 -0
  26. data/app/views/resque_manager/resque/_status_styles.erb +98 -0
  27. data/app/views/resque_manager/resque/_workers.html.erb +138 -0
  28. data/app/views/resque_manager/resque/_working.html.erb +69 -0
  29. data/app/views/resque_manager/resque/cleaner.html.erb +41 -0
  30. data/app/views/resque_manager/resque/cleaner_exec.html.erb +6 -0
  31. data/app/views/resque_manager/resque/cleaner_list.html.erb +172 -0
  32. data/app/views/resque_manager/resque/delayed.html.erb +35 -0
  33. data/app/views/resque_manager/resque/delayed_timestamp.html.erb +26 -0
  34. data/app/views/resque_manager/resque/error.erb +1 -0
  35. data/app/views/resque_manager/resque/overview.html.erb +4 -0
  36. data/app/views/resque_manager/resque/schedule.html.erb +96 -0
  37. data/app/views/resque_manager/resque/stats.html.erb +62 -0
  38. data/app/views/resque_manager/resque/status.html.erb +57 -0
  39. data/app/views/resque_manager/resque/statuses.html.erb +72 -0
  40. data/app/views/resque_manager/resque/workers.html.erb +1 -0
  41. data/config/routes.rb +38 -0
  42. data/config/sample_redis.yml +43 -0
  43. data/config/sample_resque_manager.yml +23 -0
  44. data/lib/resque_manager/engine.rb +9 -0
  45. data/lib/resque_manager/overrides/resque/failure/redis.rb +11 -0
  46. data/lib/resque_manager/overrides/resque/job.rb +69 -0
  47. data/lib/resque_manager/overrides/resque/resque.rb +8 -0
  48. data/lib/resque_manager/overrides/resque/worker.rb +291 -0
  49. data/lib/resque_manager/overrides/resque_scheduler/resque_scheduler.rb +58 -0
  50. data/lib/resque_manager/overrides/resque_status/chained_status.rb +46 -0
  51. data/lib/resque_manager/overrides/resque_status/hash.rb +12 -0
  52. data/lib/resque_manager/overrides/resque_status/status.rb +161 -0
  53. data/lib/resque_manager/recipes.rb +185 -0
  54. data/lib/resque_manager/version.rb +3 -0
  55. data/lib/resque_manager.rb +47 -0
  56. data/lib/tasks/failure.rake +8 -0
  57. data/lib/tasks/scheduler.rake +11 -0
  58. data/lib/tasks/worker.rake +129 -0
  59. data/test/dummy/README.rdoc +261 -0
  60. data/test/dummy/Rakefile +7 -0
  61. data/test/dummy/app/assets/javascripts/application.js +15 -0
  62. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  63. data/test/dummy/app/controllers/application_controller.rb +3 -0
  64. data/test/dummy/app/helpers/application_helper.rb +2 -0
  65. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  66. data/test/dummy/config/application.rb +65 -0
  67. data/test/dummy/config/boot.rb +10 -0
  68. data/test/dummy/config/environment.rb +5 -0
  69. data/test/dummy/config/environments/development.rb +37 -0
  70. data/test/dummy/config/environments/production.rb +67 -0
  71. data/test/dummy/config/environments/test.rb +37 -0
  72. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  73. data/test/dummy/config/initializers/inflections.rb +15 -0
  74. data/test/dummy/config/initializers/mime_types.rb +5 -0
  75. data/test/dummy/config/initializers/secret_token.rb +7 -0
  76. data/test/dummy/config/initializers/session_store.rb +8 -0
  77. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  78. data/test/dummy/config/locales/en.yml +5 -0
  79. data/test/dummy/config/routes.rb +4 -0
  80. data/test/dummy/config.ru +4 -0
  81. data/test/dummy/log/development.log +5045 -0
  82. data/test/dummy/public/404.html +26 -0
  83. data/test/dummy/public/422.html +26 -0
  84. data/test/dummy/public/500.html +25 -0
  85. data/test/dummy/public/favicon.ico +0 -0
  86. data/test/dummy/script/rails +6 -0
  87. data/test/dummy/tmp/cache/assets/C2A/A10/sprockets%2Fb2e622954654f415590723e9b882063e +0 -0
  88. data/test/dummy/tmp/cache/assets/C60/1D0/sprockets%2F8ed12e4193473760f95b973567a8c206 +0 -0
  89. data/test/dummy/tmp/cache/assets/CA1/970/sprockets%2Fc387148880e015d1eab0dc838b326022 +0 -0
  90. data/test/dummy/tmp/cache/assets/CAE/930/sprockets%2Fe227278d3c65d8aa1159da720263f771 +0 -0
  91. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  92. data/test/dummy/tmp/cache/assets/CDC/E30/sprockets%2Fe1207380d69eeee3284e02636c26f24a +0 -0
  93. data/test/dummy/tmp/cache/assets/CF1/720/sprockets%2Fd91a5918f5aa43a43c8135a67c78e989 +0 -0
  94. data/test/dummy/tmp/cache/assets/D0E/820/sprockets%2F00c6cc9dc46bf64347b3775d7d15541b +0 -0
  95. data/test/dummy/tmp/cache/assets/D16/180/sprockets%2F73d6fa09352cb76ac81e1683e832b93f +0 -0
  96. data/test/dummy/tmp/cache/assets/D27/170/sprockets%2Fec164819553e2e5b28f1efc9bd970978 +0 -0
  97. data/test/dummy/tmp/cache/assets/D2B/DA0/sprockets%2F989465d3ea8575dd0b54981a9e8add38 +0 -0
  98. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  99. data/test/dummy/tmp/cache/assets/D37/1F0/sprockets%2F97119b908ebed2633edfd00ac90d9011 +0 -0
  100. data/test/dummy/tmp/cache/assets/D38/FB0/sprockets%2F74e5ba1cca7a1470d53c54fb60368b78 +0 -0
  101. data/test/dummy/tmp/cache/assets/D42/4E0/sprockets%2F0fa6e3c14356aa527d68a8d56fa37f28 +0 -0
  102. data/test/dummy/tmp/cache/assets/D43/C20/sprockets%2F1efd074fd1074b3dc88145b480ff961f +0 -0
  103. data/test/dummy/tmp/cache/assets/D46/CD0/sprockets%2F67f1ef70e7ede542318b8d55e25b16c3 +0 -0
  104. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  105. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  106. data/test/dummy/tmp/cache/assets/D68/080/sprockets%2Fa26f2ae225aa4b87c462d540c7cf43f9 +0 -0
  107. data/test/dummy/tmp/cache/assets/D9A/B20/sprockets%2F0eddc19d46318e2e286cc171ae4cc73e +0 -0
  108. data/test/dummy/tmp/cache/assets/DA4/900/sprockets%2F515bf984438c6ec4b8a515fcc13baf8e +0 -0
  109. data/test/dummy/tmp/cache/assets/DBD/070/sprockets%2F60ffef45ddefd5c7746d17977fff0717 +0 -0
  110. data/test/dummy/tmp/cache/assets/DD7/AC0/sprockets%2Fc7c983c5c607dbfdb726eecc36146ca9 +0 -0
  111. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  112. data/test/dummy/tmp/cache/assets/DF5/480/sprockets%2Fea4f3c726fc1046cad1ad243faf84e7d +0 -0
  113. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  114. data/test/dummy/tmp/cache/assets/E2B/7A0/sprockets%2Fd44ef07be0aa6d5b5dea4d37d7f72b4f +0 -0
  115. data/test/functional/resque_manager/resque_controller_test.rb +9 -0
  116. data/test/integration/navigation_test.rb +10 -0
  117. data/test/resque_manager_test.rb +7 -0
  118. data/test/test_helper.rb +15 -0
  119. data/test/unit/helpers/resque_manager/resque_helper_test.rb +6 -0
  120. metadata +307 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2dc1842d613d5bea7b454adf023b49640044354b
4
+ data.tar.gz: 1a4539c2703f96879d128047b7279332905c7336
5
+ SHA512:
6
+ metadata.gz: 1b8bfb94ace1d479420b8be913c3b4efae77cd0d32b1e0a419fe96b1d15c4c272a162333fdf482bdd920a707ed228e19fb354fbe6aaf3d6ba00059628ffc918e
7
+ data.tar.gz: c7ca79eadf83075237a5990b2223e4a190ede2abeba24fa6ded8906552fc1afcb44dae2e689554f0bb7dd6884fe102fe48a4c43b37322647b990ed6a940fa283
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2009 Chris Wanstrath
2
+ Copyright (c) 2013 Kevin Tyll
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,417 @@
1
+ Resque UI has been renamed to Resque Manager to better reflect what this engine really does. It manages your resque workers...through the UI.
2
+
3
+ ResqueManager
4
+ --------
5
+
6
+ Resque Manager is a Rails engine port of the Sinatra app that is included in Chris Wanstrath's resque gem. We love the gem and love the UI,
7
+ but just didn't want to add Sinatra to our stack and wanted to be able to manage the queues from the UI.
8
+
9
+
10
+ Installation
11
+ ------------
12
+
13
+ sudo gem install resque_manager
14
+
15
+ Or just add it to your Gemfile
16
+
17
+ gem 'resque_manager'
18
+
19
+ If you have your default routes disabled, which you should if you have a RESTful API, then you'll need to add this to
20
+ the bottom of your routes.rb file.
21
+
22
+ # Resque Manager
23
+ mount ResqueManager::Engine => 'resque'
24
+
25
+ Once installed, you now have a resque controller, so you can get to the ui with: http://your_domain/resque.
26
+
27
+ Dependencies
28
+ ------------
29
+
30
+ This engine now requires rails 3.2.0 or greater.
31
+
32
+ This engine now requires redis 2.0 or greater for the expiration of keys to work correctly.
33
+
34
+ This engine requires the redis 3.0 or higher gem
35
+
36
+ This engine requires the resque 1.24 or higher gem.
37
+
38
+ This engine now requires the resque-status 0.4.0 or higher gem.
39
+
40
+ This engine now requires the resque-cleaner 0.2 or higher gem.
41
+
42
+ These gems will all be installed for you automatically when you install resque_manager.
43
+
44
+ Configuration
45
+ --------------
46
+
47
+ There are a few things you need to configure, and a few more you can if you like. The easiest is to add an initializer:
48
+
49
+ ResqueManager.configure do |config|
50
+ # configure redis
51
+ config.redis_config = YAML.load(IO.read(Rails.root.join("config", "redis.yml")))["#{Rails.env}_resque"]
52
+ resque_manager_config = YAML.load(IO.read(Rails.root.join('config', 'resque_manager.yml')))[Rails.env]
53
+ # optional - set when you want your status keys to expire. Once expired, jobs will no longer show on the status page.
54
+ config.key_expiration = resque_manager_config['key_expiration']
55
+ # optional - Tell whether to run jobs inline or not.
56
+ config.inline = resque_manager_config['inline']
57
+ # optional - If you have workers in multiple applications that you want to control through a single app's UI, this this
58
+ # to a hash where the keys are your application names, and the values are the paths where the app is deployed.
59
+ config.applications = resque_manager_config['applications']
60
+ end
61
+
62
+ See the sample .yml files in config.
63
+
64
+ Capistrano Configuration
65
+ --------------
66
+
67
+ Added the ability to stop, start, and restart workers from the workers page. This requires capistrano, and capistrano-ext to be installed on all deployed servers.
68
+
69
+ ![Manage Workers](http://img.skitch.com/20100308-ds6bgsnwqe6j9jn9yx8x7cxre3.png)
70
+
71
+
72
+ The controller calls cap tasks to manage the workers. To include the recipes in your application, add this line to your deploy.rb file:
73
+
74
+ require 'resque_manager/recipes'
75
+
76
+ You will also need to define the :resque role in your deploy/<environment>.rb file with the servers that will run your workers.
77
+
78
+ role :resque, 'server1', 'server2'
79
+
80
+ You will also need to make sure you have your rake path set in the deploy.rb file.
81
+
82
+ set :rake, "/opt/ruby-enterprise-1.8.6-20090421/bin/rake"
83
+
84
+
85
+ ...using your own path of course.
86
+
87
+ The cap tasks included are:
88
+
89
+ cap resque:work # start a resque worker.
90
+ cap resque:workers # start multiple resque workers.
91
+ cap resque:quit_worker # Gracefully kill a worker. If the worker is working, it will finish before shutting down.
92
+ cap resque:quit_workers # Gracefully kill all workers on all servers. If the worker is working, it will finish before shutting down.
93
+ cap resque:kill_worker_with_impunity # Kill a rogue worker. If the worker is working, it will not finish and the job will go to the Failed queue as a DirtyExit. arg: host=ip pid=pid
94
+ cap resque:kill_workera_with_impunity # Kill all rogue workers on all servers. If the worker is working, it will not finish and the job will go to the Failed queue as a DirtyExit.
95
+ cap resque:restart_workers # Restart all workers on all servers
96
+
97
+ Times Fixed
98
+ -----------
99
+
100
+ The displaying of times is fixed. Some browsers displayed "Nan days ago" for all process times in the original Sinatra app.
101
+ Fixed the stats/keys page. The page wasn't showing the keys' types, size or values.
102
+
103
+ Display Process Status
104
+ ----------------------
105
+
106
+ Added the ability to display process status in the worker "Processing" column on the overview, workers and working pages.
107
+ To do this, set the overview_status to the status message in your perform method.
108
+
109
+ Class YourClass
110
+
111
+ def perform(arg)
112
+ ...your code here
113
+ overview_status = "Your status message"
114
+ ...more code
115
+ overview_status = "Another status message"
116
+ ...more code
117
+ end
118
+ end
119
+
120
+ This is really handy for those long running jobs to give you assurance the the job is really running or not.
121
+
122
+ ![Status Messages](http://img.skitch.com/20100308-8mk5hrwnu462q2d23d51n8cjxp.png)
123
+
124
+ Restart Failed Jobs
125
+ -------------------
126
+
127
+ Added the Resque Cleaner gem to manage the failed queue. Have complete control over the failed queue now with the
128
+ Cleaner tab by querying and restarting failed jobs.
129
+
130
+ See what all you can do on the github page: https://github.com/ono/resque-cleaner
131
+
132
+ Remove Items from the Queue
133
+ ---------------------------
134
+
135
+ Added the ability to remove jobs, from a queue
136
+
137
+ ![Remove Items from the Queue](http://img.skitch.com/20100308-qukiw7bpsnr9y1saap7f8276qx.png)
138
+
139
+ View Processed Job Info
140
+ -----------------------
141
+
142
+ ![Job Status](https://img.skitch.com/20110309-r9mhfgnn8w63ep29sik4c6khcj.png)
143
+
144
+ resque_manager now incorporates the resque-status gem and replaced the Processed tab with the Status tab. You can read about
145
+ what you can do with resque-status [here](https://github.com/quirkey/resque-status).
146
+
147
+ I've added some additional functionality to the resque-status gem. Namely, I've added a Resque::Plugins::ChainedStatus module. We process a lot of data files. Each part of the file's process is handled by a different worker. One worker may convert
148
+ a file into a different format, then another will parse that file and peel each record off the file and put each individual
149
+ record on a separate queue. A separate worker may then do any post processing when the file is complete.
150
+
151
+ I wanted all of that to show under a single status. So to do that, the very first worker class includes
152
+ Resque::Plugins::Status, and everything after that includes from Resque::Plugins::ChainedStatus. When you call #create on
153
+ the chained job from the preceding job, you just need to pass {'uuid' => uuid} as one of the hash arguments.
154
+
155
+ class DataContributionFile
156
+ Resque::Plugins::Status
157
+
158
+ @queue = :data_contribution
159
+
160
+ def perform
161
+ ...your code here
162
+ tick "Retrieving file."
163
+ ...more code
164
+ tick "Peeling #{file_path}"
165
+ SingleRecordLoader.create({'uuid' => uuid, 'row_data' => hash_of_row_data, 'rows_in_file' => total_rows})
166
+ end
167
+ end
168
+
169
+ class SingleRecordLoader
170
+ Resque::Plugins::ChainedStatus
171
+ @queue = :single_record_loader
172
+
173
+ def completed(*messages)
174
+ if counter(:processed) >= options[:rows_in_file].to_i
175
+ super("#{options[:rows_in_file]} records processed: Started(#{status.time.to_s(:eastern_time_zone_long)}) Finished(#{Time.now.to_s(:eastern_time_zone_long)})")
176
+ post_process
177
+ end
178
+
179
+ def perform
180
+ ...your code here
181
+
182
+ incr_counter(:processed)
183
+ at((self.processed), options[:rows_in_file], "#{(self.processed)} of #{options[:rows_in_file]} completed.")
184
+ end
185
+ end
186
+
187
+ So now, the data_contribution worker and the single_record_loader workers will update the same status on the status page.
188
+ You can call #tick or #set_status to add messages along the way too. Note: These statuses are shown in the Messages column
189
+ of the status page. When you set the overview_status, those messages appear in the Processing column of the Overview, Workers,
190
+ and Working pages.
191
+
192
+ You will want to override the completed method so that it isn't called until the very end of the entire process.
193
+
194
+ I've also added two more methods, #incr_counter(:counter) and #count(:counter). We have dozens of single_record_loader
195
+ workers processing records at a time. You encounter a race condition when they are all calling #at at the same time to
196
+ update the :num attribute. So I created these two methods to atomically increment a dedicated counter. Just call #incr_counter
197
+ and pass in a symbol for what you want to call the counter. You can create any number of different counters
198
+ for different purposes. We keep track of different validation issues for each record. Use #counter and pass it the same
199
+ symbol to read the integer back. The redis entries created by these methods all get cleaned up with a call to Resque::Plugins::Status::Hash.clear(uuid)
200
+
201
+ When you kill a job on the UI, it will kill all the workers in the chain.
202
+
203
+ Pause a Worker
204
+ --------------
205
+
206
+ The workers page now has a button for every worker to pause that worker.
207
+
208
+ ### Regular Workers
209
+
210
+ For workers that do not include Resque::Plugins::Status, this will pause the worker, but not the job. So if the worker is in
211
+ the middle of a job when it is paused, it will finish it's process, but then will not pick anything else up off the queue.
212
+
213
+ You can manually pause the processing though using the worker object.
214
+
215
+ Class YourClass
216
+
217
+ def perform(arg)
218
+ ...your code here
219
+
220
+ if worker && worker.paused?
221
+ loop do
222
+ break unless worker.paused?
223
+ sleep 60
224
+ end
225
+ end
226
+ end
227
+
228
+ ...continue
229
+ end
230
+
231
+ ### Resque::Plugins::Status Workers
232
+
233
+ For workers that do include Resque::Plugins::Status or Resque::Plugins::ChainedStatus, this will pause the worker, and will automatically pause the job it is processing
234
+ on the next call to #tick. The worker is also available to the class that includes Resque::Plugins::Status so you can manually check it's status as well.
235
+
236
+ Class YourClass
237
+
238
+ Resque::Plugins::Status
239
+
240
+ def perform
241
+ ...your code here
242
+ tick "Retrieving file." #You're process will pause here automatically and the status on the Status tab will be set to paused if the worker is paused.
243
+
244
+ #Alternatively, you have access to the worker, so you can pause the process yourself too.
245
+ if worker && worker.paused?
246
+ # There could be workers in a chained job still doing work.
247
+ loop do
248
+ pause! unless status.paused?
249
+ break unless worker.paused?
250
+ sleep 60
251
+ end
252
+ tick("Job resumed at #{Time.now}")
253
+ end
254
+
255
+ ...continue
256
+ end
257
+
258
+ This will only pause the work being processed by the worker that was paused. If the job is paused by the call to #tick,
259
+ the job will sleep for 60 seconds before checking the status again.
260
+
261
+ You may have a series of classes that include Resque::Plugins::ChainedStatus and you want all processing in the chain stopped.
262
+
263
+ Class YourClass
264
+
265
+ Resque::Plugins::ChainedStatus
266
+
267
+ def perform
268
+ ...your code here
269
+ tick "Retrieving file." #You're process will pause here automatically and the status on the Status tab will be set to paused if the worker is paused.
270
+
271
+ #Alternatively, you have access to the worker, so you can pause the process yourself too.
272
+ if (worker && worker.paused?) || status.paused?
273
+ # There could be workers in a chained job still doing work.
274
+ loop do
275
+ pause! unless status.paused?
276
+ break unless worker.paused?
277
+ sleep 60
278
+ end
279
+ tick("Job resumed at #{Time.now}")
280
+ end
281
+
282
+ ...continue
283
+ end
284
+
285
+ By looking at the status.paused? method too, this process will stop, even if it's worker has not been paused.
286
+ But be aware, if this worker does other jobs, it will not process anything else and it's queue could get backed up.
287
+ This is where pausing one worker, could affect other, unrelated workers and jobs from getting backed up as well.
288
+
289
+ Throttle a Queue
290
+ ----------------
291
+
292
+ A throttle method has been added. This is useful if you have a queue that tends to have very high volume, for example,
293
+ the queue that process all the individual records of a file. You don't want to load that queue up with 1 million entries,
294
+ possibly blowing out the memory of your Redis server.
295
+
296
+ CSV.foreach(self.file_path, :headers => true, :quote_char => '"') do |row|
297
+ Resque.throttle(:single_record_loader, 10000, 30)
298
+ SingleRecordLoader.create({'uuid' => uuid, 'row_data' => row.to_hash, 'rows_in_file' => total_rows})
299
+ end
300
+
301
+ Putting the throttle before enqueing the SingleRecordLoader will check the single_record_loader queue to make sure it has
302
+ less than 10000 entries in it before proceeding. If is has 10000 or more entries, it will sleep for 30 seconds before checking again.
303
+
304
+
305
+ Multi-Threaded Workers
306
+ ---------------------
307
+ With Jruby, you have to specify the amount of memory to allocate when you start up the jvm. This has proven inefficient for us because different workers
308
+ require different amounts of memory. We have standardized our jvm configuration for the workers, which means we have to start each worker with
309
+ the maximum amount of memory needed by the most memory intensive worker. This means we are wasting a lot of resources for the workers that don't
310
+ require as much memory.
311
+
312
+ Our answer was to make the workers multi-threaded. Now you can pass multiple workers and queues into the rake task, each worker will be started in separate
313
+ threads within the same process.
314
+
315
+ NOTE: The convention to identify which queues are monitored by which worker is to prefix each worker with a '#' in the rake task argument.
316
+
317
+ rake QUEUE=#file_loader#file_loader,email resque:work
318
+
319
+ This will start up 2 workers, 1 will work the file_loader queue, and one will work the file_loader and email queue.
320
+
321
+ Be aware that when you stop a worker, it will stop all the workers within that process.
322
+
323
+ Workers in Multiple Applications
324
+ --------------------------------
325
+
326
+ You now have the ability to manage workers in multiple applications from a single app's UI. In other words, the workers
327
+ can be in a different app than the UI itself. I did this becase our app had become very large. I did not want to have to
328
+ load up our huge monolithic app just to run a small worker. So by splitting the workers out into a separate application, I
329
+ save server resources, but only our main app mounts resque_manager in the routes.rb file and manages all the workers.
330
+
331
+ To do this, you'll need to add resque_manager to the Gemfile of all apps containing workers as well as the app with the UI.
332
+
333
+ You will also need to tell the app that mounts resque_manager UI what applications contain workers and the paths where they are deployed to.
334
+
335
+ ResqueManager.applications = {application1: '/Users/ktyll/rails_sites/git/application1',
336
+ application2: '/Users/ktyll/rails_sites/git/application2'}
337
+
338
+ See the sample initializer above.
339
+
340
+ By setting the applications hash, a select box will display on the workers page so you can select the application where the
341
+ worker is you want to start.
342
+
343
+ ![Multiple Apps](https://www.evernote.com/shard/s198/sh/04538799-da21-48b4-ab63-ac19c815dc85/62f73190d926844e5f97ea74f0bedb6c/deep/0/Pasted%20Image%207/30/13%2010:30%20AM.png)
344
+
345
+ You do not need to include the application that has mounted the resque_manager UI in this hash. If you do not select an application
346
+ from the drop down, it will assume the worker is in the same app.
347
+
348
+ After Deploy Hooks
349
+ ------------------
350
+
351
+ The resque:restart_workers cap task can be added as an after deploy task to refresh your workers. Without this, your workers will
352
+ continue to run your old code base after a deployment.
353
+
354
+ To make it work add the callbacks in your deploy.rb file:
355
+
356
+ after "deploy", "resque:restart_workers"
357
+ after "deploy:migrations", "resque:restart_workers"
358
+
359
+
360
+ Resque Scheduler
361
+ ----------------
362
+
363
+ If resque-scheduler is installed, the Schedule and Delayed tabs will display.
364
+
365
+ Be sure you add the resque_scheduler gem before the resque_manager gem in your Gemfile:
366
+
367
+ gem 'resque-scheduler', :require => 'resque_scheduler'
368
+ gem 'resque_manager'
369
+
370
+ The Schedule tab functionality has been enhanced to be able to add jobs to the scheduler from the UI. This means you don't
371
+ need to edit a static file that gets loaded on initialization. This also means you don't have to deploy that file every time
372
+ you edit your schedule.
373
+
374
+ You can also create different schedules on different servers in your farm. You specify
375
+ the IP address you want to schedule a job to run on, and it will add the job to the schedule on that server. You can also start and
376
+ stop the scheduler on each server from the Schedule tab.
377
+
378
+ ![Resque Scheduler](http://img.skitch.com/20100308-quccysfiwtgubpw286ka2enr9m.png)
379
+
380
+ The caveat to this is the Arguments value must be entered in the text box as JSON in order for the arguments to get parsed and stored
381
+ in the schedule correctly. I find the easiest thing to do is to perform a Resque.encode on my parameters list in script/console. If
382
+ we have a method that takes 3 parameters:
383
+
384
+ >> Resque.encode([['300'],1,{"start_date"=>"2010-02-01","end_date"=>"2010-02-28"}])
385
+ => "[["300"],1,{"end_date":"2010-02-28","start_date":"2010-02-01"}]"
386
+
387
+ The first parameter is an array of strings, the second parameter is an integer, and the third parameter is a hash.
388
+ Remembering that the arguments are stored in an array, all the parameters need to be in an array when there is more than one.
389
+
390
+ Any string arguments need to be quoted in the text box:
391
+
392
+ >> Resque.encode("Hello World")
393
+ => ""Hello World""
394
+
395
+ ### Additional cap tasks added:
396
+
397
+ cap resque:quit_scheduler # Gracefully kill the scheduler on a server.
398
+ cap resque:scheduler # start a resque worker.
399
+ cap resque:scheduler_status # Determine if the scheduler is running or not
400
+
401
+ Delayed Tab
402
+ -----------
403
+
404
+ I have not tested or added any functionality to the Delayed tab. I get a RuntimeError: -ERR invalid bulk write count
405
+ any time I try to do a Resque.enqueue_at. I've spent some time researching, and assume it's something with my version combinations.
406
+ I believe it's a Redis issue and not a Resque-Scheduler issue. But since I'm not using it, I haven't put a great deal of
407
+ time into resolving it.
408
+
409
+ Copyright (c) 2009 Chris Wanstrath
410
+ Copyright (c) 2010 Ben VandenBos
411
+ Copyright (c) 2010 Aaron Quint
412
+ Copyright (c) 2011 Tatsuya Ono
413
+ Copyright (c) 2013 Kevin Tyll, released under the MIT license
414
+
415
+ Thanks to Karl Baum for doing the original heavy lifting for converting this to a rails engine for rails 3.
416
+
417
+ Much thanks goes to Brian Ketelsen for the ideas for the improved functionality for the UI.
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ResqueManager'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.markdown')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ rdoc.rdoc_files.include('app/**/*.rb')
22
+ end
23
+
24
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
25
+ load 'rails/tasks/engine.rake'
26
+
27
+
28
+
29
+ Bundler::GemHelper.install_tasks
30
+
31
+ require 'rake/testtask'
32
+
33
+ Rake::TestTask.new(:test) do |t|
34
+ t.libs << 'lib'
35
+ t.libs << 'test'
36
+ t.pattern = 'test/**/*_test.rb'
37
+ t.verbose = false
38
+ end
39
+
40
+
41
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require resque_manager/jquery-1.3.2.min
14
+ //= require resque_manager/jquery.relatize_date
15
+ //= require resque_manager/ranger