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
@@ -0,0 +1,95 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Resque
|
3
|
+
module Integration
|
4
|
+
class LogsRotator
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Перенаправить стандартный вывод в файл
|
8
|
+
def redirect_std
|
9
|
+
log_path = ::Resque.config.log_file
|
10
|
+
STDOUT.reopen File.open(log_path, 'a')
|
11
|
+
STDERR.reopen File.open(log_path, 'a')
|
12
|
+
STDOUT.sync = STDERR.sync = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def register_hup_signal
|
16
|
+
Signal.trap('HUP') { ::Resque::Integration::LogsRotator.reopen_logs }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Переоткрытие всех логов
|
20
|
+
#
|
21
|
+
# @see Unicorn::Utils
|
22
|
+
def reopen_logs
|
23
|
+
to_reopen = []
|
24
|
+
nr = 0
|
25
|
+
ObjectSpace.each_object(File) { |fp| is_log?(fp) and to_reopen << fp }
|
26
|
+
|
27
|
+
to_reopen.each do |fp|
|
28
|
+
orig_st = begin
|
29
|
+
fp.stat
|
30
|
+
rescue IOError, Errno::EBADF # race
|
31
|
+
next
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
b = File.stat(fp.path)
|
36
|
+
next if orig_st.ino == b.ino && orig_st.dev == b.dev
|
37
|
+
rescue Errno::ENOENT
|
38
|
+
end
|
39
|
+
|
40
|
+
begin
|
41
|
+
# stdin, stdout, stderr are special. The following dance should
|
42
|
+
# guarantee there is no window where `fp' is unwritable in MRI
|
43
|
+
# (or any correct Ruby implementation).
|
44
|
+
#
|
45
|
+
# Fwiw, GVL has zero bearing here. This is tricky because of
|
46
|
+
# the unavoidable existence of stdio FILE * pointers for
|
47
|
+
# std{in,out,err} in all programs which may use the standard C library
|
48
|
+
if fp.fileno <= 2
|
49
|
+
# We do not want to hit fclose(3)->dup(2) window for std{in,out,err}
|
50
|
+
# MRI will use freopen(3) here internally on std{in,out,err}
|
51
|
+
fp.reopen(fp.path, "a")
|
52
|
+
else
|
53
|
+
# We should not need this workaround, Ruby can be fixed:
|
54
|
+
# http://bugs.ruby-lang.org/issues/9036
|
55
|
+
# MRI will not call call fclose(3) or freopen(3) here
|
56
|
+
# since there's no associated std{in,out,err} FILE * pointer
|
57
|
+
# This should atomically use dup3(2) (or dup2(2)) syscall
|
58
|
+
File.open(fp.path, "a") { |tmpfp| fp.reopen(tmpfp) }
|
59
|
+
end
|
60
|
+
|
61
|
+
fp.sync = true
|
62
|
+
fp.flush # IO#sync=true may not implicitly flush
|
63
|
+
new_st = fp.stat
|
64
|
+
|
65
|
+
# this should only happen in the master:
|
66
|
+
if orig_st.uid != new_st.uid || orig_st.gid != new_st.gid
|
67
|
+
fp.chown(orig_st.uid, orig_st.gid)
|
68
|
+
end
|
69
|
+
|
70
|
+
nr += 1
|
71
|
+
rescue IOError, Errno::EBADF
|
72
|
+
# not much we can do...
|
73
|
+
end
|
74
|
+
end
|
75
|
+
nr
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# @see Unicorn::Utils
|
81
|
+
def is_log?(fp)
|
82
|
+
append_flags = File::WRONLY | File::APPEND
|
83
|
+
|
84
|
+
! fp.closed? &&
|
85
|
+
fp.stat.file? &&
|
86
|
+
fp.sync &&
|
87
|
+
(fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
|
88
|
+
rescue IOError, Errno::EBADF
|
89
|
+
false
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# Ordered Job
|
2
|
+
#
|
3
|
+
# Ensures that only one job for a given queue
|
4
|
+
# will be running on any worker at a given time
|
5
|
+
#
|
6
|
+
# Examples:
|
7
|
+
#
|
8
|
+
# class TestJob
|
9
|
+
# include Resque::Integration
|
10
|
+
#
|
11
|
+
# unique { |company_id, param1| [company_id] }
|
12
|
+
# ordered max_iterations: 10
|
13
|
+
#
|
14
|
+
# def self.execute(meta, company_id, param1)
|
15
|
+
# heavy_lifting_work
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# class UniqueTestJob
|
20
|
+
# include Resque::Integration
|
21
|
+
#
|
22
|
+
# unique { |company_id, param1| [company_id] }
|
23
|
+
# ordered max_iterations: 10, unique: ->(_company_id, param1) { [param1] }
|
24
|
+
# ...
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
module Resque
|
28
|
+
module Integration
|
29
|
+
module Ordered
|
30
|
+
ARGS_EXPIRATION = 1.week
|
31
|
+
|
32
|
+
def self.extended(base)
|
33
|
+
unless base.singleton_class.include?(::Resque::Integration::Unique)
|
34
|
+
base.extend ::Resque::Integration::Unique
|
35
|
+
end
|
36
|
+
|
37
|
+
unless base.singleton_class.include?(::Resque::Integration::Continuous)
|
38
|
+
base.extend ::Resque::Integration::Continuous
|
39
|
+
end
|
40
|
+
|
41
|
+
base.singleton_class.class_eval do
|
42
|
+
attr_accessor :max_iterations, :uniqueness
|
43
|
+
|
44
|
+
prepend Overrides
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Uniqueness
|
49
|
+
def initialize(&block)
|
50
|
+
@unique_block = block
|
51
|
+
end
|
52
|
+
|
53
|
+
def key(meta_id)
|
54
|
+
"ordered:unique:#{meta_id}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove(meta_id, args)
|
58
|
+
Resque.redis.hdel(key(meta_id), encoded_unique_args(args))
|
59
|
+
end
|
60
|
+
|
61
|
+
def size(meta_id)
|
62
|
+
Resque.redis.hlen(key(meta_id)).to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
def encoded_unique_args(args)
|
66
|
+
Resque.encode(@unique_block.call(*args))
|
67
|
+
end
|
68
|
+
|
69
|
+
def ordered_meta_id(meta_id, args)
|
70
|
+
Resque.redis.hget(key(meta_id), encoded_unique_args(args))
|
71
|
+
end
|
72
|
+
|
73
|
+
def set(meta_id, args, ordered_meta_id)
|
74
|
+
unique_key = key(meta_id)
|
75
|
+
|
76
|
+
if Resque.redis.hset(unique_key, encoded_unique_args(args), ordered_meta_id)
|
77
|
+
Resque.redis.expire(unique_key, ARGS_EXPIRATION)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
module Overrides
|
83
|
+
def enqueue(*args)
|
84
|
+
meta = super
|
85
|
+
|
86
|
+
if uniqueness && ordered_meta_id = uniqueness.ordered_meta_id(meta.meta_id, args)
|
87
|
+
return get_meta(ordered_meta_id)
|
88
|
+
end
|
89
|
+
|
90
|
+
ordered_meta = ::Resque::Plugins::Meta::Metadata.new('meta_id' => ordered_meta_id(args), 'job_class' => self)
|
91
|
+
ordered_meta.save
|
92
|
+
|
93
|
+
uniqueness.set(meta.meta_id, args, ordered_meta.meta_id) if uniqueness
|
94
|
+
args.unshift(ordered_meta.meta_id)
|
95
|
+
encoded_args = ::Resque.encode(args)
|
96
|
+
args_key = ordered_queue_key(meta.meta_id)
|
97
|
+
|
98
|
+
::Resque.redis.rpush(args_key, encoded_args)
|
99
|
+
::Resque.redis.expire(args_key, ARGS_EXPIRATION)
|
100
|
+
|
101
|
+
ordered_meta
|
102
|
+
end
|
103
|
+
|
104
|
+
def perform(meta_id, *)
|
105
|
+
args_key = ordered_queue_key(meta_id)
|
106
|
+
i = 1
|
107
|
+
while job_args = ::Resque.redis.lpop(args_key)
|
108
|
+
job_args = ::Resque.decode(job_args)
|
109
|
+
ordered_meta = get_meta(job_args.shift)
|
110
|
+
ordered_meta.start!
|
111
|
+
|
112
|
+
begin
|
113
|
+
execute(ordered_meta, *job_args)
|
114
|
+
rescue Exception
|
115
|
+
ordered_meta.fail!
|
116
|
+
raise
|
117
|
+
ensure
|
118
|
+
uniqueness.remove(meta_id, job_args) if uniqueness
|
119
|
+
end
|
120
|
+
|
121
|
+
ordered_meta.finish!
|
122
|
+
|
123
|
+
i += 1
|
124
|
+
return continue if max_iterations && i > max_iterations && ordered_queue_size(meta_id) > 0
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def ordered_queue_size(meta_id)
|
130
|
+
Resque.redis.llen(ordered_queue_key(meta_id)).to_i
|
131
|
+
end
|
132
|
+
|
133
|
+
def ordered_queue_key(meta_id)
|
134
|
+
"ordered:#{meta_id}"
|
135
|
+
end
|
136
|
+
|
137
|
+
def ordered_meta_id(args)
|
138
|
+
Digest::SHA1.hexdigest([Time.now.to_f, rand, self, args].join)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Resque
|
2
|
+
module Integration
|
3
|
+
# Public: job with priority queues
|
4
|
+
#
|
5
|
+
# Examples:
|
6
|
+
# class MyJob
|
7
|
+
# include Resque::Integration
|
8
|
+
#
|
9
|
+
# queue :foo
|
10
|
+
# prioritized
|
11
|
+
#
|
12
|
+
# def self.perform(arg)
|
13
|
+
# heavy_lifting_work
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# MyJob.enqueue_with_priority(:high, 1, another_param: 2) # enqueue job to :foo_high queue
|
18
|
+
# MyJob.enqueue_with_priority(:low, 1, another_param: 2) # enqueue job to :foo_low queue
|
19
|
+
#
|
20
|
+
# class MyUniqueJob
|
21
|
+
# include Resque::Integration
|
22
|
+
#
|
23
|
+
# queue :foo
|
24
|
+
# unique
|
25
|
+
# prioritized
|
26
|
+
#
|
27
|
+
# def self.execute(*args)
|
28
|
+
# meta = get_meta
|
29
|
+
#
|
30
|
+
# heavy_lifting_work do
|
31
|
+
# meta[:count] += 1
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
module Priority
|
36
|
+
def self.extended(base)
|
37
|
+
base.singleton_class.prepend(Overrides)
|
38
|
+
end
|
39
|
+
|
40
|
+
module Overrides
|
41
|
+
# Public: enqueue job with normal priority
|
42
|
+
#
|
43
|
+
# Example:
|
44
|
+
# MyJob.enqueue(1)
|
45
|
+
def enqueue(*args)
|
46
|
+
enqueue_with_priority(:normal, *args)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: dequeue job with priority
|
50
|
+
#
|
51
|
+
# Example:
|
52
|
+
# MyJob.dequeue(:high, 1)
|
53
|
+
def dequeue(priority, *args)
|
54
|
+
if unique?
|
55
|
+
super(*args, priority)
|
56
|
+
else
|
57
|
+
Resque.dequeue(self, *args, priority)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def perform(*args, _priority)
|
62
|
+
super(*args)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def priority?
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
# Public: enqueue job to priority queue
|
71
|
+
#
|
72
|
+
# Example:
|
73
|
+
# MyJob.enqueue_with_priority(:high, 1)
|
74
|
+
def enqueue_with_priority(priority, *args)
|
75
|
+
queue = priority_queue(priority)
|
76
|
+
|
77
|
+
if unique?
|
78
|
+
enqueue_to(queue, *args, priority)
|
79
|
+
else
|
80
|
+
Resque.enqueue_to(queue, self, *args, priority)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def priority_queue(priority)
|
85
|
+
priority.to_sym == :normal ? queue : "#{queue}_#{priority}".to_sym
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Resque
|
2
|
+
module Integration
|
3
|
+
class QueuesInfo
|
4
|
+
class Age
|
5
|
+
def initialize(config)
|
6
|
+
@config = config
|
7
|
+
end
|
8
|
+
|
9
|
+
def time(queue)
|
10
|
+
from_time = Time.now.utc
|
11
|
+
max_secs = 0
|
12
|
+
|
13
|
+
jobs.each do |job|
|
14
|
+
next unless job['queue'] == queue
|
15
|
+
job_secs = seconds_for(job, from_time)
|
16
|
+
max_secs = job_secs if job_secs > max_secs
|
17
|
+
end
|
18
|
+
|
19
|
+
max_secs
|
20
|
+
end
|
21
|
+
|
22
|
+
def overall
|
23
|
+
from_time = Time.now.utc
|
24
|
+
|
25
|
+
max_secs = 0
|
26
|
+
|
27
|
+
jobs.each do |job|
|
28
|
+
next unless job['queue']
|
29
|
+
job_secs = seconds_for(job, from_time)
|
30
|
+
next if job_secs < threshold(job['queue'])
|
31
|
+
max_secs = job_secs if job_secs > max_secs
|
32
|
+
end
|
33
|
+
|
34
|
+
max_secs
|
35
|
+
end
|
36
|
+
|
37
|
+
def threshold(queue)
|
38
|
+
@config.max_age(queue)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def jobs
|
44
|
+
Resque.workers.each_with_object([]) { |worker, memo| memo << worker.job unless worker.idle? }
|
45
|
+
end
|
46
|
+
|
47
|
+
def seconds_for(job, from_time)
|
48
|
+
(from_time - DateTime.strptime(job['run_at'], '%Y-%m-%dT%H:%M:%S').utc).to_i
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Integration
|
5
|
+
class QueuesInfo
|
6
|
+
class Config
|
7
|
+
def initialize(config_path)
|
8
|
+
config = load_config(config_path)
|
9
|
+
@defaults = config['defaults']
|
10
|
+
@queues = expand_config(config['queues'])
|
11
|
+
end
|
12
|
+
|
13
|
+
def max_age(queue)
|
14
|
+
threshold(queue, 'max_age')
|
15
|
+
end
|
16
|
+
|
17
|
+
def warn_age(queue)
|
18
|
+
threshold(queue, 'warn_age')
|
19
|
+
end
|
20
|
+
|
21
|
+
def max_size(queue)
|
22
|
+
threshold(queue, 'max_size')
|
23
|
+
end
|
24
|
+
|
25
|
+
def warn_size(queue)
|
26
|
+
threshold(queue, 'warn_size')
|
27
|
+
end
|
28
|
+
|
29
|
+
def max_failures_count(queue, period)
|
30
|
+
threshold(queue, "max_failures_count_per_#{period}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def warn_failures_count(queue, period)
|
34
|
+
threshold(queue, "warn_failures_count_per_#{period}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def channel(queue)
|
38
|
+
channel = threshold(queue, 'channel')
|
39
|
+
Array.wrap(channel).join(' ')
|
40
|
+
end
|
41
|
+
|
42
|
+
def warn_channel(queue)
|
43
|
+
channel = threshold(queue, 'warn_channel')
|
44
|
+
Array.wrap(channel).join(' ')
|
45
|
+
end
|
46
|
+
|
47
|
+
def data
|
48
|
+
@data ||= @queues.map do |queue_name, _queue_params|
|
49
|
+
{
|
50
|
+
'{#QUEUE}' => queue_name,
|
51
|
+
|
52
|
+
'{#CHANNEL}' => channel(queue_name),
|
53
|
+
'{#THRESHOLD_AGE}' => max_age(queue_name),
|
54
|
+
'{#THRESHOLD_SIZE}' => max_size(queue_name),
|
55
|
+
'{#THRESHOLD_FAILURES_PER_5M}' => max_failures_count(queue_name, '5m'),
|
56
|
+
'{#THRESHOLD_FAILURES_PER_1H}' => max_failures_count(queue_name, '1h'),
|
57
|
+
|
58
|
+
'{#WARNING_CHANNEL}' => warn_channel(queue_name),
|
59
|
+
'{#WARNING_AGE}' => warn_age(queue_name),
|
60
|
+
'{#WARNING_SIZE}' => warn_size(queue_name),
|
61
|
+
'{#WARNING_FAILURES_PER_5M}' => warn_failures_count(queue_name, '5m'),
|
62
|
+
'{#WARNING_FAILURES_PER_1H}' => warn_failures_count(queue_name, '1h')
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def threshold(queue, param)
|
70
|
+
@queues[queue].try(:[], param) || @defaults[param]
|
71
|
+
end
|
72
|
+
|
73
|
+
def load_config(path)
|
74
|
+
input = File.read(path)
|
75
|
+
input = ERB.new(input).result if defined?(ERB)
|
76
|
+
YAML.load(input)
|
77
|
+
end
|
78
|
+
|
79
|
+
def expand_config(config)
|
80
|
+
expanded_config = {}
|
81
|
+
|
82
|
+
config.keys.each do |key|
|
83
|
+
key.split(',').each do |queue|
|
84
|
+
queue.chomp!
|
85
|
+
queue.strip!
|
86
|
+
|
87
|
+
(expanded_config[queue] ||= {}).merge!(config[key])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
expanded_config
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Resque
|
2
|
+
module Integration
|
3
|
+
class QueuesInfo
|
4
|
+
class Size
|
5
|
+
def initialize(config)
|
6
|
+
@config = config
|
7
|
+
end
|
8
|
+
|
9
|
+
def size(queue)
|
10
|
+
Resque.size(queue) || 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def overall
|
14
|
+
max = 0
|
15
|
+
|
16
|
+
Resque.queues.each do |queue|
|
17
|
+
size = Resque.size(queue).to_i
|
18
|
+
next if size < threshold(queue)
|
19
|
+
max = size if size > max
|
20
|
+
end
|
21
|
+
|
22
|
+
max
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def threshold(queue)
|
28
|
+
@config.max_size(queue)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Resque
|
2
|
+
module Integration
|
3
|
+
class QueuesInfo
|
4
|
+
autoload :Age, "resque/integration/queues_info/age"
|
5
|
+
autoload :Size, "resque/integration/queues_info/size"
|
6
|
+
autoload :Config, "resque/integration/queues_info/config"
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@config = Config.new(options.fetch(:config))
|
10
|
+
@size = Size.new(@config)
|
11
|
+
@age = Age.new(@config)
|
12
|
+
end
|
13
|
+
|
14
|
+
def age_for_queue(queue)
|
15
|
+
@age.time(queue)
|
16
|
+
end
|
17
|
+
|
18
|
+
def age_overall
|
19
|
+
@age.overall
|
20
|
+
end
|
21
|
+
|
22
|
+
def size_for_queue(queue)
|
23
|
+
@size.size(queue)
|
24
|
+
end
|
25
|
+
|
26
|
+
def size_overall
|
27
|
+
@size.overall
|
28
|
+
end
|
29
|
+
|
30
|
+
def failures_count_for_queue(queue)
|
31
|
+
Resque::Integration::FailureBackends::QueuesTotals.count(queue)
|
32
|
+
end
|
33
|
+
|
34
|
+
def threshold_size(queue)
|
35
|
+
@config.max_size(queue)
|
36
|
+
end
|
37
|
+
|
38
|
+
def threshold_age(queue)
|
39
|
+
@config.max_age(queue)
|
40
|
+
end
|
41
|
+
|
42
|
+
def threshold_failures_count(queue, period)
|
43
|
+
@config.max_failures_count(queue, period)
|
44
|
+
end
|
45
|
+
|
46
|
+
def channel(queue)
|
47
|
+
@config.channel(queue)
|
48
|
+
end
|
49
|
+
|
50
|
+
def data
|
51
|
+
@config.data
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
namespace :resque do
|
5
|
+
# Здесь мы добавляем некоторые необходимые для корректной работы "хуки".
|
6
|
+
#
|
7
|
+
# @see https://github.com/resque/resque/tree/1-x-stable#workers
|
8
|
+
task :setup => :environment do
|
9
|
+
# принудительно инициализируем приложение
|
10
|
+
# (rails 3 не делают этого при запуске из rake-задачи)
|
11
|
+
Rails.application.eager_load! if Rails::VERSION::MAJOR < 4
|
12
|
+
|
13
|
+
# Включаем логирование в resque
|
14
|
+
ENV['VERBOSE'] = '1'
|
15
|
+
|
16
|
+
# перенаправление вывода в файл
|
17
|
+
Resque::Integration::LogsRotator.redirect_std
|
18
|
+
# слушать HUP сигнал для ротации логов
|
19
|
+
Resque::Integration::LogsRotator.register_hup_signal
|
20
|
+
|
21
|
+
# Нужно закрыть все соединения в **родительском** процессе,
|
22
|
+
# Нужно также закрыть соединение к memcache
|
23
|
+
Resque.before_first_fork do
|
24
|
+
ActiveRecord::Base.connection_handler.clear_all_connections!
|
25
|
+
Rails.cache.reset if Rails.cache.respond_to?(:reset)
|
26
|
+
end
|
27
|
+
|
28
|
+
Resque.before_fork do
|
29
|
+
Resque.redis.client.disconnect
|
30
|
+
|
31
|
+
ActiveRecord::Base.connection_handler.clear_all_connections!
|
32
|
+
end
|
33
|
+
|
34
|
+
Resque.after_fork do |job|
|
35
|
+
$0 = "resque-#{Resque::Version}: Processing #{job.queue}/#{job.payload['class']} since #{Time.now.to_s(:db)}"
|
36
|
+
|
37
|
+
ActiveRecord::Base.establish_connection
|
38
|
+
|
39
|
+
Resque.redis.client.connect
|
40
|
+
end
|
41
|
+
|
42
|
+
# Support for resque-multi-job-forks
|
43
|
+
require 'resque-multi-job-forks' if ENV['JOBS_PER_FORK'] || ENV['MINUTES_PER_FORK']
|
44
|
+
|
45
|
+
if Resque.config.run_at_exit_hooks? && ENV['RUN_AT_EXIT_HOOKS'].nil?
|
46
|
+
ENV['RUN_AT_EXIT_HOOKS'] = '1'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'resque/integration/unique'
|
2
|
+
|
3
|
+
namespace :resque do
|
4
|
+
namespace :lock do
|
5
|
+
task expire_all: :environment do
|
6
|
+
puts "Start expiring all resque locks"
|
7
|
+
|
8
|
+
redis = ::Resque.redis
|
9
|
+
cursor = 0
|
10
|
+
batch_size = 10_000
|
11
|
+
timeout = ::Resque::Integration::Unique::LOCK_TIMEOUT
|
12
|
+
count = 0
|
13
|
+
pattern = "lock:*"
|
14
|
+
|
15
|
+
loop do
|
16
|
+
cursor, keys = redis.scan(cursor, count: batch_size, match: pattern)
|
17
|
+
cursor = cursor.to_i
|
18
|
+
|
19
|
+
unless keys.empty?
|
20
|
+
redis.pipelined do
|
21
|
+
keys.each do |key|
|
22
|
+
redis.expire(key, timeout)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
count += keys.size
|
27
|
+
puts "Expired #{count}..."
|
28
|
+
end
|
29
|
+
|
30
|
+
break if cursor.zero?
|
31
|
+
end
|
32
|
+
|
33
|
+
puts "Expired total #{count} keys."
|
34
|
+
puts "Done."
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|