skyrunner 0.1.6 → 0.1.7

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