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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bfdbf69bc36cb56a1ad4e633be6df4f4edced0d0
4
- data.tar.gz: 641fc25380b54f476f5f37f8c59c6d3570d4607b
3
+ metadata.gz: ad987b88af8e85b48fd63cbeddad3e131203b65b
4
+ data.tar.gz: 821581cc67935f4b287b75a4c5932634475232a6
5
5
  SHA512:
6
- metadata.gz: fa96a5a9c6dcec63474fee276cb7be7b825f35788f533616b1b34e489b597a25f63e9f6153c46e2b074d2da1d72842725160bdb513079c591830d9bdd00c396a
7
- data.tar.gz: c124f9e8b09631d6c6c114de3beca755fc40a210513f8b31ef82f3d67875baaf0e671ff6e56e24d337afbf826d4f8bf2118d8dc83fae101e0f9635a7d7db4dce
6
+ metadata.gz: 01203b116490b52af605167f6f854ac61a72fd37f750c73d364fd847fb4760526de6f814f67a970bd852c47dffab4af2ea499f9c44f927c7ee6039be0a6b3020
7
+ data.tar.gz: 02ba62d6b4d086a229abb37b6c18558e28bd06eb82c3efbce049344ebd844ad1054bd2e3a1050a75ea8a4740a10fde460a6f054ae396179aa1ee4f47c249909f
data/Rakefile CHANGED
@@ -26,6 +26,7 @@ Rake::TestTask.new(:test) do |t|
26
26
  t.libs << 'test'
27
27
  t.pattern = 'test/**/*_test.rb'
28
28
  t.verbose = true
29
+ t.warning = false
29
30
  end
30
31
 
31
32
 
@@ -35,7 +35,18 @@ module ConeyIsland
35
35
  end
36
36
 
37
37
  def self.notifier
38
- @notifier ||= "ConeyIsland::Notifiers::#{self.config[:notifier_service]}Notifier".constantize
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/job_argument_error'
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'
@@ -0,0 +1,4 @@
1
+ module ConeyIsland
2
+ class ConfigurationError < StandardError
3
+ end
4
+ end
@@ -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 for keyword arguments
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
@@ -0,0 +1,6 @@
1
+ module ConeyIsland
2
+ module Notifiers
3
+ class NullNotifier < BaseNotifier
4
+ end
5
+ end
6
+ 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.submit(*args)
27
- if RequestStore.store[:cache_jobs]
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!(args)
36
- if @run_inline
37
- self.submit_all!(args)
35
+ def self.submit(*args)
36
+ if caching_jobs?
37
+ cache_job(*args)
38
38
  else
39
- begin
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.submit_all!(args)
53
- if :all_cached_jobs == args
54
- Rails.logger.info("ConeyIsland::Submitter.submit! about to iterate over this many jobs: #{RequestStore.store[:jobs].length}")
55
- RequestStore.store[:jobs].each do |job_id,job_args|
56
- self.publish_job(job_args,job_id)
57
- end
58
- else
59
- self.publish_job(args)
60
- end
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
-
@@ -1,3 +1,3 @@
1
1
  module ConeyIsland
2
- VERSION = "0.13.1"
2
+ VERSION = "0.14.0"
3
3
  end