skyrunner 0.1.6 → 0.1.7

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: 6980e3350176aaeafea768c35991b7dd4c7c8662
4
- data.tar.gz: c9a0e818cd049ff11a4ae6f641de8815468c11c7
3
+ metadata.gz: 37d6d89a9e1f734ed6a46610d822b6ef19f80323
4
+ data.tar.gz: ea79aca278152621d7baa6cad890820daa6a5be6
5
5
  SHA512:
6
- metadata.gz: 832397d587fa772a108a981e7aaf21e4001a29ff3caf0839362bbff446b3c2f43dcd23387c3584603c48d0fb809abe190289d345459e23a3bc105813916809e2
7
- data.tar.gz: c8e4c17772bcbddd1da726278ad9ec5fbd2a63637cc8dfb683e610ef555d992fb0b931abf02bec5208063b4395ec3378d353a7b276040bcb5f946e3f7fb63414
6
+ metadata.gz: b851b35f64f494b2b340d7a476c3c9c6bd874c408e1c2c89bbf8fb8ebfb8a92146380619a63482ef4b4560f2d14a0bb7678342a701be9e2baa6c3980f00ec9a4
7
+ data.tar.gz: 2610b2bf6082dc62513e648d41a5133ec98db858940ff675273622a8a30abd4a353e05baf40e88f4b57d780fa4e5bd52da2bcef5f1ad3b6850e173f345c37f71
data/bin/skyrunner CHANGED
@@ -56,6 +56,14 @@ when "consume"
56
56
  when "test"
57
57
  $: << "."
58
58
  require "#{File.dirname(__FILE__)}/../jobs/example_job"
59
- ExampleJobModule::ExampleJob.new.execute!(number_of_tasks: 1000)
59
+
60
+ # Solo job, does not use DynamoDB for coordination.
61
+ 1.upto(10) do |n|
62
+ ExampleJobModule::ExampleJob.new.execute!(number_of_tasks: 1)
63
+ end
64
+
65
+ # Batch job, uses DynamoDB to coordinate
66
+ ExampleJobModule::ExampleJob.new.execute!(number_of_tasks: 100)
67
+
60
68
  SkyRunner.consume!
61
69
  end
data/lib/skyrunner/job.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  module SkyRunner::Job
2
2
  attr_accessor :skyrunner_job_id
3
+ attr_accessor :skyrunner_job_is_solo
3
4
 
4
5
  def self.included(base)
5
6
  base.extend(ClassMethods)
@@ -27,26 +28,44 @@ module SkyRunner::Job
27
28
  end
28
29
  end
29
30
 
30
- def execute!(args = {})
31
+ def is_solo?
32
+ self.skyrunner_job_is_solo
33
+ end
34
+
35
+ def execute!(job_args = {})
31
36
  job_id = SecureRandom.hex
32
37
  self.skyrunner_job_id = job_id
33
38
 
34
- table = SkyRunner.dynamo_db_table
35
- queue = SkyRunner.sqs_queue
36
-
39
+ table = nil
37
40
  record = nil
38
41
 
39
- SkyRunner::retry_dynamo_db do
40
- record = table.items.put(id: job_id, task_id: job_id, class: self.class.name, args: args.to_json, total_tasks: 1, completed_tasks: 0, done: 0, failed: 0)
41
- end
42
-
42
+ queue = SkyRunner.sqs_queue
43
43
  pending_args = []
44
+ fired_solo = false
44
45
 
45
46
  flush = lambda do
46
47
  messages = pending_args.map do |task_args|
47
- { job_id: job_id, task_id: SecureRandom.hex, task_args: task_args, job_class: self.class.name }.to_json
48
+ { job_id: job_id, task_id: SecureRandom.hex, job_args: job_args, task_args: task_args, job_class: self.class.name }
48
49
  end
49
50
 
51
+ if record.nil?
52
+ # Run an un-coordinated solo job if only one message
53
+ if messages.size > 1
54
+ SkyRunner::retry_dynamo_db do
55
+ table = SkyRunner.dynamo_db_table
56
+ record = table.items.put(id: job_id, task_id: job_id, class: self.class.name, args: job_args.to_json, total_tasks: 1, completed_tasks: 0, done: 0, failed: 0)
57
+ end
58
+ else
59
+ fired_solo = true
60
+
61
+ messages.each do |m|
62
+ m[:is_solo] = true
63
+ end
64
+ end
65
+ end
66
+
67
+ messages = messages.map(&:to_json)
68
+
50
69
  dropped_message_count = 0
51
70
  pending_args.clear
52
71
 
@@ -61,12 +80,14 @@ module SkyRunner::Job
61
80
  end
62
81
  end
63
82
 
64
- SkyRunner::retry_dynamo_db do
65
- record.attributes.add({ total_tasks: messages.size - dropped_message_count })
83
+ if record
84
+ SkyRunner::retry_dynamo_db do
85
+ record.attributes.add({ total_tasks: messages.size - dropped_message_count })
86
+ end
66
87
  end
67
88
  end
68
89
 
69
- self.run(args) do |*task_args|
90
+ self.run(job_args) do |*task_args|
70
91
  pending_args << task_args
71
92
 
72
93
  if pending_args.size >= SkyRunner::SQS_MAX_BATCH_SIZE
@@ -84,15 +105,17 @@ module SkyRunner::Job
84
105
  break if pending_args.size == 0
85
106
  end
86
107
 
87
- handle_task_completed!
108
+ unless fired_solo
109
+ handle_task_completed!(job_args)
110
+ end
88
111
  end
89
112
 
90
- def consume!(task_args)
113
+ def consume!(job_args, task_args)
91
114
  begin
92
115
  self.send(task_args[0].to_sym, task_args[1].symbolize_keys)
93
- handle_task_completed!
116
+ handle_task_completed!(job_args)
94
117
  rescue Exception => e
95
- handle_task_failed! rescue nil
118
+ handle_task_failed!(job_args) rescue nil
96
119
  raise
97
120
  end
98
121
  end
@@ -103,46 +126,54 @@ module SkyRunner::Job
103
126
  SkyRunner.dynamo_db_table.items[self.skyrunner_job_id, self.skyrunner_job_id]
104
127
  end
105
128
 
106
- def handle_task_failed!
129
+ def handle_task_failed!(job_args)
107
130
  return false unless self.skyrunner_job_id
108
131
 
109
132
  begin
110
- record = dynamo_db_record
133
+ unless is_solo?
134
+ record = dynamo_db_record
111
135
 
112
- SkyRunner::retry_dynamo_db do
113
- record.attributes.add({ failed: 1 })
136
+ SkyRunner::retry_dynamo_db do
137
+ record.attributes.add({ failed: 1 })
138
+ end
114
139
  end
115
140
 
116
141
  (self.class.job_event_methods[:failed] || []).each do |method|
117
142
  if self.method(method).arity == 0 && self.method(method).parameters.size == 0
118
143
  self.send(method)
119
144
  else
120
- self.send(method, JSON.parse(record.attributes["args"]).symbolize_keys)
145
+ self.send(method, job_args.symbolize_keys)
121
146
  end
122
147
  end
123
148
 
124
- delete_task_records! rescue nil
149
+ unless is_solo?
150
+ delete_task_records! rescue nil
151
+ end
125
152
  rescue Exception => e
126
153
  end
127
154
  end
128
155
 
129
- def handle_task_completed!
156
+ def handle_task_completed!(job_args)
130
157
  return false unless self.skyrunner_job_id
131
158
 
132
- record = dynamo_db_record
133
- new_attributes = nil
159
+ unless is_solo?
160
+ record = dynamo_db_record
161
+ new_attributes = nil
134
162
 
135
- SkyRunner::retry_dynamo_db do
136
- new_attributes = record.attributes.add({ completed_tasks: 1 }, return: :all_new)
163
+ SkyRunner::retry_dynamo_db do
164
+ new_attributes = record.attributes.add({ completed_tasks: 1 }, return: :all_new)
165
+ end
137
166
  end
138
167
 
139
- if new_attributes["total_tasks"] == new_attributes["completed_tasks"]
168
+ if is_solo? || new_attributes["total_tasks"] == new_attributes["completed_tasks"]
140
169
  begin
141
- if_condition = { completed_tasks: new_attributes["total_tasks"], done: 0 }
170
+ unless is_solo?
171
+ if_condition = { completed_tasks: new_attributes["total_tasks"], done: 0 }
142
172
 
143
- SkyRunner::retry_dynamo_db do
144
- record.attributes.update(if: if_condition) do |u|
145
- u.add(done: 1)
173
+ SkyRunner::retry_dynamo_db do
174
+ record.attributes.update(if: if_condition) do |u|
175
+ u.add(done: 1)
176
+ end
146
177
  end
147
178
  end
148
179
 
@@ -150,11 +181,13 @@ module SkyRunner::Job
150
181
  if self.method(method).arity == 0 && self.method(method).parameters.size == 0
151
182
  self.send(method)
152
183
  else
153
- self.send(method, JSON.parse(record.attributes["args"]).symbolize_keys)
184
+ self.send(method, job_args.symbolize_keys)
154
185
  end
155
186
  end
156
187
 
157
- delete_task_records! rescue nil
188
+ unless is_solo?
189
+ delete_task_records! rescue nil
190
+ end
158
191
  rescue AWS::DynamoDB::Errors::ConditionalCheckFailedException => e
159
192
  # This is OK, we had a double finisher so lets block them.
160
193
  end
@@ -164,6 +197,8 @@ module SkyRunner::Job
164
197
  end
165
198
 
166
199
  def delete_task_records!
200
+ return if is_solo?
201
+
167
202
  delete_batch_queue = Queue.new
168
203
  mutex = Mutex.new
169
204
  delete_items_queued = false
@@ -1,3 +1,3 @@
1
1
  module Skyrunner
2
- VERSION = "0.1.6"
2
+ VERSION = "0.1.7"
3
3
  end
data/lib/skyrunner.rb CHANGED
@@ -83,23 +83,26 @@ module SkyRunner
83
83
  next
84
84
  end
85
85
 
86
- klass, job_id, task_id, task_args, message = local_queue.pop
86
+ klass, job_id, task_id, job_args, task_args, is_solo, message = local_queue.pop
87
87
 
88
88
  if klass
89
89
  begin
90
- # Avoid running the same task twice, enter record and raise error if exists already.
90
+ unless is_solo
91
+ # Avoid running the same task twice, enter record and raise error if exists already.
91
92
 
92
- SkyRunner::retry_dynamo_db do
93
- table.items.put({ id: "#{job_id}-tasks", task_id: task_id }, unless_exists: ["id", "task_id"])
93
+ SkyRunner::retry_dynamo_db do
94
+ table.items.put({ id: "#{job_id}-tasks", task_id: task_id }, unless_exists: ["id", "task_id"])
95
+ end
94
96
  end
95
97
 
96
98
  SkyRunner::log :info, "Run Task: #{task_args} Job: #{job_id} Message: #{message.id}"
97
99
 
98
100
  job = klass.new
99
101
  job.skyrunner_job_id = job_id
102
+ job.skyrunner_job_is_solo = is_solo
100
103
 
101
104
  begin
102
- job.consume!(task_args)
105
+ job.consume!(job_args, task_args)
103
106
  message.delete
104
107
  rescue Exception => e
105
108
  message.delete rescue nil
@@ -138,14 +141,16 @@ module SkyRunner
138
141
 
139
142
  next unless received_messages.size > 0
140
143
 
141
- job_ids = received_messages.map { |m| [m[1]["job_id"], m[1]["job_id"]] }.uniq
144
+ job_ids = received_messages.select { |m| !m[1]["is_solo"] }.map { |m| [m[1]["job_id"], m[1]["job_id"]] }.uniq
142
145
 
143
146
  job_records = {}
144
147
 
145
- SkyRunner::retry_dynamo_db do
146
- # Read DynamoDB records into job and task lookup tables.
147
- table.batch_get(["id", "task_id", "failed"], job_ids.uniq, consistent_read: true) do |record|
148
- job_records[record["id"]] = record
148
+ if job_ids.uniq.size > 0
149
+ SkyRunner::retry_dynamo_db do
150
+ # Read DynamoDB records into job and task lookup tables.
151
+ table.batch_get(["id", "task_id", "failed"], job_ids.uniq, consistent_read: true) do |record|
152
+ job_records[record["id"]] = record
153
+ end
149
154
  end
150
155
  end
151
156
 
@@ -153,14 +158,16 @@ module SkyRunner
153
158
  message, message_data = received_message
154
159
  job_id = message_data["job_id"]
155
160
  task_id = message_data["task_id"]
161
+ is_solo = message_data["is_solo"]
162
+ job_args = message_data["job_args"]
163
+ task_args = message_data["task_args"]
156
164
 
157
165
  job_record = job_records[job_id]
158
166
 
159
- if job_record && job_record["failed"] == 0
167
+ if is_solo || (job_record && job_record["failed"] == 0)
160
168
  begin
161
169
  klass = Kernel.const_get(message_data["job_class"])
162
- task_args = message_data["task_args"]
163
- local_queue.push([klass, job_id, task_id, task_args, message])
170
+ local_queue.push([klass, job_id, task_id, job_args, task_args, is_solo, message])
164
171
  rescue NameError => e
165
172
  block.call(e) if block_given?
166
173
  message.delete rescue nil
@@ -187,8 +194,12 @@ module SkyRunner
187
194
  end
188
195
 
189
196
  def self.dynamo_db_table
197
+ table = Thread.current.thread_variable_get(:skyrunner_dyn_table)
198
+ return table if table
199
+
190
200
  dynamo_db.tables[SkyRunner.dynamo_db_table_name].tap do |table|
191
201
  table.load_schema if table && table.exists?
202
+ Thread.current.thread_variable_set(:skyrunner_dyn_table, table)
192
203
  end
193
204
  end
194
205
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skyrunner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Fodor