coney_island 0.13.1 → 0.14.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 +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