cloudtasker 0.12.rc8 → 0.12.0
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/CHANGELOG.md +11 -5
- data/README.md +56 -6
- data/docs/BATCH_JOBS.md +34 -2
- data/gemfiles/google_cloud_tasks_1.0.gemfile.lock +119 -106
- data/gemfiles/google_cloud_tasks_1.1.gemfile.lock +119 -106
- data/gemfiles/google_cloud_tasks_1.2.gemfile.lock +119 -106
- data/gemfiles/google_cloud_tasks_1.3.gemfile.lock +119 -106
- data/gemfiles/rails_5.2.gemfile.lock +63 -50
- data/gemfiles/rails_6.0.gemfile.lock +63 -50
- data/lib/cloudtasker/backend/redis_task.rb +1 -1
- data/lib/cloudtasker/batch/extension/worker.rb +1 -1
- data/lib/cloudtasker/batch/job.rb +14 -12
- data/lib/cloudtasker/config.rb +26 -2
- data/lib/cloudtasker/local_server.rb +1 -1
- data/lib/cloudtasker/unique_job/job.rb +5 -12
- data/lib/cloudtasker/version.rb +1 -1
- data/lib/cloudtasker/worker_handler.rb +5 -12
- metadata +7 -7
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
cloudtasker (0.12.
|
4
|
+
cloudtasker (0.12.0)
|
5
5
|
activesupport
|
6
6
|
connection_pool
|
7
7
|
fugit
|
@@ -68,35 +68,35 @@ GEM
|
|
68
68
|
minitest (~> 5.1)
|
69
69
|
tzinfo (~> 1.1)
|
70
70
|
zeitwerk (~> 2.1, >= 2.1.8)
|
71
|
-
addressable (2.
|
71
|
+
addressable (2.8.0)
|
72
72
|
public_suffix (>= 2.0.2, < 5.0)
|
73
|
-
appraisal (2.4.
|
73
|
+
appraisal (2.4.1)
|
74
74
|
bundler
|
75
75
|
rake
|
76
76
|
thor (>= 0.14.0)
|
77
77
|
ast (2.4.2)
|
78
|
-
async (1.
|
78
|
+
async (1.30.1)
|
79
79
|
console (~> 1.10)
|
80
80
|
nio4r (~> 2.3)
|
81
81
|
timers (~> 4.1)
|
82
|
-
async-http (0.
|
83
|
-
async (
|
84
|
-
async-io (
|
85
|
-
async-pool (
|
86
|
-
protocol-http (~> 0.
|
87
|
-
protocol-http1 (~> 0.
|
82
|
+
async-http (0.56.5)
|
83
|
+
async (>= 1.25)
|
84
|
+
async-io (>= 1.28)
|
85
|
+
async-pool (>= 0.2)
|
86
|
+
protocol-http (~> 0.22.0)
|
87
|
+
protocol-http1 (~> 0.14.0)
|
88
88
|
protocol-http2 (~> 0.14.0)
|
89
|
-
async-http-faraday (0.
|
89
|
+
async-http-faraday (0.11.0)
|
90
90
|
async-http (~> 0.42)
|
91
91
|
faraday
|
92
|
-
async-io (1.
|
93
|
-
async
|
94
|
-
async-pool (0.3.
|
95
|
-
async (
|
92
|
+
async-io (1.32.2)
|
93
|
+
async
|
94
|
+
async-pool (0.3.8)
|
95
|
+
async (>= 1.25)
|
96
96
|
builder (3.2.4)
|
97
|
-
concurrent-ruby (1.1.
|
98
|
-
connection_pool (2.2.
|
99
|
-
console (1.
|
97
|
+
concurrent-ruby (1.1.9)
|
98
|
+
connection_pool (2.2.5)
|
99
|
+
console (1.13.1)
|
100
100
|
fiber-local
|
101
101
|
crack (0.4.5)
|
102
102
|
rexml
|
@@ -105,18 +105,32 @@ GEM
|
|
105
105
|
erubi (1.10.0)
|
106
106
|
et-orbi (1.2.4)
|
107
107
|
tzinfo
|
108
|
-
faraday (1.
|
108
|
+
faraday (1.7.0)
|
109
|
+
faraday-em_http (~> 1.0)
|
110
|
+
faraday-em_synchrony (~> 1.0)
|
111
|
+
faraday-excon (~> 1.1)
|
112
|
+
faraday-httpclient (~> 1.0.1)
|
109
113
|
faraday-net_http (~> 1.0)
|
114
|
+
faraday-net_http_persistent (~> 1.1)
|
115
|
+
faraday-patron (~> 1.0)
|
116
|
+
faraday-rack (~> 1.0)
|
110
117
|
multipart-post (>= 1.2, < 3)
|
111
|
-
ruby2_keywords
|
118
|
+
ruby2_keywords (>= 0.0.4)
|
119
|
+
faraday-em_http (1.0.0)
|
120
|
+
faraday-em_synchrony (1.0.0)
|
121
|
+
faraday-excon (1.1.0)
|
112
122
|
faraday-http-cache (2.2.0)
|
113
123
|
faraday (>= 0.8)
|
124
|
+
faraday-httpclient (1.0.1)
|
114
125
|
faraday-net_http (1.0.1)
|
126
|
+
faraday-net_http_persistent (1.2.0)
|
127
|
+
faraday-patron (1.0.0)
|
128
|
+
faraday-rack (1.0.0)
|
115
129
|
fiber-local (1.0.0)
|
116
|
-
fugit (1.
|
130
|
+
fugit (1.5.1)
|
117
131
|
et-orbi (~> 1.1, >= 1.1.8)
|
118
132
|
raabro (~> 1.4)
|
119
|
-
github_changelog_generator (1.16.
|
133
|
+
github_changelog_generator (1.16.4)
|
120
134
|
activesupport
|
121
135
|
async (>= 1.25.0)
|
122
136
|
async-http-faraday
|
@@ -125,9 +139,8 @@ GEM
|
|
125
139
|
octokit (~> 4.6)
|
126
140
|
rainbow (>= 2.2.1)
|
127
141
|
rake (>= 10.0)
|
128
|
-
|
129
|
-
|
130
|
-
activesupport (>= 4.2.0)
|
142
|
+
globalid (0.5.2)
|
143
|
+
activesupport (>= 5.0)
|
131
144
|
google-cloud-tasks (1.5.1)
|
132
145
|
google-gax (~> 1.8)
|
133
146
|
googleapis-common-protos (>= 1.3.9, < 2.0)
|
@@ -139,22 +152,22 @@ GEM
|
|
139
152
|
googleauth (~> 0.9)
|
140
153
|
grpc (~> 1.24)
|
141
154
|
rly (~> 0.2.3)
|
142
|
-
google-protobuf (3.
|
155
|
+
google-protobuf (3.17.3)
|
143
156
|
googleapis-common-protos (1.3.11)
|
144
157
|
google-protobuf (~> 3.14)
|
145
158
|
googleapis-common-protos-types (>= 1.0.6, < 2.0)
|
146
159
|
grpc (~> 1.27)
|
147
|
-
googleapis-common-protos-types (1.0
|
160
|
+
googleapis-common-protos-types (1.1.0)
|
148
161
|
google-protobuf (~> 3.14)
|
149
|
-
googleauth (0.
|
162
|
+
googleauth (0.17.0)
|
150
163
|
faraday (>= 0.17.3, < 2.0)
|
151
164
|
jwt (>= 1.4, < 3.0)
|
152
165
|
memoist (~> 0.16)
|
153
166
|
multi_json (~> 1.11)
|
154
167
|
os (>= 0.9, < 2.0)
|
155
168
|
signet (~> 0.14)
|
156
|
-
grpc (1.
|
157
|
-
google-protobuf (~> 3.
|
169
|
+
grpc (1.38.0)
|
170
|
+
google-protobuf (~> 3.15)
|
158
171
|
googleapis-common-protos-types (~> 1.0)
|
159
172
|
grpc-google-iam-v1 (0.6.11)
|
160
173
|
google-protobuf (~> 3.14)
|
@@ -164,8 +177,8 @@ GEM
|
|
164
177
|
i18n (1.8.10)
|
165
178
|
concurrent-ruby (~> 1.0)
|
166
179
|
jaro_winkler (1.5.4)
|
167
|
-
jwt (2.2.
|
168
|
-
loofah (2.
|
180
|
+
jwt (2.2.3)
|
181
|
+
loofah (2.12.0)
|
169
182
|
crass (~> 1.0.2)
|
170
183
|
nokogiri (>= 1.5.9)
|
171
184
|
mail (2.7.1)
|
@@ -178,25 +191,25 @@ GEM
|
|
178
191
|
nokogiri (~> 1)
|
179
192
|
rake
|
180
193
|
mini_mime (1.1.0)
|
181
|
-
mini_portile2 (2.
|
194
|
+
mini_portile2 (2.6.1)
|
182
195
|
minitest (5.14.4)
|
183
196
|
multi_json (1.15.0)
|
184
197
|
multipart-post (2.1.1)
|
185
|
-
nio4r (2.5.
|
186
|
-
nokogiri (1.
|
187
|
-
mini_portile2 (~> 2.
|
198
|
+
nio4r (2.5.8)
|
199
|
+
nokogiri (1.12.3)
|
200
|
+
mini_portile2 (~> 2.6.1)
|
188
201
|
racc (~> 1.4)
|
189
|
-
octokit (4.
|
202
|
+
octokit (4.21.0)
|
190
203
|
faraday (>= 0.9)
|
191
204
|
sawyer (~> 0.8.0, >= 0.5.3)
|
192
205
|
os (1.1.1)
|
193
206
|
parallel (1.20.1)
|
194
|
-
parser (3.0.
|
207
|
+
parser (3.0.2.0)
|
195
208
|
ast (~> 2.4.1)
|
196
209
|
protocol-hpack (1.4.2)
|
197
|
-
protocol-http (0.
|
198
|
-
protocol-http1 (0.
|
199
|
-
protocol-http (~> 0.
|
210
|
+
protocol-http (0.22.5)
|
211
|
+
protocol-http1 (0.14.1)
|
212
|
+
protocol-http (~> 0.22)
|
200
213
|
protocol-http2 (0.14.2)
|
201
214
|
protocol-hpack (~> 1.4)
|
202
215
|
protocol-http (~> 0.18)
|
@@ -224,7 +237,7 @@ GEM
|
|
224
237
|
rails-dom-testing (2.0.3)
|
225
238
|
activesupport (>= 4.2.0)
|
226
239
|
nokogiri (>= 1.6)
|
227
|
-
rails-html-sanitizer (1.
|
240
|
+
rails-html-sanitizer (1.4.1)
|
228
241
|
loofah (~> 2.3)
|
229
242
|
railties (6.0.0)
|
230
243
|
actionpack (= 6.0.0)
|
@@ -233,8 +246,8 @@ GEM
|
|
233
246
|
rake (>= 0.8.7)
|
234
247
|
thor (>= 0.20.3, < 2.0)
|
235
248
|
rainbow (3.0.0)
|
236
|
-
rake (13.0.
|
237
|
-
redis (4.
|
249
|
+
rake (13.0.6)
|
250
|
+
redis (4.4.0)
|
238
251
|
retriable (3.1.2)
|
239
252
|
rexml (3.2.5)
|
240
253
|
rly (0.2.3)
|
@@ -251,7 +264,7 @@ GEM
|
|
251
264
|
rspec-mocks (3.10.2)
|
252
265
|
diff-lcs (>= 1.2.0, < 2.0)
|
253
266
|
rspec-support (~> 3.10.0)
|
254
|
-
rspec-rails (5.0.
|
267
|
+
rspec-rails (5.0.2)
|
255
268
|
actionpack (>= 5.2)
|
256
269
|
activesupport (>= 5.2)
|
257
270
|
railties (>= 5.2)
|
@@ -270,11 +283,11 @@ GEM
|
|
270
283
|
rubocop-rspec (1.37.0)
|
271
284
|
rubocop (>= 0.68.1)
|
272
285
|
ruby-progressbar (1.11.0)
|
273
|
-
ruby2_keywords (0.0.
|
286
|
+
ruby2_keywords (0.0.5)
|
274
287
|
sawyer (0.8.2)
|
275
288
|
addressable (>= 2.3.5)
|
276
289
|
faraday (> 0.8, < 2.0)
|
277
|
-
semantic_logger (4.
|
290
|
+
semantic_logger (4.8.2)
|
278
291
|
concurrent-ruby (~> 1.0)
|
279
292
|
signet (0.15.0)
|
280
293
|
addressable (~> 2.3)
|
@@ -296,11 +309,11 @@ GEM
|
|
296
309
|
tzinfo (1.2.9)
|
297
310
|
thread_safe (~> 0.1)
|
298
311
|
unicode-display_width (1.6.1)
|
299
|
-
webmock (3.
|
300
|
-
addressable (>= 2.
|
312
|
+
webmock (3.14.0)
|
313
|
+
addressable (>= 2.8.0)
|
301
314
|
crack (>= 0.3.2)
|
302
315
|
hashdiff (>= 0.4.0, < 2.0.0)
|
303
|
-
websocket-driver (0.7.
|
316
|
+
websocket-driver (0.7.5)
|
304
317
|
websocket-extensions (>= 0.1.0)
|
305
318
|
websocket-extensions (0.1.5)
|
306
319
|
zeitwerk (2.4.2)
|
@@ -39,7 +39,7 @@ module Cloudtasker
|
|
39
39
|
def self.all
|
40
40
|
if redis.exists?(key)
|
41
41
|
# Use Schedule Set if available
|
42
|
-
redis.smembers(key).map { |id| find(id) }
|
42
|
+
redis.smembers(key).map { |id| find(id) }.compact
|
43
43
|
else
|
44
44
|
# Fallback to redis key matching and migrate tasks
|
45
45
|
# to use Task Set instead.
|
@@ -73,8 +73,12 @@ module Cloudtasker
|
|
73
73
|
# Load extension if not loaded already on the worker class
|
74
74
|
worker.class.include(Extension::Worker) unless worker.class <= Extension::Worker
|
75
75
|
|
76
|
-
# Add batch
|
76
|
+
# Add batch and parent batch to worker
|
77
77
|
worker.batch = new(worker)
|
78
|
+
worker.parent_batch = worker.batch.parent_batch
|
79
|
+
|
80
|
+
# Return the batch
|
81
|
+
worker.batch
|
78
82
|
end
|
79
83
|
|
80
84
|
#
|
@@ -259,9 +263,7 @@ module Cloudtasker
|
|
259
263
|
migrate_batch_state_to_redis_hash
|
260
264
|
|
261
265
|
# Update the batch state batch_id entry with the new status
|
262
|
-
redis.
|
263
|
-
redis.hset(batch_state_gid, batch_id, status) if redis.hexists(batch_state_gid, batch_id)
|
264
|
-
end
|
266
|
+
redis.hset(batch_state_gid, batch_id, status) if redis.hexists(batch_state_gid, batch_id)
|
265
267
|
end
|
266
268
|
|
267
269
|
#
|
@@ -273,10 +275,7 @@ module Cloudtasker
|
|
273
275
|
migrate_batch_state_to_redis_hash
|
274
276
|
|
275
277
|
# Check that all child jobs have completed
|
276
|
-
redis.
|
277
|
-
# Check that all children are complete
|
278
|
-
redis.hvals(batch_state_gid).all? { |e| COMPLETION_STATUSES.include?(e) }
|
279
|
-
end
|
278
|
+
redis.hvals(batch_state_gid).all? { |e| COMPLETION_STATUSES.include?(e) }
|
280
279
|
end
|
281
280
|
|
282
281
|
#
|
@@ -311,8 +310,8 @@ module Cloudtasker
|
|
311
310
|
# Propagate event
|
312
311
|
parent_batch&.on_child_complete(self, status)
|
313
312
|
|
314
|
-
# The batch tree is complete. Cleanup the tree.
|
315
|
-
cleanup
|
313
|
+
# The batch tree is complete. Cleanup the downstream tree.
|
314
|
+
cleanup
|
316
315
|
end
|
317
316
|
|
318
317
|
#
|
@@ -427,8 +426,11 @@ module Cloudtasker
|
|
427
426
|
# Perform job
|
428
427
|
yield
|
429
428
|
|
430
|
-
# Save batch
|
431
|
-
setup
|
429
|
+
# Save batch if child jobs added
|
430
|
+
setup if jobs.any?
|
431
|
+
|
432
|
+
# Save parent batch if batch expanded
|
433
|
+
parent_batch&.setup if parent_batch&.jobs&.any?
|
432
434
|
|
433
435
|
# Complete batch
|
434
436
|
complete(:completed)
|
data/lib/cloudtasker/config.rb
CHANGED
@@ -7,7 +7,8 @@ module Cloudtasker
|
|
7
7
|
class Config
|
8
8
|
attr_accessor :redis, :store_payloads_in_redis
|
9
9
|
attr_writer :secret, :gcp_location_id, :gcp_project_id,
|
10
|
-
:gcp_queue_prefix, :processor_path, :logger, :mode, :max_retries,
|
10
|
+
:gcp_queue_prefix, :processor_path, :logger, :mode, :max_retries,
|
11
|
+
:dispatch_deadline, :on_error, :on_dead
|
11
12
|
|
12
13
|
# Max Cloud Task size in bytes
|
13
14
|
MAX_TASK_SIZE = 100 * 1024 # 100 KB
|
@@ -51,6 +52,9 @@ module Cloudtasker
|
|
51
52
|
MIN_DISPATCH_DEADLINE = 15 # seconds
|
52
53
|
MAX_DISPATCH_DEADLINE = 30 * 60 # 30 minutes
|
53
54
|
|
55
|
+
# Default on_error Proc
|
56
|
+
DEFAULT_ON_ERROR = ->(error, worker) {}
|
57
|
+
|
54
58
|
# The number of times jobs will be attempted before declaring them dead.
|
55
59
|
#
|
56
60
|
# With the default retry configuration (maxDoublings = 16 and minBackoff = 0.100s)
|
@@ -229,11 +233,31 @@ module Cloudtasker
|
|
229
233
|
# @return [String] The cloudtasker secret
|
230
234
|
#
|
231
235
|
def secret
|
232
|
-
@secret
|
236
|
+
@secret ||= (
|
233
237
|
defined?(Rails) && Rails.application.credentials&.dig(:secret_key_base)
|
234
238
|
) || raise(StandardError, SECRET_MISSING_ERROR)
|
235
239
|
end
|
236
240
|
|
241
|
+
#
|
242
|
+
# Return a Proc invoked whenever a worker runtime error is raised.
|
243
|
+
# See Cloudtasker::WorkerHandler.with_worker_handling
|
244
|
+
#
|
245
|
+
# @return [Proc] A Proc handler
|
246
|
+
#
|
247
|
+
def on_error
|
248
|
+
@on_error || DEFAULT_ON_ERROR
|
249
|
+
end
|
250
|
+
|
251
|
+
#
|
252
|
+
# Return a Proc invoked whenever a worker DeadWorkerError is raised.
|
253
|
+
# See Cloudtasker::WorkerHandler.with_worker_handling
|
254
|
+
#
|
255
|
+
# @return [Proc] A Proc handler
|
256
|
+
#
|
257
|
+
def on_dead
|
258
|
+
@on_dead || DEFAULT_ON_ERROR
|
259
|
+
end
|
260
|
+
|
237
261
|
#
|
238
262
|
# Return the chain of client middlewares.
|
239
263
|
#
|
@@ -149,25 +149,18 @@ module Cloudtasker
|
|
149
149
|
# if taken by another job.
|
150
150
|
#
|
151
151
|
def lock!
|
152
|
-
redis.
|
153
|
-
|
152
|
+
lock_acquired = redis.set(unique_gid, id, nx: true, ex: lock_ttl)
|
153
|
+
lock_already_acquired = !lock_acquired && redis.get(unique_gid) == id
|
154
154
|
|
155
|
-
|
156
|
-
raise(LockError, locked_id) if locked_id && locked_id != id
|
157
|
-
|
158
|
-
# Take job lock if the lock is currently free
|
159
|
-
redis.set(unique_gid, id, ex: lock_ttl) unless locked_id
|
160
|
-
end
|
155
|
+
raise(LockError) unless lock_acquired || lock_already_acquired
|
161
156
|
end
|
162
157
|
|
163
158
|
#
|
164
159
|
# Delete the job lock.
|
165
160
|
#
|
166
161
|
def unlock!
|
167
|
-
redis.
|
168
|
-
|
169
|
-
redis.del(unique_gid) if locked_id == id
|
170
|
-
end
|
162
|
+
locked_id = redis.get(unique_gid)
|
163
|
+
redis.del(unique_gid) if locked_id == id
|
171
164
|
end
|
172
165
|
end
|
173
166
|
end
|
data/lib/cloudtasker/version.rb
CHANGED
@@ -14,12 +14,6 @@ module Cloudtasker
|
|
14
14
|
# payloads in Redis
|
15
15
|
REDIS_PAYLOAD_NAMESPACE = 'payload'
|
16
16
|
|
17
|
-
# Arg payload cache keys get expired instead of deleted
|
18
|
-
# in case jobs are re-processed due to connection interruption
|
19
|
-
# (job is successful but Cloud Task considers it as failed due
|
20
|
-
# to network interruption)
|
21
|
-
ARGS_PAYLOAD_CLEANUP_TTL = 3600 # 1 hour
|
22
|
-
|
23
17
|
#
|
24
18
|
# Return a namespaced key
|
25
19
|
#
|
@@ -100,20 +94,19 @@ module Cloudtasker
|
|
100
94
|
# Yied worker
|
101
95
|
resp = yield(worker)
|
102
96
|
|
103
|
-
#
|
104
|
-
|
105
|
-
# succeeds but is considered as failed by Cloud Task due to network interruption.
|
106
|
-
# In such case the job is likely to be re-processed soon after.
|
107
|
-
redis.expire(args_payload_key, ARGS_PAYLOAD_CLEANUP_TTL) if args_payload_key && !worker.job_reenqueued
|
97
|
+
# Delete stored args payload if job has completed
|
98
|
+
redis.del(args_payload_key) if args_payload_key && !worker.job_reenqueued
|
108
99
|
|
109
100
|
resp
|
110
101
|
rescue DeadWorkerError => e
|
111
102
|
# Delete stored args payload if job is dead
|
112
|
-
redis.
|
103
|
+
redis.del(args_payload_key) if args_payload_key
|
113
104
|
log_execution_error(worker, e)
|
105
|
+
Cloudtasker.config.on_dead.call(e, worker)
|
114
106
|
raise(e)
|
115
107
|
rescue StandardError => e
|
116
108
|
log_execution_error(worker, e)
|
109
|
+
Cloudtasker.config.on_error.call(e, worker)
|
117
110
|
raise(e)
|
118
111
|
end
|
119
112
|
|