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 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