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 +4 -4
- data/bin/skyrunner +9 -1
- data/lib/skyrunner/job.rb +69 -34
- data/lib/skyrunner/version.rb +1 -1
- data/lib/skyrunner.rb +24 -13
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37d6d89a9e1f734ed6a46610d822b6ef19f80323
|
4
|
+
data.tar.gz: ea79aca278152621d7baa6cad890820daa6a5be6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
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 =
|
35
|
-
queue = SkyRunner.sqs_queue
|
36
|
-
|
39
|
+
table = nil
|
37
40
|
record = nil
|
38
41
|
|
39
|
-
SkyRunner
|
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 }
|
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
|
-
|
65
|
-
|
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(
|
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
|
-
|
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
|
-
|
133
|
+
unless is_solo?
|
134
|
+
record = dynamo_db_record
|
111
135
|
|
112
|
-
|
113
|
-
|
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,
|
145
|
+
self.send(method, job_args.symbolize_keys)
|
121
146
|
end
|
122
147
|
end
|
123
148
|
|
124
|
-
|
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
|
-
|
133
|
-
|
159
|
+
unless is_solo?
|
160
|
+
record = dynamo_db_record
|
161
|
+
new_attributes = nil
|
134
162
|
|
135
|
-
|
136
|
-
|
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
|
-
|
170
|
+
unless is_solo?
|
171
|
+
if_condition = { completed_tasks: new_attributes["total_tasks"], done: 0 }
|
142
172
|
|
143
|
-
|
144
|
-
|
145
|
-
|
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,
|
184
|
+
self.send(method, job_args.symbolize_keys)
|
154
185
|
end
|
155
186
|
end
|
156
187
|
|
157
|
-
|
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
|
data/lib/skyrunner/version.rb
CHANGED
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
|
-
|
90
|
+
unless is_solo
|
91
|
+
# Avoid running the same task twice, enter record and raise error if exists already.
|
91
92
|
|
92
|
-
|
93
|
-
|
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
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
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
|
|