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