backburner-allq 1.0.0
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/.gitignore +17 -0
- data/.travis.yml +29 -0
- data/CHANGELOG.md +133 -0
- data/CONTRIBUTING.md +37 -0
- data/Gemfile +4 -0
- data/HOOKS.md +99 -0
- data/LICENSE +22 -0
- data/README.md +658 -0
- data/Rakefile +17 -0
- data/TODO +4 -0
- data/backburner-allq.gemspec +26 -0
- data/bin/backburner +7 -0
- data/circle.yml +3 -0
- data/deploy.sh +3 -0
- data/examples/custom.rb +25 -0
- data/examples/demo.rb +60 -0
- data/examples/god.rb +46 -0
- data/examples/hooked.rb +87 -0
- data/examples/retried.rb +31 -0
- data/examples/simple.rb +43 -0
- data/examples/stress.rb +31 -0
- data/lib/backburner.rb +75 -0
- data/lib/backburner/allq_wrapper.rb +317 -0
- data/lib/backburner/async_proxy.rb +25 -0
- data/lib/backburner/cli.rb +53 -0
- data/lib/backburner/configuration.rb +48 -0
- data/lib/backburner/connection.rb +157 -0
- data/lib/backburner/helpers.rb +193 -0
- data/lib/backburner/hooks.rb +53 -0
- data/lib/backburner/job.rb +118 -0
- data/lib/backburner/logger.rb +53 -0
- data/lib/backburner/performable.rb +95 -0
- data/lib/backburner/queue.rb +145 -0
- data/lib/backburner/tasks.rb +54 -0
- data/lib/backburner/version.rb +3 -0
- data/lib/backburner/worker.rb +221 -0
- data/lib/backburner/workers/forking.rb +52 -0
- data/lib/backburner/workers/simple.rb +29 -0
- data/lib/backburner/workers/threading.rb +163 -0
- data/lib/backburner/workers/threads_on_fork.rb +263 -0
- data/test/async_proxy_test.rb +36 -0
- data/test/back_burner_test.rb +88 -0
- data/test/connection_test.rb +179 -0
- data/test/fixtures/hooked.rb +122 -0
- data/test/fixtures/test_fork_jobs.rb +72 -0
- data/test/fixtures/test_forking_jobs.rb +56 -0
- data/test/fixtures/test_jobs.rb +87 -0
- data/test/fixtures/test_queue_settings.rb +14 -0
- data/test/helpers/templogger.rb +22 -0
- data/test/helpers_test.rb +278 -0
- data/test/hooks_test.rb +112 -0
- data/test/job_test.rb +185 -0
- data/test/logger_test.rb +44 -0
- data/test/performable_test.rb +88 -0
- data/test/queue_test.rb +69 -0
- data/test/test_helper.rb +128 -0
- data/test/worker_test.rb +157 -0
- data/test/workers/forking_worker_test.rb +181 -0
- data/test/workers/simple_worker_test.rb +350 -0
- data/test/workers/threading_worker_test.rb +104 -0
- data/test/workers/threads_on_fork_worker_test.rb +484 -0
- metadata +217 -0
@@ -0,0 +1,193 @@
|
|
1
|
+
module Backburner
|
2
|
+
module Helpers
|
3
|
+
# Loads in instance and class levels
|
4
|
+
def self.included(base)
|
5
|
+
base.extend self
|
6
|
+
end
|
7
|
+
|
8
|
+
# Prints out exception_message based on specified e
|
9
|
+
def exception_message(e)
|
10
|
+
msg = [ "Exception #{e.class} -> #{e.message}" ]
|
11
|
+
|
12
|
+
base = File.expand_path(Dir.pwd) + '/'
|
13
|
+
e.backtrace.each do |t|
|
14
|
+
msg << " #{File.expand_path(t).gsub(/#{base}/, '')}"
|
15
|
+
end if e.backtrace
|
16
|
+
|
17
|
+
msg.join("\n")
|
18
|
+
end
|
19
|
+
|
20
|
+
# Given a word with dashes, returns a camel cased version of it.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# classify('job-name') # => 'JobName'
|
24
|
+
#
|
25
|
+
def classify(dashed_word)
|
26
|
+
dashed_word.to_s.split('-').each { |part| part[0] = part[0].chr.upcase }.join
|
27
|
+
end
|
28
|
+
|
29
|
+
# Given a class, dasherizes the name, used for getting tube names
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# dasherize('JobName') # => "job-name"
|
33
|
+
#
|
34
|
+
def dasherize(word)
|
35
|
+
classify(word).to_s.gsub(/::/, '/').
|
36
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
37
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
38
|
+
tr("_", "-").downcase
|
39
|
+
end
|
40
|
+
|
41
|
+
# Tries to find a constant with the name specified in the argument string:
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# constantize("Module") # => Module
|
45
|
+
# constantize("Test::Unit") # => Test::Unit
|
46
|
+
#
|
47
|
+
# NameError is raised when the constant is unknown.
|
48
|
+
def constantize(camel_cased_word)
|
49
|
+
camel_cased_word = camel_cased_word.to_s
|
50
|
+
|
51
|
+
if camel_cased_word.include?('-')
|
52
|
+
camel_cased_word = classify(camel_cased_word)
|
53
|
+
end
|
54
|
+
|
55
|
+
names = camel_cased_word.split('::')
|
56
|
+
names.shift if names.empty? || names.first.empty?
|
57
|
+
|
58
|
+
constant = Object
|
59
|
+
names.each do |name|
|
60
|
+
args = Module.method(:const_get).arity != 1 ? [false] : []
|
61
|
+
|
62
|
+
if constant.const_defined?(name, *args)
|
63
|
+
constant = constant.const_get(name)
|
64
|
+
else
|
65
|
+
constant = constant.const_missing(name)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
constant
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns configuration options for backburner
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# queue_config.max_job_retries => 3
|
75
|
+
#
|
76
|
+
def queue_config
|
77
|
+
Backburner.configuration
|
78
|
+
end
|
79
|
+
|
80
|
+
# Expands a tube to include the prefix
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# expand_tube_name("foo_with_settings:3:100:6") # => <prefix>.foo_with_settings
|
84
|
+
# expand_tube_name("foo") # => <prefix>.foo
|
85
|
+
# expand_tube_name(FooJob) # => <prefix>.foo-job
|
86
|
+
#
|
87
|
+
def expand_tube_name(tube)
|
88
|
+
prefix = queue_config.tube_namespace
|
89
|
+
separator = queue_config.namespace_separator
|
90
|
+
queue_name = if tube.is_a?(String)
|
91
|
+
tube
|
92
|
+
elsif tube.respond_to?(:queue) # use queue name
|
93
|
+
queue = tube.queue
|
94
|
+
queue.is_a?(Proc) ? queue.call(tube) : queue
|
95
|
+
elsif tube.is_a?(Proc)
|
96
|
+
tube.call
|
97
|
+
elsif tube.is_a?(Class) # no queue name, use default
|
98
|
+
queue_config.primary_queue # tube.name
|
99
|
+
else # turn into a string
|
100
|
+
tube.to_s
|
101
|
+
end
|
102
|
+
[prefix.gsub(/\.$/, ''), dasherize(queue_name).gsub(/^#{prefix}/, '')].join(separator).gsub(/#{Regexp::escape(separator)}+/, separator).split(':').first
|
103
|
+
end
|
104
|
+
|
105
|
+
# Resolves job priority based on the value given. Can be integer, a class or nothing
|
106
|
+
#
|
107
|
+
# @example
|
108
|
+
# resolve_priority(1000) => 1000
|
109
|
+
# resolve_priority(FooBar) => <queue priority>
|
110
|
+
# resolve_priority(nil) => <default priority>
|
111
|
+
#
|
112
|
+
def resolve_priority(pri)
|
113
|
+
if pri.respond_to?(:queue_priority)
|
114
|
+
resolve_priority(pri.queue_priority)
|
115
|
+
elsif pri.is_a?(String) || pri.is_a?(Symbol) # named priority
|
116
|
+
resolve_priority(Backburner.configuration.priority_labels[pri.to_sym])
|
117
|
+
elsif pri.is_a?(Integer) # numerical
|
118
|
+
pri
|
119
|
+
else # default
|
120
|
+
Backburner.configuration.default_priority
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Resolves job respond timeout based on the value given. Can be integer, a class or nothing
|
125
|
+
#
|
126
|
+
# @example
|
127
|
+
# resolve_respond_timeout(1000) => 1000
|
128
|
+
# resolve_respond_timeout(FooBar) => <queue respond_timeout>
|
129
|
+
# resolve_respond_timeout(nil) => <default respond_timeout>
|
130
|
+
#
|
131
|
+
def resolve_respond_timeout(ttr)
|
132
|
+
if ttr.respond_to?(:queue_respond_timeout)
|
133
|
+
resolve_respond_timeout(ttr.queue_respond_timeout)
|
134
|
+
elsif ttr.is_a?(Integer) # numerical
|
135
|
+
ttr
|
136
|
+
else # default
|
137
|
+
Backburner.configuration.respond_timeout
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Resolves max retries based on the value given. Can be integer, a class or nothing
|
142
|
+
#
|
143
|
+
# @example
|
144
|
+
# resolve_max_job_retries(5) => 5
|
145
|
+
# resolve_max_job_retries(FooBar) => <queue max_job_retries>
|
146
|
+
# resolve_max_job_retries(nil) => <default max_job_retries>
|
147
|
+
#
|
148
|
+
def resolve_max_job_retries(retries)
|
149
|
+
if retries.respond_to?(:queue_max_job_retries)
|
150
|
+
resolve_max_job_retries(retries.queue_max_job_retries)
|
151
|
+
elsif retries.is_a?(Integer) # numerical
|
152
|
+
retries
|
153
|
+
else # default
|
154
|
+
Backburner.configuration.max_job_retries
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Resolves retry delay based on the value given. Can be integer, a class or nothing
|
159
|
+
#
|
160
|
+
# @example
|
161
|
+
# resolve_retry_delay(5) => 5
|
162
|
+
# resolve_retry_delay(FooBar) => <queue retry_delay>
|
163
|
+
# resolve_retry_delay(nil) => <default retry_delay>
|
164
|
+
#
|
165
|
+
def resolve_retry_delay(delay)
|
166
|
+
if delay.respond_to?(:queue_retry_delay)
|
167
|
+
resolve_retry_delay(delay.queue_retry_delay)
|
168
|
+
elsif delay.is_a?(Integer) # numerical
|
169
|
+
delay
|
170
|
+
else # default
|
171
|
+
Backburner.configuration.retry_delay
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Resolves retry delay proc based on the value given. Can be proc, a class or nothing
|
176
|
+
#
|
177
|
+
# @example
|
178
|
+
# resolve_retry_delay_proc(proc) => proc
|
179
|
+
# resolve_retry_delay_proc(FooBar) => <queue retry_delay_proc>
|
180
|
+
# resolve_retry_delay_proc(nil) => <default retry_delay_proc>
|
181
|
+
#
|
182
|
+
def resolve_retry_delay_proc(proc)
|
183
|
+
if proc.respond_to?(:queue_retry_delay_proc)
|
184
|
+
resolve_retry_delay_proc(proc.queue_retry_delay_proc)
|
185
|
+
elsif proc.is_a?(Proc)
|
186
|
+
proc
|
187
|
+
else # default
|
188
|
+
Backburner.configuration.retry_delay_proc
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end # Helpers
|
193
|
+
end # Backburner
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Backburner
|
2
|
+
class Hooks
|
3
|
+
class << self
|
4
|
+
# Triggers all method hooks that match the given event type with specified arguments.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# invoke_hook_events(hookable, :before_enqueue, 'some', 'args')
|
8
|
+
# invoke_hook_events(hookable, :after_perform, 5)
|
9
|
+
#
|
10
|
+
def invoke_hook_events(hookable, event, *args)
|
11
|
+
res = find_hook_events(hookable, event).map { |e| hookable.send(e, *args) }
|
12
|
+
return false if res.any? { |result| result == false }
|
13
|
+
res
|
14
|
+
end
|
15
|
+
|
16
|
+
# Triggers all method hooks that match given around event type. Used for 'around' hooks
|
17
|
+
# that stack over the original task cumulatively onto one another.
|
18
|
+
#
|
19
|
+
# The final block will be the one that actually invokes the
|
20
|
+
# original task after calling all other around blocks.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# around_hook_events(hookable, :around_perform) { hookable.perform }
|
24
|
+
#
|
25
|
+
def around_hook_events(hookable, event, *args, &block)
|
26
|
+
raise "Please pass a block to hook events!" unless block_given?
|
27
|
+
around_hooks = find_hook_events(hookable, event).reverse
|
28
|
+
aggregate_filter = Proc.new { |&blk| blk.call }
|
29
|
+
around_hooks.each do |ah|
|
30
|
+
prior_around_filter = aggregate_filter
|
31
|
+
aggregate_filter = Proc.new do |&blk|
|
32
|
+
hookable.method(ah).call(*args) do
|
33
|
+
prior_around_filter.call(&blk)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
aggregate_filter.call(&block)
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# Returns all methods that match given hook type
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# find_hook_events(:before_enqueue)
|
46
|
+
# # => ['before_enqueue_foo', 'before_enqueue_bar']
|
47
|
+
#
|
48
|
+
def find_hook_events(hookable, event)
|
49
|
+
(hookable.methods - Object.methods).grep(/^#{event}/).sort
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end # Hooks
|
53
|
+
end # Backburner
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Backburner
|
2
|
+
# A single backburner job which can be processed and removed by the worker
|
3
|
+
class Job < SimpleDelegator
|
4
|
+
include Backburner::Helpers
|
5
|
+
|
6
|
+
# Raises when a job times out
|
7
|
+
class JobTimeout < RuntimeError; end
|
8
|
+
class JobNotFound < RuntimeError; end
|
9
|
+
class JobFormatInvalid < RuntimeError; end
|
10
|
+
class RetryJob < RuntimeError; end
|
11
|
+
|
12
|
+
attr_accessor :task, :body, :name, :args
|
13
|
+
|
14
|
+
# Construct a job to be parsed and processed
|
15
|
+
#
|
16
|
+
# task is a reserved object containing the json body in the form of
|
17
|
+
# { :class => "NewsletterSender", :args => ["foo@bar.com"] }
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# Backburner::Job.new(payload)
|
21
|
+
#
|
22
|
+
def initialize(task)
|
23
|
+
@hooks = Backburner::Hooks
|
24
|
+
@task = task
|
25
|
+
@body = task.body.is_a?(Hash) ? task.body : Backburner.configuration.job_parser_proc.call(task.body)
|
26
|
+
@name = body["class"] || body[:class]
|
27
|
+
@args = body["args"] || body[:args]
|
28
|
+
rescue => ex # Job was not valid format
|
29
|
+
self.bury
|
30
|
+
raise JobFormatInvalid, "Job body could not be parsed: #{ex.inspect}"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets the delegator object to the underlying beaneater job
|
34
|
+
# self.bury
|
35
|
+
def __getobj__
|
36
|
+
__setobj__(@task)
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
# Processes a job and handles any failure, deleting the job once complete
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# @task.process
|
44
|
+
#
|
45
|
+
def process
|
46
|
+
# Invoke before hook and stop if false
|
47
|
+
res = @hooks.invoke_hook_events(job_name, :before_perform, *args)
|
48
|
+
return false unless res
|
49
|
+
# Execute the job
|
50
|
+
@hooks.around_hook_events(job_name, :around_perform, *args) do
|
51
|
+
# We subtract one to ensure we timeout before beanstalkd does, except if:
|
52
|
+
# a) ttr == 0, to support never timing out
|
53
|
+
# b) ttr == 1, so that we don't accidentally set it to never time out
|
54
|
+
# NB: A ttr of 1 will likely result in race conditions between
|
55
|
+
# Backburner and beanstalkd and should probably be avoided
|
56
|
+
timeout_job_after(task.ttr > 1 ? task.ttr - 1 : task.ttr) { job_class.perform(*args) }
|
57
|
+
end
|
58
|
+
task.delete
|
59
|
+
# Invoke after perform hook
|
60
|
+
@hooks.invoke_hook_events(job_name, :after_perform, *args)
|
61
|
+
rescue => e
|
62
|
+
@hooks.invoke_hook_events(job_name, :on_failure, e, *args)
|
63
|
+
raise e
|
64
|
+
end
|
65
|
+
|
66
|
+
def bury
|
67
|
+
@hooks.invoke_hook_events(job_name, :on_bury, *args)
|
68
|
+
task.bury
|
69
|
+
end
|
70
|
+
|
71
|
+
def retry(count, delay)
|
72
|
+
@hooks.invoke_hook_events(job_name, :on_retry, count, delay, *args)
|
73
|
+
task.release(delay: delay)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the class for the job handler
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
# job_class # => NewsletterSender
|
80
|
+
#
|
81
|
+
def job_class
|
82
|
+
handler = try_job_class
|
83
|
+
raise(JobNotFound, self.name) unless handler
|
84
|
+
handler
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
# Attempts to return a constantized job name, otherwise reverts to the name string
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# job_name # => "SomeUnknownJob"
|
93
|
+
def job_name
|
94
|
+
handler = try_job_class
|
95
|
+
handler ? handler : self.name
|
96
|
+
end
|
97
|
+
|
98
|
+
def try_job_class
|
99
|
+
constantize(self.name)
|
100
|
+
rescue NameError
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
# Timeout job within specified block after given time.
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# timeout_job_after(3) { do_something! }
|
108
|
+
#
|
109
|
+
def timeout_job_after(secs, &block)
|
110
|
+
begin
|
111
|
+
Timeout::timeout(secs) { yield }
|
112
|
+
rescue Timeout::Error => e
|
113
|
+
raise JobTimeout, "#{name}(#{(@args||[]).join(', ')}) hit #{secs}s timeout.\nbacktrace: #{e.backtrace}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end # Job
|
118
|
+
end # Backburner
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Backburner
|
4
|
+
module Logger
|
5
|
+
# Loads in instance and class levels
|
6
|
+
def self.included(base)
|
7
|
+
base.extend self
|
8
|
+
end
|
9
|
+
|
10
|
+
# Print out when a job is about to begin
|
11
|
+
def log_job_begin(name, args)
|
12
|
+
log_info "Work job #{name} with #{args.inspect}"
|
13
|
+
Thread.current[:job_started_at] = Time.now
|
14
|
+
end
|
15
|
+
|
16
|
+
# Print out when a job completed
|
17
|
+
# If message is nil, job is considered complete
|
18
|
+
def log_job_end(name, message = nil)
|
19
|
+
ellapsed = Time.now - job_started_at
|
20
|
+
ms = (ellapsed.to_f * 1000).to_i
|
21
|
+
action_word = message ? 'Finished' : 'Completed'
|
22
|
+
log_info("#{action_word} #{name} in #{ms}ms #{message}")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns true if the job logging started
|
26
|
+
def job_started_at
|
27
|
+
Thread.current[:job_started_at]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Print a message to stdout
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# log_info("Working on task")
|
34
|
+
#
|
35
|
+
def log_info(msg)
|
36
|
+
logger ? logger.info(msg) : puts(msg)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Print an error to stderr
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# log_error("Task failed!")
|
43
|
+
#
|
44
|
+
def log_error(msg)
|
45
|
+
logger ? logger.error(msg) : $stderr.puts(msg)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return logger if specified
|
49
|
+
def logger
|
50
|
+
Backburner.configuration.logger
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'backburner/async_proxy'
|
2
|
+
|
3
|
+
module Backburner
|
4
|
+
module Performable
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
base.send(:include, Backburner::Queue)
|
8
|
+
base.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
# Return proxy object to enqueue jobs for object
|
13
|
+
# Options: `pri` (priority), `delay` (delay in secs), `ttr` (time to respond), `queue` (queue name)
|
14
|
+
# @example
|
15
|
+
# @model.async(:pri => 1000).do_something("foo")
|
16
|
+
#
|
17
|
+
def async(opts={})
|
18
|
+
Backburner::AsyncProxy.new(self.class, self.id, opts)
|
19
|
+
end
|
20
|
+
end # InstanceMethods
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
# Return proxy object to enqueue jobs for object
|
24
|
+
# Options: `pri` (priority), `delay` (delay in secs), `ttr` (time to respond), `queue` (queue name)
|
25
|
+
# @example
|
26
|
+
# Model.async(:ttr => 300).do_something("foo")
|
27
|
+
def async(opts={})
|
28
|
+
Backburner::AsyncProxy.new(self, nil, opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Defines perform method for job processing
|
32
|
+
# @example
|
33
|
+
# perform(55, :do_something, "foo", "bar")
|
34
|
+
def perform(id, method, *args)
|
35
|
+
if id # instance
|
36
|
+
find(id).send(method, *args)
|
37
|
+
else # class method
|
38
|
+
send(method, *args)
|
39
|
+
end
|
40
|
+
end # perform
|
41
|
+
|
42
|
+
# Always handle an instance method asynchronously
|
43
|
+
# @example
|
44
|
+
# User.handle_asynchronously :send_welcome_email, queue: 'send-mail', delay: 10
|
45
|
+
def handle_asynchronously(method, opts={})
|
46
|
+
Backburner::Performable.handle_asynchronously(self, method, opts)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Always handle a class method asynchronously
|
50
|
+
# @example
|
51
|
+
# User.handle_static_asynchronously :update_recent_visitors, ttr: 300
|
52
|
+
def handle_static_asynchronously(method, opts={})
|
53
|
+
Backburner::Performable.handle_static_asynchronously(self, method, opts)
|
54
|
+
end
|
55
|
+
end # ClassMethods
|
56
|
+
|
57
|
+
|
58
|
+
# Make all calls to an instance method asynchronous. The given opts will be passed
|
59
|
+
# to the async method.
|
60
|
+
# @example
|
61
|
+
# Backburner::Performable.handle_asynchronously(MyObject, :long_task, queue: 'long-tasks')
|
62
|
+
# NB: The method called on the async proxy will be ""#{method}_without_async". This
|
63
|
+
# will also be what's given to the Worker.enqueue method so your workers need
|
64
|
+
# to know about that. It shouldn't be a problem unless the producer and consumer are
|
65
|
+
# from different codebases (or anywhere they don't both call the handle_asynchronously
|
66
|
+
# method when booting up)
|
67
|
+
def self.handle_asynchronously(klass, method, opts={})
|
68
|
+
_handle_asynchronously(klass, klass, method, opts)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Make all calls to a class method asynchronous. The given opts will be passed
|
72
|
+
# to the async method. Please see the NB on #handle_asynchronously
|
73
|
+
def self.handle_static_asynchronously(klass, method, opts={})
|
74
|
+
_handle_asynchronously(klass, klass.singleton_class, method, opts)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self._handle_asynchronously(klass, klass_eval_scope, method, opts={})
|
78
|
+
aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
|
79
|
+
with_async_name = :"#{aliased_method}_with_async#{punctuation}"
|
80
|
+
without_async_name = :"#{aliased_method}_without_async#{punctuation}"
|
81
|
+
|
82
|
+
klass.send(:include, Performable) unless included_modules.include?(Performable)
|
83
|
+
klass_eval_scope.class_eval do
|
84
|
+
define_method with_async_name do |*args|
|
85
|
+
async(opts).__send__ without_async_name, *args
|
86
|
+
end
|
87
|
+
alias_method without_async_name, method.to_sym
|
88
|
+
alias_method method.to_sym, with_async_name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
private_class_method :_handle_asynchronously
|
92
|
+
|
93
|
+
|
94
|
+
end # Performable
|
95
|
+
end # Backburner
|