coney_island 0.8 → 0.9

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