coney_island 0.13.1 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -0
- data/lib/coney_island.rb +38 -5
- data/lib/coney_island/configuration_error.rb +4 -0
- data/lib/coney_island/job.rb +2 -2
- data/lib/coney_island/jobs_cache.rb +96 -0
- data/lib/coney_island/notifiers/airbrake_notifier.rb +5 -3
- data/lib/coney_island/notifiers/base_notifier.rb +15 -0
- data/lib/coney_island/notifiers/bugsnag_notifier.rb +11 -0
- data/lib/coney_island/notifiers/honeybadger_notifier.rb +5 -3
- data/lib/coney_island/notifiers/null_notifier.rb +6 -0
- data/lib/coney_island/performer.rb +11 -4
- data/lib/coney_island/submitter.rb +23 -43
- data/lib/coney_island/version.rb +1 -1
- data/test/coney_island_test.rb +28 -1
- data/test/dummy/log/test.log +1703 -0
- data/test/jobs_cache_test.rb +84 -0
- data/test/performer_test.rb +23 -0
- data/test/submitter_test.rb +1 -0
- data/test/test_helper.rb +1 -0
- metadata +45 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad987b88af8e85b48fd63cbeddad3e131203b65b
|
4
|
+
data.tar.gz: 821581cc67935f4b287b75a4c5932634475232a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01203b116490b52af605167f6f854ac61a72fd37f750c73d364fd847fb4760526de6f814f67a970bd852c47dffab4af2ea499f9c44f927c7ee6039be0a6b3020
|
7
|
+
data.tar.gz: 02ba62d6b4d086a229abb37b6c18558e28bd06eb82c3efbce049344ebd844ad1054bd2e3a1050a75ea8a4740a10fde460a6f054ae396179aa1ee4f47c249909f
|
data/Rakefile
CHANGED
data/lib/coney_island.rb
CHANGED
@@ -35,7 +35,18 @@ module ConeyIsland
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def self.notifier
|
38
|
-
|
38
|
+
case self.config[:notifier]
|
39
|
+
when :airbrake
|
40
|
+
Notifiers::AirbrakeNotifier
|
41
|
+
when :bugsnag
|
42
|
+
Notifiers::BugsnagNotifier
|
43
|
+
when :honeybadger
|
44
|
+
Notifiers::HoneybadgerNotifier
|
45
|
+
when :none
|
46
|
+
Notifiers::NullNotifier
|
47
|
+
else
|
48
|
+
fail ConfigurationError, "#{self.config[:notifier]} is an invalid notifier. Valid options: :airbrake, :bugsnag, :honeybadger, :none"
|
49
|
+
end
|
39
50
|
end
|
40
51
|
|
41
52
|
def self.config=(config_hash)
|
@@ -87,10 +98,18 @@ module ConeyIsland
|
|
87
98
|
ConeyIsland::Submitter.cache_jobs
|
88
99
|
end
|
89
100
|
|
101
|
+
def self.cached_jobs
|
102
|
+
ConeyIsland::Submitter.cached_jobs
|
103
|
+
end
|
104
|
+
|
90
105
|
def self.stop_caching_jobs
|
91
106
|
ConeyIsland::Submitter.stop_caching_jobs
|
92
107
|
end
|
93
108
|
|
109
|
+
def self.caching_jobs(&blk)
|
110
|
+
ConeyIsland::Submitter.caching_jobs(&blk)
|
111
|
+
end
|
112
|
+
|
94
113
|
def self.flush_jobs
|
95
114
|
ConeyIsland::Submitter.flush_jobs
|
96
115
|
end
|
@@ -99,7 +118,7 @@ module ConeyIsland
|
|
99
118
|
ConeyIsland::Submitter.submit(*args)
|
100
119
|
end
|
101
120
|
|
102
|
-
def self.poke_the_badger(message, context, attempts = 1)
|
121
|
+
def self.poke_the_badger(message, context = {}, attempts = 1)
|
103
122
|
Timeout::timeout(3) do
|
104
123
|
self.notifier.notify(message, context)
|
105
124
|
end
|
@@ -111,18 +130,32 @@ module ConeyIsland
|
|
111
130
|
end
|
112
131
|
|
113
132
|
def self.default_settings
|
114
|
-
{ work_queue: 'default', timeout: 30, delay: 0 }
|
133
|
+
{ work_queue: 'default', timeout: 30, delay: 0, highlander: false }
|
115
134
|
end
|
116
135
|
|
117
136
|
end
|
118
137
|
|
138
|
+
# RabbitMQ
|
139
|
+
require 'bunny'
|
140
|
+
# Active Support
|
141
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
142
|
+
require 'active_support/core_ext/module/delegation'
|
143
|
+
require 'active_support/core_ext/string/inflections'
|
144
|
+
|
145
|
+
require 'coney_island/configuration_error'
|
146
|
+
require 'coney_island/job_argument_error'
|
147
|
+
|
148
|
+
require 'coney_island/notifiers/base_notifier'
|
149
|
+
require 'coney_island/notifiers/airbrake_notifier'
|
150
|
+
require 'coney_island/notifiers/bugsnag_notifier'
|
119
151
|
require 'coney_island/notifiers/honeybadger_notifier'
|
152
|
+
require 'coney_island/notifiers/null_notifier'
|
153
|
+
|
120
154
|
require 'coney_island/worker'
|
121
155
|
require 'coney_island/job'
|
122
156
|
require 'coney_island/submitter'
|
123
|
-
require 'coney_island/
|
157
|
+
require 'coney_island/jobs_cache'
|
124
158
|
if defined?(Rails) && defined?(ActiveJob)
|
125
159
|
require 'coney_island/coney_island_adapter'
|
126
160
|
end
|
127
161
|
require 'coney_island/performer'
|
128
|
-
require 'bunny'
|
data/lib/coney_island/job.rb
CHANGED
@@ -6,7 +6,7 @@ module ConeyIsland
|
|
6
6
|
|
7
7
|
def initialize(metadata, args)
|
8
8
|
@args = args
|
9
|
-
@id = SecureRandom.uuid
|
9
|
+
@id = args['job_id'] || SecureRandom.uuid
|
10
10
|
@dont_log = args['dont_log']
|
11
11
|
self.log.info ("Starting job #{@id}: #{@args}") unless self.dont_log
|
12
12
|
@delay = args['delay'].to_i if args['delay']
|
@@ -17,7 +17,7 @@ module ConeyIsland
|
|
17
17
|
@class_name = args['klass']
|
18
18
|
@klass = @class_name.constantize
|
19
19
|
@method_args = args['args']
|
20
|
-
# Symbolize hash keys for consistency and
|
20
|
+
# Symbolize hash keys for consistency and keyword arguments
|
21
21
|
@method_args.each { |v| v.symbolize_keys! if v.is_a?(Hash) } if !!@method_args
|
22
22
|
@attempts = args['attempt_count'] || 1
|
23
23
|
@retry_limit = args['retry_limit'] || 3
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ConeyIsland
|
2
|
+
# Handles job caching hijinks. This is especially useful for Rails apps where
|
3
|
+
# you can set Coney to cache jobs at the beginning of the request and flush
|
4
|
+
# them once you the request is served. Most methods are exposed by ConeyIsland
|
5
|
+
# so you'd just use ConeyIsland.cache_jobs, ConeyIsland.flush_jobs.
|
6
|
+
class JobsCache
|
7
|
+
delegate :submit!, to: Submitter
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@adapter = RequestStore
|
11
|
+
end
|
12
|
+
|
13
|
+
# Are we caching jobs?
|
14
|
+
def caching_jobs?
|
15
|
+
!! is_caching_jobs
|
16
|
+
end
|
17
|
+
|
18
|
+
# Start caching jobs
|
19
|
+
def cache_jobs
|
20
|
+
self.is_caching_jobs = true
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Stop caching jobs
|
25
|
+
def stop_caching_jobs
|
26
|
+
self.is_caching_jobs = false
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
# Caches jobs for the duration of the block, flushes them at the end.
|
31
|
+
def caching_jobs(&blk)
|
32
|
+
_was_caching = caching_jobs?
|
33
|
+
cache_jobs
|
34
|
+
blk.call
|
35
|
+
flush_jobs
|
36
|
+
self.is_caching_jobs = _was_caching
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Cache a job with the given args
|
41
|
+
def cache_job(*args)
|
42
|
+
self.cached_jobs[generate_id(*args)] = args
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Publish all the cached jobs
|
47
|
+
def flush_jobs
|
48
|
+
# Get all the jobs, one at a time, pulling from the list
|
49
|
+
while job = self.cached_jobs.shift
|
50
|
+
# Map the array to the right things
|
51
|
+
job_id, args = *job
|
52
|
+
# Submit! takes care of rescuing, error logging, etc and never caches
|
53
|
+
submit! args, job_id
|
54
|
+
end
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
# List of the currently cached jobs, anxiously waiting to be flushed
|
59
|
+
def cached_jobs
|
60
|
+
@adapter.store[:jobs] ||= {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def clear
|
64
|
+
self.is_caching_jobs = false
|
65
|
+
self.cached_jobs = {}
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
def cached_jobs=(something)
|
71
|
+
@adapter.store[:jobs] = something
|
72
|
+
end
|
73
|
+
|
74
|
+
def is_caching_jobs
|
75
|
+
@adapter.store[:caching_jobs]
|
76
|
+
end
|
77
|
+
|
78
|
+
def is_caching_jobs=(boolean)
|
79
|
+
@adapter.store[:caching_jobs] = boolean
|
80
|
+
end
|
81
|
+
|
82
|
+
def generate_id(*args)
|
83
|
+
# Duplicate the args so we don't mess with the original
|
84
|
+
_args = args.dup
|
85
|
+
# Do we have job arguments and highlander is true?
|
86
|
+
if _args.last.is_a?(Hash) && !!ActiveSupport::HashWithIndifferentAccess.new(_args.pop)[:highlander]
|
87
|
+
# We simply generate an id based on the class, method, arguments signature
|
88
|
+
_args.map(&:to_s).join("-")
|
89
|
+
else
|
90
|
+
# We generate a new id every time
|
91
|
+
SecureRandom.uuid
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module ConeyIsland
|
2
2
|
module Notifiers
|
3
|
-
class AirbrakeNotifier
|
4
|
-
def self.notify(message, extra_params)
|
3
|
+
class AirbrakeNotifier < BaseNotifier
|
4
|
+
def self.notify(message, extra_params = {})
|
5
5
|
Airbrake.notify(message, extra_params)
|
6
|
+
rescue NameError => e
|
7
|
+
fail ConfigurationError, fail_message(:airbrake, "Airbrake")
|
6
8
|
end
|
7
9
|
end
|
8
10
|
end
|
9
|
-
end
|
11
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ConeyIsland
|
2
|
+
module Notifiers
|
3
|
+
class BaseNotifier
|
4
|
+
def self.notify(message = "", extra_params = {})
|
5
|
+
# NOOP
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
def self.fail_message(notifier_symbol, notifier_class)
|
11
|
+
"You have specified #{notifier_symbol} as your notifier, but #{notifier_class} doesn't seem to be installed. Try adding #{notifier_symbol} to your Gemfile."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module ConeyIsland
|
2
|
+
module Notifiers
|
3
|
+
class BugsnagNotifier < BaseNotifier
|
4
|
+
def self.notify(message, extra_params = {})
|
5
|
+
Bugsnag.notify(message, meta_data: extra_params)
|
6
|
+
rescue NameError => e
|
7
|
+
fail ConfigurationError, fail_message(:bugsnag, "Bugsnag")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module ConeyIsland
|
2
2
|
module Notifiers
|
3
|
-
class HoneybadgerNotifier
|
4
|
-
def self.notify(message, extra_params)
|
3
|
+
class HoneybadgerNotifier < BaseNotifier
|
4
|
+
def self.notify(message, extra_params = {})
|
5
5
|
Honeybadger.notify(message, { context: extra_params })
|
6
|
+
rescue NameError => e
|
7
|
+
fail ConfigurationError, fail_message(:honeybadger, "Honeybadger")
|
6
8
|
end
|
7
9
|
end
|
8
10
|
end
|
9
|
-
end
|
11
|
+
end
|
@@ -3,18 +3,20 @@ module ConeyIsland
|
|
3
3
|
|
4
4
|
def self.included(base)
|
5
5
|
base.extend ClassMethods
|
6
|
+
delegate :get_coney_settings, to: :class
|
6
7
|
# http://apidock.com/rails/Class/class_attribute
|
7
8
|
base.class_attribute :coney_island_settings
|
8
9
|
end
|
9
10
|
|
11
|
+
|
10
12
|
def method_missing(method_name, *args)
|
11
13
|
method_str = method_name.to_s
|
12
14
|
if method_str =~ /.*_async$/
|
13
15
|
synchronous_method = method_str.sub(/_async$/, '')
|
14
16
|
if self.respond_to?(:id) && self.class.respond_to?(:find)
|
15
|
-
ConeyIsland.submit(self.class, synchronous_method, instance_id: self.id, args: args)
|
17
|
+
ConeyIsland.submit(self.class, synchronous_method, instance_id: self.id, args: args, highlander: get_coney_settings[:highlander])
|
16
18
|
else
|
17
|
-
ConeyIsland.submit(self.class, synchronous_method, singleton: true, args: args)
|
19
|
+
ConeyIsland.submit(self.class, synchronous_method, singleton: true, args: args, highlander: get_coney_settings[:highlander])
|
18
20
|
end
|
19
21
|
else
|
20
22
|
super
|
@@ -31,8 +33,13 @@ module ConeyIsland
|
|
31
33
|
# :timeout - Timeout the job with retry. The timeout value is a number
|
32
34
|
# of seconds. By default ConeyIsland will retry 3 times before bailing
|
33
35
|
# out.
|
36
|
+
# :highlander - There can only be one job with the same arguments per
|
37
|
+
# request lifecycle. This makes it so that even if you enqueue the same
|
38
|
+
# job with the same arguments twice, it will only fire once.
|
39
|
+
# Only makes sense when caching jobs (like in a Rails app where you can
|
40
|
+
# cache jobs and flush them all at once after the end of the request)
|
34
41
|
def set_background_defaults(options = {})
|
35
|
-
options = options.dup.symbolize_keys.slice(:work_queue, :delay, :timeout)
|
42
|
+
options = options.dup.symbolize_keys.slice(:work_queue, :delay, :timeout, :highlander)
|
36
43
|
self.coney_island_settings = get_coney_settings.merge(options)
|
37
44
|
end
|
38
45
|
|
@@ -46,7 +53,7 @@ module ConeyIsland
|
|
46
53
|
method_str = method_name.to_s
|
47
54
|
if method_str =~ /.*_async$/
|
48
55
|
synchronous_method = method_str.sub(/_async$/, '')
|
49
|
-
ConeyIsland.submit(self, synchronous_method, args: args)
|
56
|
+
ConeyIsland.submit(self, synchronous_method, args: args, highlander: get_coney_settings[:highlander])
|
50
57
|
else
|
51
58
|
super
|
52
59
|
end
|
@@ -3,6 +3,11 @@ module ConeyIsland
|
|
3
3
|
# ease of testing and thread safety.
|
4
4
|
class Submitter
|
5
5
|
|
6
|
+
class << self
|
7
|
+
delegate :caching_jobs?, :caching_jobs, :cached_jobs, :cache_job, :cache_jobs,
|
8
|
+
:stop_caching_jobs, :flush_jobs, to: :jobs_cache
|
9
|
+
end
|
10
|
+
|
6
11
|
def self.run_inline
|
7
12
|
@run_inline = true
|
8
13
|
end
|
@@ -23,41 +28,29 @@ module ConeyIsland
|
|
23
28
|
@tcp_connection_retries
|
24
29
|
end
|
25
30
|
|
26
|
-
def self.
|
27
|
-
|
28
|
-
job_id = SecureRandom.uuid
|
29
|
-
RequestStore.store[:jobs][job_id] = args
|
30
|
-
else
|
31
|
-
self.submit!(args)
|
32
|
-
end
|
31
|
+
def self.jobs_cache
|
32
|
+
@jobs_cache ||= JobsCache.new
|
33
33
|
end
|
34
34
|
|
35
|
-
def self.submit
|
36
|
-
if
|
37
|
-
|
35
|
+
def self.submit(*args)
|
36
|
+
if caching_jobs?
|
37
|
+
cache_job(*args)
|
38
38
|
else
|
39
|
-
|
40
|
-
self.submit_all!(args)
|
41
|
-
rescue StandardError => e
|
42
|
-
Rails.logger.error(e)
|
43
|
-
ConeyIsland.poke_the_badger(e,{
|
44
|
-
code_source: "ConeyIsland::Submitter.submit!",
|
45
|
-
message: "Error submitting job",
|
46
|
-
job_args: args
|
47
|
-
})
|
48
|
-
end
|
39
|
+
submit!(args)
|
49
40
|
end
|
50
41
|
end
|
51
42
|
|
52
|
-
def self.
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
43
|
+
def self.submit!(args, job_id = nil)
|
44
|
+
Rails.logger.info "Submitting job #{job_id}: #{args}"
|
45
|
+
publish_job(args, job_id)
|
46
|
+
rescue StandardError => e
|
47
|
+
Rails.logger.error(e)
|
48
|
+
ConeyIsland.poke_the_badger(e,{
|
49
|
+
code_source: "ConeyIsland::Submitter.submit!",
|
50
|
+
message: "Error submitting job",
|
51
|
+
job_args: args
|
52
|
+
})
|
53
|
+
fail e if running_inline?
|
61
54
|
end
|
62
55
|
|
63
56
|
def self.connection=(conn)
|
@@ -154,6 +147,7 @@ module ConeyIsland
|
|
154
147
|
@connection
|
155
148
|
end
|
156
149
|
|
150
|
+
# TODO: Document me
|
157
151
|
def self.publish_job(args, job_id = nil)
|
158
152
|
# Map arguments
|
159
153
|
klass, method_name, job_args = *args
|
@@ -203,19 +197,6 @@ module ConeyIsland
|
|
203
197
|
true
|
204
198
|
end
|
205
199
|
|
206
|
-
def self.cache_jobs
|
207
|
-
RequestStore.store[:cache_jobs] = true
|
208
|
-
RequestStore.store[:jobs] = {}
|
209
|
-
end
|
210
|
-
|
211
|
-
def self.flush_jobs
|
212
|
-
self.submit!(:all_cached_jobs) if RequestStore.store[:jobs].any?
|
213
|
-
end
|
214
|
-
|
215
|
-
def self.stop_caching_jobs
|
216
|
-
RequestStore.store[:cache_jobs] = false
|
217
|
-
end
|
218
|
-
|
219
200
|
protected
|
220
201
|
|
221
202
|
# Publishes a job to a delayed queue exchange
|
@@ -242,4 +223,3 @@ module ConeyIsland
|
|
242
223
|
|
243
224
|
end
|
244
225
|
end
|
245
|
-
|
data/lib/coney_island/version.rb
CHANGED