resque-integration 3.4.1
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.
- checksums.yaml +7 -0
- data/.drone.yml +28 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/Appraisals +27 -0
- data/CHANGELOG.md +311 -0
- data/Gemfile +4 -0
- data/README.md +281 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/standalone/progress_bar.js +47 -0
- data/app/controllers/resque/jobs_controller.rb +42 -0
- data/app/controllers/resque/queues/info_controller.rb +9 -0
- data/app/controllers/resque/queues/status_controller.rb +38 -0
- data/app/views/shared/job_progress_bar.html.haml +17 -0
- data/bin/resque-status +59 -0
- data/config/routes.rb +13 -0
- data/config.ru +9 -0
- data/dip.yml +44 -0
- data/docker-compose.development.yml +12 -0
- data/docker-compose.drone.yml +6 -0
- data/docker-compose.yml +10 -0
- data/lib/generators/resque/integration/install/install_generator.rb +38 -0
- data/lib/resque/integration/backtrace.rb +29 -0
- data/lib/resque/integration/configuration.rb +238 -0
- data/lib/resque/integration/continuous.rb +75 -0
- data/lib/resque/integration/engine.rb +103 -0
- data/lib/resque/integration/extensions/job.rb +17 -0
- data/lib/resque/integration/extensions/worker.rb +17 -0
- data/lib/resque/integration/extensions.rb +8 -0
- data/lib/resque/integration/failure_backends/queues_totals.rb +37 -0
- data/lib/resque/integration/failure_backends.rb +7 -0
- data/lib/resque/integration/god.erb +99 -0
- data/lib/resque/integration/hooks.rb +72 -0
- data/lib/resque/integration/logs_rotator.rb +95 -0
- data/lib/resque/integration/monkey_patch/verbose_formatter.rb +10 -0
- data/lib/resque/integration/ordered.rb +142 -0
- data/lib/resque/integration/priority.rb +89 -0
- data/lib/resque/integration/queues_info/age.rb +53 -0
- data/lib/resque/integration/queues_info/config.rb +96 -0
- data/lib/resque/integration/queues_info/size.rb +33 -0
- data/lib/resque/integration/queues_info.rb +55 -0
- data/lib/resque/integration/tasks/hooks.rake +49 -0
- data/lib/resque/integration/tasks/lock.rake +37 -0
- data/lib/resque/integration/tasks/resque.rake +101 -0
- data/lib/resque/integration/unique.rb +218 -0
- data/lib/resque/integration/version.rb +5 -0
- data/lib/resque/integration.rb +146 -0
- data/lib/resque-integration.rb +1 -0
- data/resque-integration.gemspec +40 -0
- data/spec/fixtures/resque_queues.yml +45 -0
- data/spec/resque/controllers/jobs_controller_spec.rb +65 -0
- data/spec/resque/integration/configuration_spec.rb +147 -0
- data/spec/resque/integration/continuous_spec.rb +122 -0
- data/spec/resque/integration/failure_backends/queues_totals_spec.rb +105 -0
- data/spec/resque/integration/ordered_spec.rb +87 -0
- data/spec/resque/integration/priority_spec.rb +94 -0
- data/spec/resque/integration/queues_info_spec.rb +402 -0
- data/spec/resque/integration/unique_spec.rb +184 -0
- data/spec/resque/integration_spec.rb +105 -0
- data/spec/shared/resque_inline.rb +10 -0
- data/spec/spec_helper.rb +28 -0
- data/vendor/assets/images/progressbar/white.gif +0 -0
- data/vendor/assets/javascripts/jquery.progressbar.js +177 -0
- data/vendor/assets/stylesheets/jquery.progressbar.css.erb +33 -0
- data/vendor/assets/stylesheets/jquery.progressbar.no_pipeline.css +33 -0
- metadata +402 -0
data/README.md
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
# Resque::Integration
|
2
|
+
|
3
|
+
<a href="http://dolly.railsc.ru/projects/47/builds/latest/?ref=master"><img src="http://dolly.railsc.ru/badges/abak-press/resque-integration/master" height=18 /></a>
|
4
|
+
|
5
|
+
Интеграция Resque в Rails-приложения с поддержкой следующих плагинов:
|
6
|
+
* [resque-progress](https://github.com/idris/resque-progress)
|
7
|
+
* [resque-lock](https://github.com/defunkt/resque-lock)
|
8
|
+
* [resque-multi-job-forks](https://github.com/stulentsev/resque-multi-job-forks)
|
9
|
+
* [resque-failed-job-mailer](https://github.com/anandagrawal84/resque_failed_job_mailer)
|
10
|
+
* [resque-scheduler](https://github.com/resque/resque-scheduler)
|
11
|
+
* [resque-retry](https://github.com/lantins/resque-retry)
|
12
|
+
|
13
|
+
Этот гем существует затем, чтобы избежать повторения чужих ошибок и сократить время, необходимое для включения resque в проект.
|
14
|
+
|
15
|
+
## Установка
|
16
|
+
|
17
|
+
Добавьте в `Gemfile`:
|
18
|
+
```ruby
|
19
|
+
gem 'resque-integration'
|
20
|
+
```
|
21
|
+
|
22
|
+
Добавьте в `config/routes.rb`:
|
23
|
+
```ruby
|
24
|
+
mount Resque::Integration::Application => "/_job_", :as => "job_status"
|
25
|
+
```
|
26
|
+
|
27
|
+
Вместо `_job_` можно прописать любой другой адрес. По этому адресу прогресс-бар будет узнавать о состоянии джоба.
|
28
|
+
|
29
|
+
Если вы до сих пор не используете sprockets, то сделайте что-то вроде этого:
|
30
|
+
```bash
|
31
|
+
$ rails generate resque:integration:install
|
32
|
+
```
|
33
|
+
(результат не гарантирован, т.к. не тестировалось)
|
34
|
+
|
35
|
+
## Задачи
|
36
|
+
|
37
|
+
Создайте файл `app/jobs/resque_job_test.rb`:
|
38
|
+
```ruby
|
39
|
+
class ResqueJobTest
|
40
|
+
include Resque::Integration
|
41
|
+
|
42
|
+
# это название очереди, в которой будет выполняться джоб
|
43
|
+
queue :my_queue
|
44
|
+
|
45
|
+
# с помощью unique можно указать, что задача является уникальной, и какие аргументы определяют уникальность задачи.
|
46
|
+
# в данном случае не может быть двух одновременных задач ResqueJobTest с одинаковым первым аргументом
|
47
|
+
# (второй аргумент может быть любым)
|
48
|
+
unique { |id, description| [id] }
|
49
|
+
|
50
|
+
# В отличие от обычных джобов resque, надо определять метод execute.
|
51
|
+
#
|
52
|
+
# Либо же вы можете определить метод perform, но первым аргументом должен быть указан meta_id (уникальный ID джоба):
|
53
|
+
# def self.perform(meta_id, id, description)
|
54
|
+
# ...
|
55
|
+
# end
|
56
|
+
def self.execute(id, description)
|
57
|
+
(1..100).each do |t|
|
58
|
+
at(t, 100, "Processing #{id}: at #{t} of 100")
|
59
|
+
sleep 0.5
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
### Перезапуск задачи
|
66
|
+
|
67
|
+
Допустим, у вас есть очень длинная задача и вы не хотите, чтобы возникло переполнение очереди (или памяти). Тогда можно выполнить часть задачи, а потом заново поставить задачу в очередь.
|
68
|
+
```ruby
|
69
|
+
class ResqueJobTest
|
70
|
+
include Resque::Integration
|
71
|
+
|
72
|
+
unique
|
73
|
+
continuous # теперь вы можете перезапускать задачу
|
74
|
+
|
75
|
+
def self.execute(company_id)
|
76
|
+
products = company.products.where('updated_at < ?', 1.day.ago)
|
77
|
+
|
78
|
+
products.limit(1000).each do |product|
|
79
|
+
product.touch
|
80
|
+
end
|
81
|
+
|
82
|
+
# В очередь поставится задача с теми же аргументами, что и текущая.
|
83
|
+
# Можно передать другие аргументы: `continue(another_argument)`
|
84
|
+
continue if products.count > 0
|
85
|
+
end
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
Такая задача будет выполняться по частям, не потребляя много памяти. Еще один плюс: другие задачи из очереди тоже смогут выполниться.
|
90
|
+
ОПАСНО! Избегайте бесконечных циклов, т.к. это в некотором роде "рекурсия".
|
91
|
+
|
92
|
+
## Конфигурация воркеров resque
|
93
|
+
|
94
|
+
Создайте файл `config/resque.yml` с несколькими секциями:
|
95
|
+
```yaml
|
96
|
+
# конфигурация redis
|
97
|
+
# секция не обязательная, вы сами можете настроить подключение через Resque.redis = Redis.new
|
98
|
+
redis:
|
99
|
+
host: bz-redis
|
100
|
+
port: 6379
|
101
|
+
namespace: blizko
|
102
|
+
|
103
|
+
resque:
|
104
|
+
interval: 5 # частота, с которой resque берет задачи из очереди в секундах (по умолчанию 5)
|
105
|
+
verbosity: 1 # "шумность" логера (0 - ничего не пишет, 1 - пишет о начале/конце задачи, 2 - пишет все)
|
106
|
+
root: "/home/pc/current" # (production) абсолютный путь до корня проекта
|
107
|
+
log_file: "/home/pc/static/pulscen/local/log/resque.log" # (production) абсолютный путь до лога
|
108
|
+
config_file: "/home/pc/static/pulscen/local/log/resque.god" # (production) абсолютный путь до кофига god
|
109
|
+
pids: "/home/pc/static/pulscen/local/pids" # (production) абсолютный путь до папки с пид файлами
|
110
|
+
|
111
|
+
# переменные окружения, которые надобно передавать в resque
|
112
|
+
env:
|
113
|
+
RUBY_HEAP_MIN_SLOTS: 2500000
|
114
|
+
RUBY_HEAP_SLOTS_INCREMENT: 1000000
|
115
|
+
RUBY_HEAP_SLOTS_GROWTH_FACTOR: 1
|
116
|
+
RUBY_GC_MALLOC_LIMIT: 50000000
|
117
|
+
|
118
|
+
# конфигурация воркеров (названия воркеров являются названиями очередей)
|
119
|
+
workers:
|
120
|
+
kirby: 2 # 2 воркера в очереди kirby
|
121
|
+
images:
|
122
|
+
count: 8 # 8 воркеров в очереди images
|
123
|
+
jobs_per_fork: 250 # каждый воркер обрабатывает 250 задач прежде, чем форкается заново
|
124
|
+
minutes_per_fork: 30 # альтернатива предыдущей настройке - сколько минут должен работать воркер, прежде чем форкнуться заново
|
125
|
+
stop_timeout: 5 # максимальное время, отпущенное воркеру для остановки/рестарта
|
126
|
+
env: # переменные окружение, специфичные для данного воркера
|
127
|
+
RUBY_HEAP_SLOTS_GROWTH_FACTOR: 0.5
|
128
|
+
'companies,images': 2 # совмещённая очередь, приоритет будет у companies
|
129
|
+
'xls,yml':
|
130
|
+
shuffle: true # совмещённая очередь, приоритета не будет
|
131
|
+
|
132
|
+
# конфигурация failure-бэкэндов
|
133
|
+
failure:
|
134
|
+
# конфигурация отправщика отчетов об ошибках
|
135
|
+
notifier:
|
136
|
+
enabled: true
|
137
|
+
# адреса, на которые надо посылать уведомления об ошибках
|
138
|
+
to: [teamlead@apress.ru, pm@apress.ru, programmer@apress.ru]
|
139
|
+
# необязательные настройки
|
140
|
+
# от какого адреса слать
|
141
|
+
from: no-reply@blizko.ru
|
142
|
+
# включать в письмо payload (аргументы, с которыми вызвана задача)
|
143
|
+
include_payload: true
|
144
|
+
# класс отправщика (должен быть наследником ActionMailer::Base, по умолчанию ResqueFailedJobMailer::Mailer
|
145
|
+
mailer: "Blizko::ResqueMailer"
|
146
|
+
# метод, который вызывается у отправщика (по умолчанию alert)
|
147
|
+
mail: alert
|
148
|
+
```
|
149
|
+
|
150
|
+
Обратите внимание на параметр `stop_timeout` в секции конфигурирования воркеров.
|
151
|
+
Это очень важный параметр. По умолчанию воркеру отводится всего 10 секунд на то,
|
152
|
+
чтобы остановиться. Если воркер не укладывается в это время, супервизор (мы используем
|
153
|
+
[god](http://godrb.com/)) посылает воркеру сигнал `KILL`, который "прибьет" задачу.
|
154
|
+
Если у вас есть длинные задачи (навроде импорта из XML), то для таких воркеров
|
155
|
+
лучше ставить `stop_timeout` побольше.
|
156
|
+
|
157
|
+
Для разработки можно (и нужно) создать файл `config/resque.local.yml`, в котором можно переопределить любые параметры:
|
158
|
+
```yaml
|
159
|
+
redis:
|
160
|
+
host: localhost
|
161
|
+
port: 6379
|
162
|
+
|
163
|
+
resque:
|
164
|
+
verbosity: 2
|
165
|
+
|
166
|
+
workers:
|
167
|
+
'*': 1
|
168
|
+
```
|
169
|
+
|
170
|
+
## Запуск воркеров
|
171
|
+
|
172
|
+
Ручной запуск воркера (см. [официальную документацию resque](https://github.com/resque/resque/blob/1-x-stable/README.markdown))
|
173
|
+
```bash
|
174
|
+
$ QUEUE=* rake resque:work
|
175
|
+
```
|
176
|
+
|
177
|
+
Запуск всех воркеров так, как они сконфигурированы в `config/resque.yml`:
|
178
|
+
```bash
|
179
|
+
$ rake resque:start
|
180
|
+
```
|
181
|
+
|
182
|
+
Останов всех воркеров:
|
183
|
+
```bash
|
184
|
+
$ rake resque:stop
|
185
|
+
```
|
186
|
+
|
187
|
+
Перезапуск воркеров:
|
188
|
+
```bash
|
189
|
+
$ rake resque:restart
|
190
|
+
```
|
191
|
+
|
192
|
+
## Постановка задач в очередь
|
193
|
+
|
194
|
+
### Для задач, в который включен модуль `Resque::Integration`
|
195
|
+
```ruby
|
196
|
+
meta = ResqueJobTest.enqueue(id=2)
|
197
|
+
@job_id = meta.meta_id
|
198
|
+
```
|
199
|
+
|
200
|
+
Вот так можно показать прогресс-бар:
|
201
|
+
```haml
|
202
|
+
%div#progressbar
|
203
|
+
|
204
|
+
:javascript
|
205
|
+
$('#progressbar').progressBar({
|
206
|
+
url: #{job_status_path.to_json}, // адрес джоб-бэкенда (определяется в ваших маршрутах)
|
207
|
+
pid: #{@job_id.to_json}, // job id
|
208
|
+
interval: 1100, // частота опроса джоб-бэкэнда в миллисекундах
|
209
|
+
text: "Initializing" // initializing text appears on progress bar when job is already queued but not started yet
|
210
|
+
}).show();
|
211
|
+
```
|
212
|
+
|
213
|
+
### Для обычных задач Resque
|
214
|
+
```ruby
|
215
|
+
Resque.enqueue(ImageProcessingJob, id=2)
|
216
|
+
```
|
217
|
+
|
218
|
+
## Resque Scheduler
|
219
|
+
|
220
|
+
Расписание для cron-like заданий должно храниться здесь `config/resque_schedule.yml`
|
221
|
+
|
222
|
+
## Resque Retry
|
223
|
+
|
224
|
+
В силу несовместимостей почти всех плагинов с resque-meta (unique основан на нём) - объявить задание перезапускаемым
|
225
|
+
в случае ошибки нужно ДО `unique`
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
class ResqueJobTest
|
229
|
+
include Resque::Integration
|
230
|
+
|
231
|
+
retrys delay: 10, limit: 2
|
232
|
+
unique
|
233
|
+
end
|
234
|
+
```
|
235
|
+
|
236
|
+
## Resque Multi Job Forks
|
237
|
+
|
238
|
+
```yaml
|
239
|
+
workers:
|
240
|
+
'high':
|
241
|
+
count: 1
|
242
|
+
jobs_per_fork: 10
|
243
|
+
```
|
244
|
+
|
245
|
+
## Resque Ordered
|
246
|
+
|
247
|
+
Уникальный по каким-то параметрам джоб может выполняться в одно и тоже время только на одном из воркеров
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
class ResqueJobTest
|
251
|
+
include Resque::Integration
|
252
|
+
|
253
|
+
unique { |company_id, param1| [company_id] }
|
254
|
+
ordered max_iterations: 20 # max_iterations - сколько раз запустится метод `execute` с аргументами в очереди,
|
255
|
+
# прежде чем джоб перепоставится
|
256
|
+
|
257
|
+
def self.execute(ordered_meta, company_id, param1)
|
258
|
+
heavy_lifting_work
|
259
|
+
end
|
260
|
+
end
|
261
|
+
```
|
262
|
+
|
263
|
+
При необходимости, можно добиться уникальности упорядоченных джобов, указав параметры в опции `unique`
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
class UniqueOrderedJob
|
267
|
+
include Resque::Integration
|
268
|
+
|
269
|
+
unique { |company_id, param1| [company_id] }
|
270
|
+
ordered max_iterations: 10, unique: ->(_company_id, param1) { [param1] }
|
271
|
+
...
|
272
|
+
end
|
273
|
+
```
|
274
|
+
|
275
|
+
## Contributing
|
276
|
+
|
277
|
+
1. Fork it ( https://github.com/abak-press/resque-integration/fork )
|
278
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
279
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
280
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
281
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
//модуль для показа прогресс-бара создания отчетов
|
2
|
+
|
3
|
+
app.modules.progressBar = (function(self) {
|
4
|
+
|
5
|
+
function _requestError(data) {
|
6
|
+
var payload = data.payload;
|
7
|
+
|
8
|
+
if (app.config.progressBar.documentLocationError) {
|
9
|
+
alert('Ошибка');
|
10
|
+
location.href = app.config.progressBar.documentLocationError;
|
11
|
+
}
|
12
|
+
|
13
|
+
if (payload && payload.error_messages) {
|
14
|
+
$('.js-pberrors').html(payload.error_messages.join('<br>'));
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
function _requestSuccess(data) {
|
19
|
+
var
|
20
|
+
progress = data.progress;
|
21
|
+
|
22
|
+
if (progress && progress.message) {
|
23
|
+
$('.js-pbstat').text(progress.num + ' из ' + progress.total + ' (' + Math.round(progress.percent) + '%)');
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
function _init() {
|
28
|
+
$('.js-progressbar').progressBar({
|
29
|
+
url: app.config.progressBar.url,
|
30
|
+
pid: app.config.progressBar.pid,
|
31
|
+
onSuccess: function() {
|
32
|
+
location.href = app.config.progressBar.documentLocation;
|
33
|
+
},
|
34
|
+
onError: function(data) {
|
35
|
+
_requestError(data);
|
36
|
+
},
|
37
|
+
onRequestSuccess: function(data) {
|
38
|
+
_requestSuccess(data);
|
39
|
+
}
|
40
|
+
});
|
41
|
+
}
|
42
|
+
|
43
|
+
self.load = function() {
|
44
|
+
_init();
|
45
|
+
};
|
46
|
+
return self;
|
47
|
+
}(app.modules.progressBar || {}));
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Resque
|
2
|
+
class JobsController < ActionController::Metal
|
3
|
+
include ActionController::ConditionalGet
|
4
|
+
|
5
|
+
JOB_ID_PATTERN = /([a-f0-9]{32})/
|
6
|
+
|
7
|
+
def show
|
8
|
+
unless meta
|
9
|
+
self.status = 404
|
10
|
+
self.content_type = "application/json; charset=utf-8"
|
11
|
+
self.response_body = '{"message":"not found"}'
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
data = {
|
16
|
+
enqueued_at: meta.enqueued_at,
|
17
|
+
started_at: meta.started_at,
|
18
|
+
finished_at: meta.finished_at,
|
19
|
+
succeeded: meta.succeeded?,
|
20
|
+
failed: meta.failed?,
|
21
|
+
progress: meta.progress,
|
22
|
+
payload: meta['payload']
|
23
|
+
}
|
24
|
+
|
25
|
+
self.status = 200
|
26
|
+
self.content_type = "application/json; charset=utf-8"
|
27
|
+
self.response_body = MultiJson.dump(data)
|
28
|
+
expires_now
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def meta
|
34
|
+
@meta ||= Resque::Plugins::Meta.get_meta(meta_id) if meta_id
|
35
|
+
end
|
36
|
+
|
37
|
+
def meta_id
|
38
|
+
id = params[:id]
|
39
|
+
id if JOB_ID_PATTERN =~ id
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Resque
|
2
|
+
module Queues
|
3
|
+
class StatusController < ActionController::Metal
|
4
|
+
def show
|
5
|
+
request = params.fetch('request')
|
6
|
+
self.response_body =
|
7
|
+
case request
|
8
|
+
when 'age'
|
9
|
+
age(params['queue'])
|
10
|
+
when 'size'
|
11
|
+
size(params['queue'])
|
12
|
+
when 'failures_count'
|
13
|
+
Resque.queues_info.failures_count_for_queue(params['queue'])
|
14
|
+
when 'threshold_size'
|
15
|
+
Resque.queues_info.threshold_size(params.fetch('queue'))
|
16
|
+
when 'threshold_age'
|
17
|
+
Resque.queues_info.threshold_age(params.fetch('queue'))
|
18
|
+
when /^threshold_failures_per_(?<period>\w+)$/
|
19
|
+
Resque.queues_info.threshold_failures_count(params.fetch('queue'), $LAST_MATCH_INFO['period'])
|
20
|
+
when 'channel'
|
21
|
+
Resque.queues_info.channel(params.fetch('queue'))
|
22
|
+
else
|
23
|
+
0
|
24
|
+
end.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def age(queue)
|
30
|
+
queue ? Resque.queues_info.age_for_queue(queue) : Resque.queues_info.age_overall
|
31
|
+
end
|
32
|
+
|
33
|
+
def size(queue)
|
34
|
+
queue ? Resque.queues_info.size_for_queue(queue) : Resque.queues_info.size_overall
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
- content_for :javascripts_bottom do
|
2
|
+
= javascript_include_tag 'standalone/progress_bar', :defer => true
|
3
|
+
|
4
|
+
#progress
|
5
|
+
.progressbar.js-progressbar
|
6
|
+
#pbstat.js-pbstat
|
7
|
+
#pberrors.pberrors.js-pberrors
|
8
|
+
- if @back_url.present?
|
9
|
+
= link_to 'Закрыть', @back_url
|
10
|
+
|
11
|
+
:javascript
|
12
|
+
app.config.progressBar = {
|
13
|
+
url: #{job_status_path(domain: :current).to_json},
|
14
|
+
pid: #{@job_id.to_json},
|
15
|
+
documentLocation: #{@success_url.to_json},
|
16
|
+
documentLocationError: #{@error_url ? @error_url.to_json : nil.to_json}
|
17
|
+
};
|
data/bin/resque-status
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'redis'
|
5
|
+
require 'resque'
|
6
|
+
require 'active_support/core_ext/hash/slice'
|
7
|
+
require 'active_support/time'
|
8
|
+
|
9
|
+
options = {}
|
10
|
+
|
11
|
+
opt_parser = OptionParser.new do |opt|
|
12
|
+
opt.banner = 'Usage: resque-status [OPTIONS]'
|
13
|
+
opt.separator ''
|
14
|
+
opt.separator 'Resque Status Tool'
|
15
|
+
opt.separator ''
|
16
|
+
|
17
|
+
opt.separator 'Options:'
|
18
|
+
|
19
|
+
opt.on('-k', '--key [KEY]', 'resque info key, default pending') do |value|
|
20
|
+
options[:key] = value
|
21
|
+
end
|
22
|
+
|
23
|
+
opt.on('-h', '--host HOST', 'redis host') do |value|
|
24
|
+
options[:host] = value
|
25
|
+
end
|
26
|
+
|
27
|
+
opt.on('-p', '--port PORT', 'redis port') do |value|
|
28
|
+
options[:port] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
opt.on('-n', '--namespace NAMESPACE', 'redis namespace') do |value|
|
32
|
+
options[:namespace] = value
|
33
|
+
end
|
34
|
+
|
35
|
+
opt.on('--help', 'help') do
|
36
|
+
puts opt_parser
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
opt_parser.parse!
|
42
|
+
|
43
|
+
Resque.redis = Redis.new(options.slice(:host, :port))
|
44
|
+
Resque.redis.namespace = options.fetch(:namespace)
|
45
|
+
|
46
|
+
info = Resque.info
|
47
|
+
key = options.fetch(:key, :pending).to_sym
|
48
|
+
|
49
|
+
if info.key?(key)
|
50
|
+
puts info[key]
|
51
|
+
elsif key == :oldest
|
52
|
+
# oldest job work time in seconds
|
53
|
+
workers = Resque.workers
|
54
|
+
jobs = workers.map(&:job)
|
55
|
+
worker_jobs = workers.zip(jobs).reject { |w, j| w.idle? || j['queue'].nil? }
|
56
|
+
puts worker_jobs.map { |_, job| (Time.now.utc - DateTime.strptime(job['run_at'] ,'%Y-%m-%dT%H:%M:%S').utc).to_i }.max || 'null'
|
57
|
+
else
|
58
|
+
$stderr.puts "Unknown key. Should be one of the [#{info.keys.join(', ')}]"
|
59
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
namespace :resque do
|
3
|
+
namespace :queues do
|
4
|
+
resource :info, only: :show, controller: :info
|
5
|
+
resource :status, only: :show, controller: :status
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
get "#{Rails.application.config.resque_job_status.fetch(:route_path)}(/:id)",
|
10
|
+
to: 'resque/jobs#show',
|
11
|
+
as: 'job_status',
|
12
|
+
constraints: ::Rails.application.config.resque_job_status.fetch(:route_constraints)
|
13
|
+
end
|
data/config.ru
ADDED
data/dip.yml
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
version: '1'
|
2
|
+
|
3
|
+
environment:
|
4
|
+
DOCKER_RUBY_VERSION: '2.2'
|
5
|
+
RUBY_IMAGE_TAG: 2.2-latest
|
6
|
+
COMPOSE_FILE_EXT: development
|
7
|
+
RAILS_ENV: test
|
8
|
+
APRESS_GEMS_CREDENTIALS: ""
|
9
|
+
|
10
|
+
compose:
|
11
|
+
files:
|
12
|
+
- docker-compose.yml
|
13
|
+
- docker-compose.${COMPOSE_FILE_EXT}.yml
|
14
|
+
|
15
|
+
interaction:
|
16
|
+
sh:
|
17
|
+
service: app
|
18
|
+
|
19
|
+
irb:
|
20
|
+
service: app
|
21
|
+
command: irb
|
22
|
+
|
23
|
+
bundle:
|
24
|
+
service: app
|
25
|
+
command: bundle
|
26
|
+
|
27
|
+
appraisal:
|
28
|
+
service: app
|
29
|
+
command: bundle exec appraisal
|
30
|
+
|
31
|
+
rspec:
|
32
|
+
service: app
|
33
|
+
command: bundle exec appraisal bundle exec rspec
|
34
|
+
|
35
|
+
clean:
|
36
|
+
service: app
|
37
|
+
command: rm -f Gemfile.lock gemfiles/*.gemfile.*
|
38
|
+
|
39
|
+
provision:
|
40
|
+
- docker volume create --name bundler_data
|
41
|
+
- dip bundle config --local https://gems.railsc.ru/ ${APRESS_GEMS_CREDENTIALS}
|
42
|
+
- dip clean
|
43
|
+
- dip bundle install
|
44
|
+
- dip appraisal install
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
# Supply generator for Rails 3.0.x or if asset pipeline is not enabled
|
4
|
+
if !defined?(Sprockets) || !::Rails.application.config.assets.enabled
|
5
|
+
module Resque::Integration
|
6
|
+
module Generators
|
7
|
+
class InstallGenerator < ::Rails::Generators::Base
|
8
|
+
|
9
|
+
desc "This generator installs resque-integration (#{Resque::Integration::VERSION}) assets"
|
10
|
+
source_root File.expand_path('../../../../../../vendor/assets', __FILE__)
|
11
|
+
|
12
|
+
def copy_resque_progressbar
|
13
|
+
say_status("copying", "resque-integration (#{Resque::Integration::VERSION})", :green)
|
14
|
+
copy_file "images/progressbar/white.gif", "public/images/progressbar/white.gif"
|
15
|
+
copy_file "javascripts/jquery.progressbar.js", "public/javascripts/jquery.js"
|
16
|
+
copy_file "stylesheets/jquery.progressbar.no_pipeline.css", "public/stylesheets/jquery.progressbar.css"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
else
|
22
|
+
module Resque::Integration
|
23
|
+
module Generators
|
24
|
+
class InstallGenerator < ::Rails::Generators::Base
|
25
|
+
desc "Just show instructions so people will know what to do when mistakenly using generator for Rails 3.1 apps"
|
26
|
+
|
27
|
+
def do_nothing
|
28
|
+
say_status("deprecated", "You are using Rails 3.1 with the asset pipeline enabled, so this generator is not needed.")
|
29
|
+
say_status("", "The necessary files are already in your asset pipeline.")
|
30
|
+
say_status("", "Just add `//= require jquery.progressbar` to your app/assets/javascripts/application.js")
|
31
|
+
say_status("", "If you upgraded your app from Rails 3.0 and still have jquery.progressbar.js in your javascripts, be sure to remove them.")
|
32
|
+
say_status("", "If you do not want the asset pipeline enabled, you may turn it off in application.rb and re-run this generator.")
|
33
|
+
# ok, nothing
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Integration
|
5
|
+
# Extend your job with this class to see full backtrace in resque log
|
6
|
+
module Backtrace
|
7
|
+
def around_perform_backtrace(*)
|
8
|
+
yield
|
9
|
+
rescue => ex
|
10
|
+
$stderr.puts(_format_exception(ex))
|
11
|
+
|
12
|
+
raise
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def _format_exception(exception)
|
17
|
+
bt = exception.backtrace.dup
|
18
|
+
|
19
|
+
"%s %s: %s (%s)\n%s" % [
|
20
|
+
Time.now.to_s,
|
21
|
+
bt.shift,
|
22
|
+
exception.message,
|
23
|
+
exception.class.to_s,
|
24
|
+
bt.map { |line| ' ' * 4 + line }.join("\n")
|
25
|
+
]
|
26
|
+
end
|
27
|
+
end # module Backtrace
|
28
|
+
end # module Integration
|
29
|
+
end # module Resque
|