coney_island 0.8 → 0.9

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: c35298c5543296c4c41a42bd756f6ef193a29186
4
- data.tar.gz: ef14d5797940fd78f324126b7d86e42c7130ef04
3
+ metadata.gz: 7dba043d7a55d3830d4a1d26147fa56148433713
4
+ data.tar.gz: be41a962dd7a4507839f4f7285e823c2a4ba0508
5
5
  SHA512:
6
- metadata.gz: 790d75b9af3c13a51f0493350956c8026d87976efb5f45b2cee87c7c06891a19ac574264ab9aa8e638b162c56f93e693fbd5141e70bb01f7d69b9a5bacd9c3a7
7
- data.tar.gz: 7b45b0327ea9fe7f01bf76a509921f80dc3e8b355ad35a49206d8d0df68c08fc8289b7581cefbc766b42e7be364d82654288e5ef6eb551802deaf17a0bcb2fbd
6
+ metadata.gz: 9e816ff7f5e2620a672c6fb8be05755b5b270c77be586595befcea3258b8624d3a65011461781d9c62b8972acf608ef06ae6799336dc13bb27b472e44aeb1512
7
+ data.tar.gz: 3d8e612aad3bd9e432e15f3328cbdad05de6497a46ae62e4a6ba517fab9b89e211e0f6415e354cca7d28e6db341d2ce592abfcfba80b361d3990c5e2c69a3cc2
data/Rakefile CHANGED
@@ -25,7 +25,7 @@ Rake::TestTask.new(:test) do |t|
25
25
  t.libs << 'lib'
26
26
  t.libs << 'test'
27
27
  t.pattern = 'test/**/*_test.rb'
28
- t.verbose = false
28
+ t.verbose = true
29
29
  end
30
30
 
31
31
 
data/lib/coney_island.rb CHANGED
@@ -57,6 +57,14 @@ module ConeyIsland
57
57
  ConeyIsland::Worker.config
58
58
  end
59
59
 
60
+ def self.delay_seed
61
+ @delay_seed ||= 2
62
+ end
63
+
64
+ def self.delay_seed=(seed)
65
+ @delay_seed = seed
66
+ end
67
+
60
68
  def self.single_amqp_connection?
61
69
  !!self.amqp_parameters
62
70
  end
@@ -73,6 +81,10 @@ module ConeyIsland
73
81
  ConeyIsland::Submitter.run_inline
74
82
  end
75
83
 
84
+ def self.running_inline?
85
+ ConeyIsland::Submitter.running_inline?
86
+ end
87
+
76
88
  def self.stop_running_inline
77
89
  ConeyIsland::Submitter.stop_running_inline
78
90
  end
@@ -112,6 +124,8 @@ end
112
124
 
113
125
  require 'coney_island/notifiers/honeybadger_notifier'
114
126
  require 'coney_island/worker'
127
+ require 'coney_island/job'
115
128
  require 'coney_island/submitter'
116
129
  require 'coney_island/job_argument_error'
117
130
  require 'coney_island/railtie' if defined?(Rails)
131
+ require 'coney_island/performer'
@@ -0,0 +1,91 @@
1
+ module ConeyIsland
2
+ class Job
3
+ attr_accessor :delay, :timeout, :method_name, :class_name, :klass, :method_args, :id, :args,
4
+ :instance_id, :object, :metadata, :attempts, :retry_limit, :retry_on_exception
5
+
6
+ def initialize(metadata, args)
7
+ @args = args
8
+ @id = SecureRandom.uuid
9
+ self.log.info ("Starting job #{@id}: #{@args}")
10
+ @delay = args['delay'].to_i if args['delay']
11
+ @timeout = args['timeout']
12
+ @method_name = args['method_name']
13
+ @instance_id = args['instance_id']
14
+ @class_name = args['klass']
15
+ @klass = @class_name.constantize
16
+ @method_args = args['args']
17
+ @attempts = args['attempt_count'] || 1
18
+ @retry_limit = args['retry_limit'] || 3
19
+ @retry_on_exception = args['retry_on_exception']
20
+ @metadata = metadata
21
+ if @klass.respond_to? :coney_island_settings
22
+ @delay ||= @klass.coney_island_settings[:delay]
23
+ @timeout ||= @klass.coney_island_settings[:timeout]
24
+ end
25
+ @timeout ||= BG_TIMEOUT_SECONDS
26
+ if @instance_id.present?
27
+ @object = @klass.find(@instance_id)
28
+ else
29
+ @object = @klass
30
+ end
31
+ end
32
+
33
+ def ticket
34
+ ConeyIsland::Worker.ticket
35
+ end
36
+
37
+ def log
38
+ ConeyIsland::Worker.log
39
+ end
40
+
41
+ def execute_job_method
42
+ if method_args.present? and method_args.length > 0
43
+ object.send method_name, *method_args
44
+ else
45
+ object.send method_name
46
+ end
47
+ end
48
+
49
+ def handle_job
50
+ Timeout::timeout(timeout) do
51
+ execute_job_method
52
+ end
53
+ rescue Timeout::Error => e
54
+ if self.attempts >= self.retry_limit
55
+ log.error("Request #{self.id} timed out after #{self.timeout} seconds, bailing out after 3 attempts")
56
+ ConeyIsland.poke_the_badger(e, {work_queue: self.ticket, job_payload: self.args, reason: 'Bailed out after 3 attempts'})
57
+ else
58
+ log.error("Request #{self.id} timed out after #{self.timeout} seconds on attempt number #{self.attempts}, retrying...")
59
+ self.attempts += 1
60
+ ConeyIsland.submit(self.klass, self.method_name, self.resubmit_args)
61
+ end
62
+ rescue Exception => e
63
+ if retry_on_exception && (self.attempts < self.retry_limit)
64
+ self.attempts += 1
65
+ ConeyIsland.poke_the_badger(e, {work_queue: self.ticket, job_payload: self.args, attempt_count: self.attempts})
66
+ ConeyIsland.submit(self.klass, self.method_name, self.resubmit_args)
67
+ log.error("Resubmitting after error on attempt ##{self.attempts}:")
68
+ else
69
+ ConeyIsland.poke_the_badger(e, {work_queue: self.ticket, job_payload: self.args})
70
+ log.error("Bailing out after error on final attempt ##{self.attempts}:")
71
+ end
72
+ log.error("Error executing #{self.class_name}##{self.method_name} #{self.id} for id #{self.instance_id} with args #{self.args}:")
73
+ log.error(e.message)
74
+ log.error(e.backtrace.join("\n"))
75
+ ensure
76
+ finalize_job
77
+ end
78
+
79
+ def resubmit_args
80
+ args.select{|key,val| ['timeout','retry_on_exception','retry_limit','args','instance_id'].include? key}.merge(
81
+ 'attempt_count' => self.attempts, 'work_queue' => self.ticket, 'delay' => ConeyIsland.delay_seed**(self.attempts - 1))
82
+ end
83
+
84
+ def finalize_job
85
+ metadata.ack unless ConeyIsland.running_inline?
86
+ log.info("finished job #{id}")
87
+ ConeyIsland::Worker.running_jobs.delete self
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,41 @@
1
+ module ConeyIsland
2
+ module Performer
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def set_background_defaults(work_queue: nil, delay: nil, timeout: nil)
10
+ self.coney_island_settings[:work_queue] = work_queue
11
+ self.coney_island_settings[:delay] = delay
12
+ self.coney_island_settings[:timeout] = timeout
13
+ end
14
+
15
+ def coney_island_settings
16
+ @coney_island_settings ||= {}
17
+ end
18
+
19
+ def create_instance_async_methods(*synchronous_methods)
20
+ synchronous_methods.each do |synchronous_method|
21
+ define_method("#{synchronous_method}_async") do |*args|
22
+ unless self.respond_to? :id
23
+ raise StandardError.new(
24
+ "#{synchronous_method} is not an instance method, ConeyIsland can't async it via :create_instance_async_methods")
25
+ end
26
+ ConeyIsland.submit(self.class, synchronous_method, instance_id: self.id, args: args)
27
+ end
28
+ end
29
+ end
30
+
31
+ def create_class_async_methods(*synchronous_methods)
32
+ synchronous_methods.each do |synchronous_method|
33
+ define_singleton_method("#{synchronous_method}_async") do |*args|
34
+ ConeyIsland.submit(self, synchronous_method, args: args)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -9,6 +9,10 @@ module ConeyIsland
9
9
  @run_inline = false
10
10
  end
11
11
 
12
+ def self.running_inline?
13
+ @run_inline
14
+ end
15
+
12
16
  def self.submit(*args)
13
17
  if RequestStore.store[:cache_jobs]
14
18
  job_id = SecureRandom.uuid
@@ -93,19 +97,24 @@ module ConeyIsland
93
97
  def self.publish_job(args, job_id = nil)
94
98
  if (args.first.is_a? Class or args.first.is_a? Module) and (args[1].is_a? String or args[1].is_a? Symbol) and args.last.is_a? Hash and 3 == args.length
95
99
  klass = args.shift
96
- klass = klass.name
100
+ klass_name = klass.name
101
+
97
102
  method_name = args.shift
98
103
  job_args = args.shift
99
104
  job_args ||= {}
100
- job_args['klass'] = klass
105
+ job_args['klass'] = klass_name
101
106
  job_args['method_name'] = method_name
102
107
  if @run_inline
103
108
  job_args.stringify_keys!
104
- ConeyIsland::Worker.execute_job_method(job_args)
109
+ job = ConeyIsland::Job.new(nil, job_args)
110
+ job.handle_job
105
111
  else
106
112
  work_queue = job_args.delete :work_queue
113
+ if klass.respond_to? :coney_island_settings
114
+ work_queue ||= klass.coney_island_settings[:work_queue]
115
+ end
107
116
  work_queue ||= 'default'
108
- self.exchange.publish((job_args.to_json), routing_key: "carousels.#{work_queue}") do
117
+ self.exchange.publish(job_args.to_json, {routing_key: "carousels.#{work_queue}"}) do
109
118
  RequestStore.store[:jobs].delete job_id if RequestStore.store[:jobs] && job_id.present?
110
119
  end
111
120
  end
@@ -1,3 +1,3 @@
1
1
  module ConeyIsland
2
- VERSION = "0.8"
2
+ VERSION = "0.9"
3
3
  end
@@ -17,12 +17,24 @@ module ConeyIsland
17
17
  @log = log_thing
18
18
  end
19
19
 
20
- def self.job_attempts
21
- @job_attempts ||= {}
20
+ def self.running_jobs
21
+ @running_jobs ||= []
22
22
  end
23
23
 
24
- def self.clear_job_attempts
25
- @job_attempts = {}
24
+ def self.clear_running_jobs
25
+ @running_jobs = []
26
+ end
27
+
28
+ def self.ticket
29
+ @ticket
30
+ end
31
+
32
+ def self.ticket=(some_ticket)
33
+ @ticket = some_ticket
34
+ end
35
+
36
+ def self.reset_child_pids
37
+ @child_pids = []
26
38
  end
27
39
 
28
40
  def self.initialize_background
@@ -42,7 +54,7 @@ module ConeyIsland
42
54
  @worker_count = @instance_config[:worker_count] if @instance_config
43
55
  @worker_count ||= 1
44
56
  @child_count = @worker_count - 1
45
- @child_pids = []
57
+ reset_child_pids
46
58
 
47
59
  @full_instance_name = @ticket
48
60
 
@@ -146,16 +158,15 @@ module ConeyIsland
146
158
 
147
159
  def self.handle_incoming_message(metadata,payload)
148
160
  begin
149
- job_id = SecureRandom.uuid
150
- self.job_attempts[job_id] = 1
151
161
  args = JSON.parse(payload)
152
- self.log.info ("Starting job #{job_id}: #{args}")
153
- if args.has_key? 'delay'
154
- EventMachine.add_timer(args['delay'].to_i) do
155
- self.handle_job(metadata,args,job_id)
162
+ job = Job.new(metadata, args)
163
+ self.running_jobs << job
164
+ if job.delay.present?
165
+ EventMachine.add_timer(job.delay) do
166
+ job.handle_job
156
167
  end
157
168
  else
158
- self.handle_job(metadata,args,job_id)
169
+ job.handle_job
159
170
  end
160
171
  rescue Timeout::Error => e
161
172
  ConeyIsland.poke_the_badger(e, {code_source: 'ConeyIsland', job_payload: args, reason: 'timeout in subscribe code before calling job method'})
@@ -165,60 +176,6 @@ module ConeyIsland
165
176
  end
166
177
  end
167
178
 
168
- def self.handle_job(metadata,args,job_id)
169
- timeout = args['timeout']
170
- timeout ||= BG_TIMEOUT_SECONDS
171
- begin
172
- Timeout::timeout(timeout) do
173
- self.execute_job_method(args)
174
- end
175
- rescue Timeout::Error => e
176
- if self.job_attempts.has_key? job_id
177
- if self.job_attempts[job_id] >= 3
178
- self.log.error("Request #{job_id} timed out after #{timeout} seconds, bailing out after 3 attempts")
179
- self.finalize_job(metadata,job_id)
180
- ConeyIsland.poke_the_badger(e, {work_queue: @ticket, job_payload: args, reason: 'Bailed out after 3 attempts'})
181
- else
182
- self.log.error("Request #{job_id} timed out after #{timeout} seconds on attempt number #{self.job_attempts[job_id]}, retrying...")
183
- self.job_attempts[job_id] += 1
184
- self.handle_job(metadata,args,job_id)
185
- end
186
- end
187
- rescue Exception => e
188
- ConeyIsland.poke_the_badger(e, {work_queue: @ticket, job_payload: args})
189
- self.log.error("Error executing #{args['klass']}##{args['method_name']} #{job_id} for id #{args['instance_id']} with args #{args}:")
190
- self.log.error(e.message)
191
- self.log.error(e.backtrace.join("\n"))
192
- self.finalize_job(metadata,job_id)
193
- else
194
- self.finalize_job(metadata,job_id)
195
- end
196
- end
197
-
198
- def self.execute_job_method(args)
199
- class_name = args['klass']
200
- method_name = args['method_name']
201
- klass = class_name.constantize
202
- method_args = args['args']
203
- if args.has_key? 'instance_id'
204
- instance_id = args['instance_id']
205
- object = klass.find(instance_id)
206
- else
207
- object = klass
208
- end
209
- if method_args and method_args.length > 0
210
- object.send method_name, *method_args
211
- else
212
- object.send method_name
213
- end
214
- end
215
-
216
- def self.finalize_job(metadata,job_id)
217
- metadata.ack
218
- self.log.info("finished job #{job_id}")
219
- self.job_attempts.delete job_id
220
- end
221
-
222
179
  def self.handle_missing_children
223
180
  @child_pids.each do |child_pid|
224
181
  begin
@@ -231,7 +188,7 @@ module ConeyIsland
231
188
 
232
189
  def self.abandon_and_shutdown
233
190
  self.log.info("Lost RabbitMQ connection, abandoning current jobs and shutting down")
234
- self.clear_job_attempts
191
+ self.clear_running_jobs
235
192
  self.shutdown('TERM')
236
193
  end
237
194
 
@@ -242,8 +199,8 @@ module ConeyIsland
242
199
  end
243
200
  @queue.unsubscribe rescue nil
244
201
  EventMachine.add_periodic_timer(1) do
245
- if self.job_attempts.any?
246
- self.log.info("Waiting for #{self.job_attempts.length} requests to finish")
202
+ if self.running_jobs.any?
203
+ self.log.info("Waiting for #{self.running_jobs.length} requests to finish")
247
204
  else
248
205
  self.log.info("Shutting down coney island #{@ticket}")
249
206
  EventMachine.stop
@@ -252,4 +209,4 @@ module ConeyIsland
252
209
  end
253
210
 
254
211
  end
255
- end
212
+ end
@@ -2,19 +2,52 @@ ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
2
2
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
3
3
  ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
4
4
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
5
+ Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
6
+ ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
7
+ ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
8
+ Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
9
+ Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
10
+ ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
11
+ mocked method :publish expects 0 arguments, got 2
12
+ undefined method `publish' for nil:NilClass
13
+ undefined method `publish' for nil:NilClass
14
+ undefined method `publish' for nil:NilClass
15
+ undefined method `publish' for nil:NilClass
16
+ undefined method `publish' for nil:NilClass
17
+ undefined method `publish' for nil:NilClass
18
+ undefined method `publish' for nil:NilClass
19
+ undefined method `publish' for nil:NilClass
20
+ undefined method `publish' for nil:NilClass
21
+ undefined method `publish' for nil:NilClass
22
+ undefined method `publish' for nil:NilClass
23
+ undefined method `publish' for nil:NilClass
24
+ undefined method `publish' for nil:NilClass
25
+ mocked method :publish expects 0 arguments, got 2
26
+ mocked method :publish expects 0 arguments, got 2
27
+ mocked method :publish expects 0 arguments, got 2
28
+ Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
29
+ ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
30
+ Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
31
+ ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
32
+ Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
33
+ ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
5
34
  ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
6
35
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
7
36
  ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
8
37
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
9
38
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
10
39
  ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
40
+ ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
11
41
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
12
42
  ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
43
+ Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
13
44
  ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
14
45
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
15
46
  ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
16
47
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
17
48
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
18
49
  ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
50
+ Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
19
51
  ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
20
52
  Failed to connecto to RabbitMQ Attempt #1 time(s), trying again in 0 seconds...
53
+ ConeyIsland::Submitter.submit! about to iterate over this many jobs: 1
data/test/job_test.rb ADDED
@@ -0,0 +1,151 @@
1
+ require 'test_helper'
2
+
3
+ class JobTest < MiniTest::Test
4
+ describe "ConeyIsland::Job" do
5
+ describe "handling a job" do
6
+ before do
7
+ @metadata = MiniTest::Mock.new
8
+ @metadata.expect :ack, nil
9
+ end
10
+
11
+ it "retries on timeout with correct initial attempt_count and delay" do
12
+ job = ConeyIsland::Job.new(@metadata,
13
+ { 'klass' => 'TestModel',
14
+ 'method_name' => :take_too_long,
15
+ 'timeout' => 0.0001 }
16
+ )
17
+ capture_submissions = lambda { |klass,method_name,options|
18
+ ::JobTest.messages[:job_options] ||= []
19
+ ::JobTest.messages[:job_options] << options
20
+ }
21
+ ConeyIsland.stub(:submit, capture_submissions) do
22
+ job.handle_job
23
+ end
24
+ ::JobTest.messages[:job_options].last['attempt_count'].must_equal 2
25
+ ::JobTest.messages[:job_options].last['delay'].must_equal 2
26
+ end
27
+
28
+ it "retries on timeout with correct subsequent attempt_count and delay" do
29
+ job = ConeyIsland::Job.new(@metadata,
30
+ { 'klass' => 'TestModel',
31
+ 'method_name' => :take_too_long,
32
+ 'timeout' => 0.0001,
33
+ 'attempt_count' => 2,
34
+ 'delay' => 2 }
35
+ )
36
+ capture_submissions = lambda { |klass,method_name,options|
37
+ ::JobTest.messages[:job_options] ||= []
38
+ ::JobTest.messages[:job_options] << options
39
+ }
40
+ ConeyIsland.stub(:submit, capture_submissions) do
41
+ job.handle_job
42
+ end
43
+ ::JobTest.messages[:job_options].last['attempt_count'].must_equal 3
44
+ ::JobTest.messages[:job_options].last['delay'].must_equal 4
45
+ end
46
+
47
+ it "bails out on timeout if retry limit reached" do
48
+ job = ConeyIsland::Job.new(@metadata,
49
+ { 'klass' => 'TestModel',
50
+ 'method_name' => :take_too_long,
51
+ 'timeout' => 0.0001,
52
+ 'attempt_count' => 3,
53
+ 'delay' => 2 }
54
+ )
55
+ capture_submissions = lambda { |klass,method_name,options|
56
+ fail "Should not have called ConeyIsland.submit, should have bailed out instead"
57
+ }
58
+ ConeyIsland.stub(:submit, capture_submissions) do
59
+ job.handle_job
60
+ end
61
+ @metadata.verify #we ack the message bus when we bail out
62
+ end
63
+
64
+ it "retries on exception with correct initial attempt_count and delay if retry_on_exception set" do
65
+ job = ConeyIsland::Job.new(@metadata,
66
+ { 'klass' => 'TestModel',
67
+ 'method_name' => :throw_an_error,
68
+ 'retry_on_exception' => true }
69
+ )
70
+ capture_submissions = lambda { |klass,method_name,options|
71
+ ::JobTest.messages[:job_options] ||= []
72
+ ::JobTest.messages[:job_options] << options
73
+ }
74
+ ConeyIsland.stub(:submit, capture_submissions) do
75
+ job.handle_job
76
+ end
77
+ ::JobTest.messages[:job_options].last['attempt_count'].must_equal 2
78
+ ::JobTest.messages[:job_options].last['delay'].must_equal 2
79
+ end
80
+
81
+ it "bails out on exception if retry_on_exception set and retry_limit reached" do
82
+ job = ConeyIsland::Job.new(@metadata,
83
+ { 'klass' => 'TestModel',
84
+ 'method_name' => :throw_an_error,
85
+ 'retry_on_exception' => true,
86
+ 'retry_limit' => 2,
87
+ 'attempt_count' => 2}
88
+ )
89
+ capture_submissions = lambda { |klass,method_name,options|
90
+ fail "Should not have called ConeyIsland.submit, should have bailed out instead"
91
+ }
92
+ ConeyIsland.stub(:submit, capture_submissions) do
93
+ job.handle_job
94
+ end
95
+ @metadata.verify #we ack the message bus when we bail out
96
+ end
97
+
98
+ it "bails out on exception if retry_on_exception not set" do
99
+ job = ConeyIsland::Job.new(@metadata,
100
+ { 'klass' => 'TestModel',
101
+ 'method_name' => :throw_an_error }
102
+ )
103
+ capture_submissions = lambda { |klass,method_name,options|
104
+ fail "Should not have called ConeyIsland.submit, should have bailed out instead"
105
+ }
106
+ ConeyIsland.stub(:submit, capture_submissions) do
107
+ job.handle_job
108
+ end
109
+ @metadata.verify #we ack the message bus when we bail out
110
+ end
111
+
112
+ it "sends exeptions to a notification service when bailing out" do
113
+ @poke_the_badger = MiniTest::Mock.new
114
+ @poke_the_badger.expect :call, nil, [Exception,Hash]
115
+ job = ConeyIsland::Job.new(@metadata,
116
+ { 'klass' => 'TestModel',
117
+ 'method_name' => :throw_an_error }
118
+ )
119
+ ConeyIsland.stub(:poke_the_badger,@poke_the_badger) do
120
+ job.handle_job
121
+ end
122
+ @poke_the_badger.verify
123
+ end
124
+
125
+ it "calls find on the submitted class if an instance_id is present" do
126
+ TestModel.new('id' => 'my_id')
127
+ job = ConeyIsland::Job.new(@metadata,{
128
+ 'klass' => 'TestModel',
129
+ 'method_name' => :set_color,
130
+ 'instance_id' => 'my_id',
131
+ 'args' => ['green']
132
+ })
133
+ job.handle_job
134
+ my_thing = TestModel.find('my_id')
135
+ my_thing.color.must_equal 'green'
136
+ end
137
+
138
+ it "acknowledges job completion to the message bus" do
139
+ ConeyIsland.stop_running_inline
140
+ job = ConeyIsland::Job.new(@metadata,
141
+ { 'klass' => 'TestModel',
142
+ 'method_name' => :add_to_list,
143
+ 'args' => [[]]})
144
+ job.handle_job
145
+ @metadata.verify
146
+ ConeyIsland::Worker.running_jobs.must_be :empty?
147
+ end
148
+
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,113 @@
1
+ require 'test_helper'
2
+
3
+ class PerformerTest < MiniTest::Test
4
+ describe "settings" do
5
+ it 'sets default class-level settings' do
6
+ job = ConeyIsland::Job.new(nil,
7
+ { 'klass' => 'MyPerformer',
8
+ 'method_name' => 'set_color',
9
+ 'args' => ['carmine'] }
10
+ )
11
+ job.delay.must_equal 1
12
+ job.timeout.must_equal 5
13
+ end
14
+ it 'overrides class-level settings with job-level settings' do
15
+ job = ConeyIsland::Job.new(nil,
16
+ { 'klass' => 'MyPerformer',
17
+ 'method_name' => 'set_color',
18
+ 'args' => ['carmine'],
19
+ 'delay' => 3,
20
+ 'timeout' => 10 }
21
+ )
22
+ job.delay.must_equal 3
23
+ job.timeout.must_equal 10
24
+ end
25
+ it 'sets work queue for the class' do
26
+ capture_publish = proc do |payload,options|
27
+ end
28
+ @exchange = Minitest::Mock.new
29
+ def @exchange.publish(payload,options,&blk)
30
+ ::PerformerTest.messages[:publish_hash] = options
31
+ end
32
+ ConeyIsland::Submitter.stub(:handle_connection, nil) do
33
+ ConeyIsland::Submitter.stub(:exchange, @exchange) do
34
+ ConeyIsland::Submitter.stop_running_inline
35
+ ConeyIsland::Submitter.submit(MyPerformer, :add_to_list, args: [[]])
36
+ end
37
+ end
38
+ @exchange.verify
39
+ ::PerformerTest.messages[:publish_hash][:routing_key].must_equal "carousels.cyclone"
40
+ end
41
+ it 'overrides the class work queue with the job-level work queue' do
42
+ capture_publish = proc do |payload,options|
43
+ end
44
+ @exchange = Minitest::Mock.new
45
+ def @exchange.publish(payload,options,&blk)
46
+ ::PerformerTest.messages[:publish_hash] = options
47
+ end
48
+ ConeyIsland::Submitter.stub(:handle_connection, nil) do
49
+ ConeyIsland::Submitter.stub(:exchange, @exchange) do
50
+ ConeyIsland::Submitter.stop_running_inline
51
+ ConeyIsland::Submitter.submit(MyPerformer, :add_to_list, work_queue: 'boardwalk', args: [[]])
52
+ end
53
+ end
54
+ @exchange.verify
55
+ ::PerformerTest.messages[:publish_hash][:routing_key].must_equal "carousels.boardwalk"
56
+ end
57
+ end
58
+
59
+ describe 'async methods' do
60
+ it 'creates async instance methods' do
61
+ my_performer = MyPerformer.new(7)
62
+ ConeyIsland.run_inline
63
+ my_performer.set_color_async 'sage'
64
+ my_performer.color.must_equal 'sage'
65
+ end
66
+ it 'creates async class methods' do
67
+ ConeyIsland.run_inline
68
+ MyPerformer.set_tone_async 'contemplative'
69
+ MyPerformer.tone.must_equal 'contemplative'
70
+ end
71
+ end
72
+ end
73
+
74
+ class MyPerformer
75
+ include ConeyIsland::Performer
76
+ set_background_defaults work_queue: 'cyclone', delay: 1, timeout: 5
77
+ create_class_async_methods :set_tone
78
+ create_instance_async_methods :set_color
79
+
80
+ def self.instances
81
+ @instances ||= {}
82
+ end
83
+
84
+ def self.find(id)
85
+ instances[id]
86
+ end
87
+
88
+ def self.set_tone(tone)
89
+ @tone = tone
90
+ end
91
+
92
+ def self.tone
93
+ @tone
94
+ end
95
+
96
+ def id
97
+ @id
98
+ end
99
+
100
+ def initialize(id)
101
+ @id = id
102
+ self.class.instances[id] = self
103
+ end
104
+
105
+ def set_color(color)
106
+ @color = color
107
+ end
108
+
109
+ def color
110
+ @color
111
+ end
112
+ end
113
+
@@ -4,13 +4,13 @@ class SubmitterTest < MiniTest::Test
4
4
  describe "ConeyIsland::Submitter" do
5
5
  describe "running jobs inline" do
6
6
  it "calls the worker directly" do
7
- @execute_job_method = Minitest::Mock.new
8
- @execute_job_method.expect :call, nil, [Hash]
9
- ConeyIsland::Worker.stub(:execute_job_method,@execute_job_method) do
7
+ @job = Minitest::Mock.new
8
+ @job.expect :handle_job, nil
9
+ ConeyIsland::Job.stub(:new,@job) do
10
10
  ConeyIsland::Submitter.run_inline
11
11
  ConeyIsland::Submitter.submit(TestModel, :add_to_list, args: [[]])
12
12
  end
13
- @execute_job_method.verify
13
+ @job.verify
14
14
  end
15
15
  end
16
16
  describe "running jobs in the background" do
data/test/test_helper.rb CHANGED
@@ -18,6 +18,21 @@ if ActiveSupport::TestCase.method_defined?(:fixture_path=)
18
18
  ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
19
19
  end
20
20
 
21
+ class MiniTest::Test
22
+ def self.messages
23
+ @@messages[Thread.current.object_id] ||= {}
24
+ end
25
+
26
+ def self.clear_messages
27
+ @@messages ||= {}
28
+ @@messages[Thread.current.object_id] = {}
29
+ end
30
+
31
+ def setup
32
+ self.class.clear_messages
33
+ end
34
+ end
35
+
21
36
  class TestModel
22
37
 
23
38
  def self.add_to_list(array)
data/test/worker_test.rb CHANGED
@@ -7,46 +7,26 @@ class WorkerTest < MiniTest::Test
7
7
  @metadata = MiniTest::Mock.new
8
8
  @metadata.expect :ack, nil
9
9
  end
10
- it "handles timeouts with 3 retries before bailing out" do
11
- ConeyIsland::Worker.job_attempts['my_job_id'] = 1
12
- ConeyIsland::Worker.job_attempts.stub(:delete,nil) do
13
- ConeyIsland::Worker.handle_job(@metadata,{
14
- 'klass' => 'TestModel',
15
- 'method_name' => :take_too_long,
16
- 'timeout' => 0.0001
17
- },'my_job_id')
10
+ it 'handles incoming messages' do
11
+ @capture_running_jobs = []
12
+ def @capture_running_jobs.delete(item)
18
13
  end
19
- ConeyIsland::Worker.job_attempts['my_job_id'].must_equal 3
20
- end
21
- it "sends other exeptions to a notification service" do
22
- @poke_the_badger = MiniTest::Mock.new
23
- @poke_the_badger.expect :call, nil, [Exception,Hash]
24
- ConeyIsland.stub(:poke_the_badger,@poke_the_badger) do
25
- ConeyIsland::Worker.handle_job(@metadata,{
26
- 'klass' => 'TestModel',
27
- 'method_name' => :throw_an_error
28
- },'my_job_id')
14
+ ConeyIsland::Worker.stub :running_jobs, @capture_running_jobs do
15
+ ConeyIsland::Worker.handle_incoming_message(@metadata,
16
+ "{\"klass\":\"TestModel\",\"method_name\":\"add_to_list\",\"args\":[[]]}")
29
17
  end
30
- @poke_the_badger.verify
31
- end
32
- it "calls find on the submitted class if an instance_id is present" do
33
- TestModel.new('id' => 'my_id')
34
- ConeyIsland::Worker.handle_job(@metadata,{
35
- 'klass' => 'TestModel',
36
- 'method_name' => :set_color,
37
- 'instance_id' => 'my_id',
38
- 'args' => ['green']
39
- },'my_job_id')
40
- my_thing = TestModel.find('my_id')
41
- my_thing.color.must_equal 'green'
18
+ @capture_running_jobs.first.args['method_name'].must_equal 'add_to_list'
42
19
  end
43
- it "acknowledges job completion to the message bus" do
44
- ConeyIsland::Worker.handle_job(@metadata,
45
- { 'klass' => 'TestModel',
46
- 'method_name' => :add_to_list,
47
- 'args' => [[]]},
48
- 'my_job_id')
49
- @metadata.verify
20
+ it 'passes work_queue to the job' do
21
+ ConeyIsland::Worker.ticket = 'test_queue'
22
+ @capture_running_jobs = []
23
+ def @capture_running_jobs.delete(item)
24
+ end
25
+ ConeyIsland::Worker.stub :running_jobs, @capture_running_jobs do
26
+ ConeyIsland::Worker.handle_incoming_message(@metadata,
27
+ "{\"klass\":\"TestModel\",\"method_name\":\"add_to_list\",\"args\":[[]]}")
28
+ end
29
+ @capture_running_jobs.first.ticket.must_equal 'test_queue'
50
30
  end
51
31
  end
52
32
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coney_island
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.8'
4
+ version: '0.9'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Draut
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-01-30 00:00:00.000000000 Z
12
+ date: 2015-03-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -104,14 +104,15 @@ extensions: []
104
104
  extra_rdoc_files: []
105
105
  files:
106
106
  - MIT-LICENSE
107
- - README.rdoc
108
107
  - Rakefile
109
108
  - bin/coney_island
110
109
  - lib/coney_island.rb
111
110
  - lib/coney_island/coney_island_adapter.rb
111
+ - lib/coney_island/job.rb
112
112
  - lib/coney_island/job_argument_error.rb
113
113
  - lib/coney_island/notifiers/airbrake_notifier.rb
114
114
  - lib/coney_island/notifiers/honeybadger_notifier.rb
115
+ - lib/coney_island/performer.rb
115
116
  - lib/coney_island/railtie.rb
116
117
  - lib/coney_island/submitter.rb
117
118
  - lib/coney_island/version.rb
@@ -153,6 +154,8 @@ files:
153
154
  - test/dummy/public/422.html
154
155
  - test/dummy/public/500.html
155
156
  - test/dummy/public/favicon.ico
157
+ - test/job_test.rb
158
+ - test/performer_test.rb
156
159
  - test/submitter_test.rb
157
160
  - test/test_helper.rb
158
161
  - test/worker_test.rb
@@ -220,6 +223,8 @@ test_files:
220
223
  - test/dummy/public/favicon.ico
221
224
  - test/dummy/Rakefile
222
225
  - test/dummy/README.rdoc
226
+ - test/job_test.rb
227
+ - test/performer_test.rb
223
228
  - test/submitter_test.rb
224
229
  - test/test_helper.rb
225
230
  - test/worker_test.rb
data/README.rdoc DELETED
@@ -1,3 +0,0 @@
1
- = ConeyIsland
2
-
3
- This project rocks and uses MIT-LICENSE.