cloudtasker 0.9.5 → 0.10.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +16 -0
  4. data/Appraisals +0 -16
  5. data/CHANGELOG.md +0 -22
  6. data/README.md +40 -1
  7. data/app/controllers/cloudtasker/application_controller.rb +8 -0
  8. data/app/controllers/cloudtasker/worker_controller.rb +1 -4
  9. data/cloudtasker.gemspec +1 -2
  10. data/gemfiles/google_cloud_tasks_1.0.gemfile +5 -3
  11. data/gemfiles/google_cloud_tasks_1.0.gemfile.lock +154 -208
  12. data/gemfiles/google_cloud_tasks_1.1.gemfile +5 -3
  13. data/gemfiles/google_cloud_tasks_1.1.gemfile.lock +154 -208
  14. data/gemfiles/google_cloud_tasks_1.2.gemfile +5 -3
  15. data/gemfiles/google_cloud_tasks_1.2.gemfile.lock +154 -208
  16. data/gemfiles/google_cloud_tasks_1.3.gemfile +5 -3
  17. data/gemfiles/google_cloud_tasks_1.3.gemfile.lock +154 -208
  18. data/gemfiles/rails_5.2.gemfile +5 -3
  19. data/gemfiles/rails_5.2.gemfile.lock +91 -146
  20. data/gemfiles/rails_6.0.gemfile +5 -3
  21. data/gemfiles/rails_6.0.gemfile.lock +92 -147
  22. data/lib/cloudtasker/backend/google_cloud_task.rb +1 -1
  23. data/lib/cloudtasker/backend/memory_task.rb +8 -23
  24. data/lib/cloudtasker/config.rb +16 -1
  25. data/lib/cloudtasker/testing.rb +2 -2
  26. data/lib/cloudtasker/version.rb +1 -1
  27. data/lib/cloudtasker/worker_handler.rb +142 -5
  28. data/lib/cloudtasker/worker_logger.rb +1 -2
  29. data/lib/cloudtasker.rb +0 -1
  30. metadata +15 -32
  31. data/.github/workflows/test.yml +0 -45
  32. data/gemfiles/semantic_logger_3.4.gemfile +0 -7
  33. data/gemfiles/semantic_logger_4.6.gemfile +0 -7
  34. data/gemfiles/semantic_logger_4.7.0.gemfile +0 -7
  35. data/gemfiles/semantic_logger_4.7.2.gemfile +0 -7
  36. data/gemfiles/semantic_logger_4.7.gemfile +0 -9
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- cloudtasker (0.9.4)
4
+ cloudtasker (0.10.rc1)
5
5
  activesupport
6
6
  fugit
7
- google-cloud-tasks (~> 1.0)
7
+ google-cloud-tasks
8
8
  jwt
9
9
  redis
10
10
 
@@ -66,79 +66,40 @@ GEM
66
66
  minitest (~> 5.1)
67
67
  tzinfo (~> 1.1)
68
68
  zeitwerk (~> 2.1, >= 2.1.8)
69
- addressable (2.8.0)
69
+ addressable (2.7.0)
70
70
  public_suffix (>= 2.0.2, < 5.0)
71
- appraisal (2.4.1)
71
+ appraisal (2.2.0)
72
72
  bundler
73
73
  rake
74
74
  thor (>= 0.14.0)
75
- ast (2.4.2)
76
- async (1.30.1)
77
- console (~> 1.10)
78
- nio4r (~> 2.3)
79
- timers (~> 4.1)
80
- async-http (0.56.5)
81
- async (>= 1.25)
82
- async-io (>= 1.28)
83
- async-pool (>= 0.2)
84
- protocol-http (~> 0.22.0)
85
- protocol-http1 (~> 0.14.0)
86
- protocol-http2 (~> 0.14.0)
87
- async-http-faraday (0.11.0)
88
- async-http (~> 0.42)
89
- faraday
90
- async-io (1.32.2)
91
- async
92
- async-pool (0.3.8)
93
- async (>= 1.25)
75
+ ast (2.4.0)
94
76
  builder (3.2.4)
95
- concurrent-ruby (1.1.9)
96
- console (1.13.1)
97
- fiber-local
98
- crack (0.4.5)
99
- rexml
77
+ concurrent-ruby (1.1.6)
78
+ crack (0.4.3)
79
+ safe_yaml (~> 1.0.0)
100
80
  crass (1.0.6)
101
- diff-lcs (1.4.4)
102
- erubi (1.10.0)
103
- et-orbi (1.2.4)
81
+ diff-lcs (1.3)
82
+ erubi (1.9.0)
83
+ et-orbi (1.2.3)
104
84
  tzinfo
105
- faraday (1.7.0)
106
- faraday-em_http (~> 1.0)
107
- faraday-em_synchrony (~> 1.0)
108
- faraday-excon (~> 1.1)
109
- faraday-httpclient (~> 1.0.1)
110
- faraday-net_http (~> 1.0)
111
- faraday-net_http_persistent (~> 1.1)
112
- faraday-patron (~> 1.0)
113
- faraday-rack (~> 1.0)
85
+ faraday (0.17.3)
114
86
  multipart-post (>= 1.2, < 3)
115
- ruby2_keywords (>= 0.0.4)
116
- faraday-em_http (1.0.0)
117
- faraday-em_synchrony (1.0.0)
118
- faraday-excon (1.1.0)
119
- faraday-http-cache (2.2.0)
120
- faraday (>= 0.8)
121
- faraday-httpclient (1.0.1)
122
- faraday-net_http (1.0.1)
123
- faraday-net_http_persistent (1.2.0)
124
- faraday-patron (1.0.0)
125
- faraday-rack (1.0.0)
126
- fiber-local (1.0.0)
127
- fugit (1.5.1)
87
+ faraday-http-cache (2.0.0)
88
+ faraday (~> 0.8)
89
+ fugit (1.3.3)
128
90
  et-orbi (~> 1.1, >= 1.1.8)
129
- raabro (~> 1.4)
130
- github_changelog_generator (1.16.4)
91
+ raabro (~> 1.1)
92
+ github_changelog_generator (1.15.0)
131
93
  activesupport
132
- async (>= 1.25.0)
133
- async-http-faraday
134
94
  faraday-http-cache
135
95
  multi_json
136
96
  octokit (~> 4.6)
137
97
  rainbow (>= 2.2.1)
138
98
  rake (>= 10.0)
139
- globalid (0.5.2)
140
- activesupport (>= 5.0)
141
- google-cloud-tasks (1.5.1)
99
+ retriable (~> 3.0)
100
+ globalid (0.4.2)
101
+ activesupport (>= 4.2.0)
102
+ google-cloud-tasks (1.4.0)
142
103
  google-gax (~> 1.8)
143
104
  googleapis-common-protos (>= 1.3.9, < 2.0)
144
105
  googleapis-common-protos-types (>= 1.0.4, < 2.0)
@@ -149,33 +110,32 @@ GEM
149
110
  googleauth (~> 0.9)
150
111
  grpc (~> 1.24)
151
112
  rly (~> 0.2.3)
152
- google-protobuf (3.17.3)
153
- googleapis-common-protos (1.3.11)
154
- google-protobuf (~> 3.14)
155
- googleapis-common-protos-types (>= 1.0.6, < 2.0)
156
- grpc (~> 1.27)
157
- googleapis-common-protos-types (1.1.0)
158
- google-protobuf (~> 3.14)
159
- googleauth (0.17.0)
113
+ google-protobuf (3.11.4)
114
+ googleapis-common-protos (1.3.9)
115
+ google-protobuf (~> 3.0)
116
+ googleapis-common-protos-types (~> 1.0)
117
+ grpc (~> 1.0)
118
+ googleapis-common-protos-types (1.0.4)
119
+ google-protobuf (~> 3.0)
120
+ googleauth (0.11.0)
160
121
  faraday (>= 0.17.3, < 2.0)
161
122
  jwt (>= 1.4, < 3.0)
162
123
  memoist (~> 0.16)
163
124
  multi_json (~> 1.11)
164
125
  os (>= 0.9, < 2.0)
165
- signet (~> 0.14)
166
- grpc (1.38.0)
167
- google-protobuf (~> 3.15)
126
+ signet (~> 0.12)
127
+ grpc (1.27.0)
128
+ google-protobuf (~> 3.11)
168
129
  googleapis-common-protos-types (~> 1.0)
169
- grpc-google-iam-v1 (0.6.11)
170
- google-protobuf (~> 3.14)
171
- googleapis-common-protos (>= 1.3.11, < 2.0)
172
- grpc (~> 1.27)
130
+ grpc-google-iam-v1 (0.6.9)
131
+ googleapis-common-protos (>= 1.3.1, < 2.0)
132
+ grpc (~> 1.0)
173
133
  hashdiff (1.0.1)
174
- i18n (1.8.10)
134
+ i18n (1.8.2)
175
135
  concurrent-ruby (~> 1.0)
176
136
  jaro_winkler (1.5.4)
177
- jwt (2.2.3)
178
- loofah (2.12.0)
137
+ jwt (2.2.1)
138
+ loofah (2.4.0)
179
139
  crass (~> 1.0.2)
180
140
  nokogiri (>= 1.5.9)
181
141
  mail (2.7.1)
@@ -183,37 +143,26 @@ GEM
183
143
  marcel (0.3.3)
184
144
  mimemagic (~> 0.3.2)
185
145
  memoist (0.16.2)
186
- method_source (1.0.0)
187
- mimemagic (0.3.10)
188
- nokogiri (~> 1)
189
- rake
190
- mini_mime (1.1.0)
191
- mini_portile2 (2.6.1)
192
- minitest (5.14.4)
193
- multi_json (1.15.0)
146
+ method_source (0.9.2)
147
+ mimemagic (0.3.4)
148
+ mini_mime (1.0.2)
149
+ mini_portile2 (2.4.0)
150
+ minitest (5.14.0)
151
+ multi_json (1.14.1)
194
152
  multipart-post (2.1.1)
195
- nio4r (2.5.8)
196
- nokogiri (1.12.3)
197
- mini_portile2 (~> 2.6.1)
198
- racc (~> 1.4)
199
- octokit (4.21.0)
153
+ nio4r (2.5.2)
154
+ nokogiri (1.10.9)
155
+ mini_portile2 (~> 2.4.0)
156
+ octokit (4.16.0)
200
157
  faraday (>= 0.9)
201
158
  sawyer (~> 0.8.0, >= 0.5.3)
202
- os (1.1.1)
203
- parallel (1.20.1)
204
- parser (3.0.2.0)
205
- ast (~> 2.4.1)
206
- protocol-hpack (1.4.2)
207
- protocol-http (0.22.5)
208
- protocol-http1 (0.14.1)
209
- protocol-http (~> 0.22)
210
- protocol-http2 (0.14.2)
211
- protocol-hpack (~> 1.4)
212
- protocol-http (~> 0.18)
213
- public_suffix (4.0.6)
214
- raabro (1.4.0)
215
- racc (1.5.2)
216
- rack (2.2.3)
159
+ os (1.0.1)
160
+ parallel (1.19.1)
161
+ parser (2.7.0.4)
162
+ ast (~> 2.4.0)
163
+ public_suffix (4.0.3)
164
+ raabro (1.1.6)
165
+ rack (2.2.2)
217
166
  rack-test (1.1.0)
218
167
  rack (>= 1.0, < 3)
219
168
  rails (6.0.0)
@@ -234,7 +183,7 @@ GEM
234
183
  rails-dom-testing (2.0.3)
235
184
  activesupport (>= 4.2.0)
236
185
  nokogiri (>= 1.6)
237
- rails-html-sanitizer (1.4.1)
186
+ rails-html-sanitizer (1.3.0)
238
187
  loofah (~> 2.3)
239
188
  railties (6.0.0)
240
189
  actionpack (= 6.0.0)
@@ -243,31 +192,31 @@ GEM
243
192
  rake (>= 0.8.7)
244
193
  thor (>= 0.20.3, < 2.0)
245
194
  rainbow (3.0.0)
246
- rake (13.0.6)
247
- redis (4.4.0)
248
- rexml (3.2.5)
195
+ rake (13.0.1)
196
+ redis (4.1.3)
197
+ retriable (3.1.2)
249
198
  rly (0.2.3)
250
- rspec (3.10.0)
251
- rspec-core (~> 3.10.0)
252
- rspec-expectations (~> 3.10.0)
253
- rspec-mocks (~> 3.10.0)
254
- rspec-core (3.10.1)
255
- rspec-support (~> 3.10.0)
256
- rspec-expectations (3.10.1)
199
+ rspec (3.9.0)
200
+ rspec-core (~> 3.9.0)
201
+ rspec-expectations (~> 3.9.0)
202
+ rspec-mocks (~> 3.9.0)
203
+ rspec-core (3.9.1)
204
+ rspec-support (~> 3.9.1)
205
+ rspec-expectations (3.9.0)
257
206
  diff-lcs (>= 1.2.0, < 2.0)
258
- rspec-support (~> 3.10.0)
259
- rspec-mocks (3.10.2)
207
+ rspec-support (~> 3.9.0)
208
+ rspec-mocks (3.9.1)
260
209
  diff-lcs (>= 1.2.0, < 2.0)
261
- rspec-support (~> 3.10.0)
262
- rspec-rails (5.0.2)
263
- actionpack (>= 5.2)
264
- activesupport (>= 5.2)
265
- railties (>= 5.2)
266
- rspec-core (~> 3.10)
267
- rspec-expectations (~> 3.10)
268
- rspec-mocks (~> 3.10)
269
- rspec-support (~> 3.10)
270
- rspec-support (3.10.2)
210
+ rspec-support (~> 3.9.0)
211
+ rspec-rails (3.9.0)
212
+ actionpack (>= 3.0)
213
+ activesupport (>= 3.0)
214
+ railties (>= 3.0)
215
+ rspec-core (~> 3.9.0)
216
+ rspec-expectations (~> 3.9.0)
217
+ rspec-mocks (~> 3.9.0)
218
+ rspec-support (~> 3.9.0)
219
+ rspec-support (3.9.2)
271
220
  rubocop (0.76.0)
272
221
  jaro_winkler (~> 1.5.1)
273
222
  parallel (~> 1.10)
@@ -277,41 +226,38 @@ GEM
277
226
  unicode-display_width (>= 1.4.0, < 1.7)
278
227
  rubocop-rspec (1.37.0)
279
228
  rubocop (>= 0.68.1)
280
- ruby-progressbar (1.11.0)
281
- ruby2_keywords (0.0.5)
229
+ ruby-progressbar (1.10.1)
230
+ safe_yaml (1.0.5)
282
231
  sawyer (0.8.2)
283
232
  addressable (>= 2.3.5)
284
233
  faraday (> 0.8, < 2.0)
285
- semantic_logger (4.8.2)
286
- concurrent-ruby (~> 1.0)
287
- signet (0.15.0)
234
+ signet (0.13.0)
288
235
  addressable (~> 2.3)
289
236
  faraday (>= 0.17.3, < 2.0)
290
237
  jwt (>= 1.5, < 3.0)
291
238
  multi_json (~> 1.10)
292
- sprockets (4.0.2)
239
+ sprockets (4.0.0)
293
240
  concurrent-ruby (~> 1.0)
294
241
  rack (> 1, < 3)
295
- sprockets-rails (3.2.2)
242
+ sprockets-rails (3.2.1)
296
243
  actionpack (>= 4.0)
297
244
  activesupport (>= 4.0)
298
245
  sprockets (>= 3.0.0)
299
246
  sqlite3 (1.4.2)
300
- thor (1.1.0)
247
+ thor (1.0.1)
301
248
  thread_safe (0.3.6)
302
- timecop (0.9.4)
303
- timers (4.3.3)
304
- tzinfo (1.2.9)
249
+ timecop (0.9.1)
250
+ tzinfo (1.2.6)
305
251
  thread_safe (~> 0.1)
306
252
  unicode-display_width (1.6.1)
307
- webmock (3.14.0)
308
- addressable (>= 2.8.0)
253
+ webmock (3.8.2)
254
+ addressable (>= 2.3.6)
309
255
  crack (>= 0.3.2)
310
256
  hashdiff (>= 0.4.0, < 2.0.0)
311
- websocket-driver (0.7.5)
257
+ websocket-driver (0.7.1)
312
258
  websocket-extensions (>= 0.1.0)
313
- websocket-extensions (0.1.5)
314
- zeitwerk (2.4.2)
259
+ websocket-extensions (0.1.4)
260
+ zeitwerk (2.3.0)
315
261
 
316
262
  PLATFORMS
317
263
  ruby
@@ -327,10 +273,9 @@ DEPENDENCIES
327
273
  rspec-rails
328
274
  rubocop (= 0.76.0)
329
275
  rubocop-rspec (= 1.37.0)
330
- semantic_logger
331
276
  sqlite3
332
277
  timecop
333
278
  webmock
334
279
 
335
280
  BUNDLED WITH
336
- 2.2.9
281
+ 2.1.4
@@ -146,7 +146,7 @@ module Cloudtasker
146
146
  #
147
147
  def self.delete(id)
148
148
  client.delete_task(id)
149
- rescue Google::Gax::RetryError, GRPC::NotFound
149
+ rescue Google::Gax::RetryError, GRPC::NotFound, Google::Gax::PermissionDeniedError
150
150
  nil
151
151
  end
152
152
 
@@ -7,6 +7,7 @@ module Cloudtasker
7
7
  # Manage local tasks pushed to memory.
8
8
  # Used for testing.
9
9
  class MemoryTask
10
+ attr_accessor :job_retries
10
11
  attr_reader :id, :http_request, :schedule_time, :queue
11
12
 
12
13
  #
@@ -18,17 +19,6 @@ module Cloudtasker
18
19
  @queue ||= []
19
20
  end
20
21
 
21
- #
22
- # Return the workers currently in the queue.
23
- #
24
- # @param [String] worker_class_name Filter jobs on worker class name.
25
- #
26
- # @return [Array<Cloudtasker::Worker] The list of workers
27
- #
28
- def self.jobs(worker_class_name = nil)
29
- all(worker_class_name).map(&:worker)
30
- end
31
-
32
22
  #
33
23
  # Run all Tasks in the queue. Optionally filter which tasks to run based
34
24
  # on the worker class name.
@@ -116,11 +106,12 @@ module Cloudtasker
116
106
  # @param [Hash] http_request The HTTP request content.
117
107
  # @param [Integer] schedule_time When to run the task (Unix timestamp)
118
108
  #
119
- def initialize(id:, http_request:, schedule_time: nil, queue: nil)
109
+ def initialize(id:, http_request:, schedule_time: nil, queue: nil, job_retries: 0)
120
110
  @id = id
121
111
  @http_request = http_request
122
112
  @schedule_time = Time.at(schedule_time || 0)
123
113
  @queue = queue
114
+ @job_retries = job_retries || 0
124
115
  end
125
116
 
126
117
  #
@@ -155,26 +146,20 @@ module Cloudtasker
155
146
  }
156
147
  end
157
148
 
158
- #
159
- # Return the worker attached to this task.
160
- #
161
- # @return [Cloudtasker::Worker] The task worker.
162
- #
163
- def worker
164
- @worker ||= Worker.from_hash(payload)
165
- end
166
-
167
149
  #
168
150
  # Execute the task.
169
151
  #
170
152
  # @return [Any] The return value of the worker perform method.
171
153
  #
172
154
  def execute
173
- resp = worker.execute
155
+ # Execute worker
156
+ resp = WorkerHandler.with_worker_handling(payload, &:execute)
157
+
158
+ # Delete task
174
159
  self.class.delete(id)
175
160
  resp
176
161
  rescue StandardError
177
- worker.job_retries += 1
162
+ self.job_retries += 1
178
163
  end
179
164
 
180
165
  #
@@ -5,7 +5,7 @@ require 'logger'
5
5
  module Cloudtasker
6
6
  # Holds cloudtasker configuration. See Cloudtasker#configure
7
7
  class Config
8
- attr_accessor :redis
8
+ attr_accessor :redis, :store_payloads_in_redis
9
9
  attr_writer :secret, :gcp_location_id, :gcp_project_id,
10
10
  :gcp_queue_prefix, :processor_path, :logger, :mode, :max_retries
11
11
 
@@ -54,6 +54,21 @@ module Cloudtasker
54
54
  Please specify a secret in the cloudtasker initializer or add Rails secret_key_base in your credentials
55
55
  DOC
56
56
 
57
+ #
58
+ # Return the threshold above which job arguments must be stored
59
+ # in Redis instead of being sent to the backend as part of the job
60
+ # payload.
61
+ #
62
+ # Return nil if redis payload storage is disabled.
63
+ #
64
+ # @return [Integer, nil] The threshold above which payloads will be stored in Redis.
65
+ #
66
+ def redis_payload_storage_threshold
67
+ return nil unless store_payloads_in_redis
68
+
69
+ store_payloads_in_redis.respond_to?(:to_i) ? store_payloads_in_redis.to_i : 0
70
+ end
71
+
57
72
  #
58
73
  # The number of times jobs will be retried. This number of
59
74
  # retries does not include failures due to the application being unreachable.
@@ -114,10 +114,10 @@ module Cloudtasker
114
114
  #
115
115
  # Return all jobs related to this worker class.
116
116
  #
117
- # @return [Array<Cloudtasker::Worker] The list of workers
117
+ # @return [Array<Cloudtasker::Backend::MemoryTask>] The list of tasks
118
118
  #
119
119
  def jobs
120
- Backend::MemoryTask.jobs(to_s)
120
+ Backend::MemoryTask.all(to_s)
121
121
  end
122
122
 
123
123
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cloudtasker
4
- VERSION = '0.9.5'
4
+ VERSION = '0.10.rc1'
5
5
  end
@@ -10,16 +10,113 @@ module Cloudtasker
10
10
  # Alrogith used to sign the verification token
11
11
  JWT_ALG = 'HS256'
12
12
 
13
+ # Sub-namespace to use for redis keys when storing
14
+ # payloads in Redis
15
+ REDIS_PAYLOAD_NAMESPACE = 'payload'
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
+ #
24
+ # Return a namespaced key
25
+ #
26
+ # @param [String, Symbol] val The key to namespace
27
+ #
28
+ # @return [String] The namespaced key.
29
+ #
30
+ def self.key(val)
31
+ return nil if val.nil?
32
+
33
+ [to_s.underscore, val.to_s].join('/')
34
+ end
35
+
36
+ #
37
+ # Return the cloudtasker redis client
38
+ #
39
+ # @return [Cloudtasker::RedisClient] The cloudtasker redis client.
40
+ #
41
+ def self.redis
42
+ @redis ||= begin
43
+ require 'cloudtasker/redis_client'
44
+ RedisClient.new
45
+ end
46
+ end
47
+
13
48
  #
14
49
  # Execute a task worker from a task payload
15
50
  #
16
- # @param [Hash] payload The Cloud Task payload.
51
+ # @param [Hash] input_payload The Cloud Task payload.
17
52
  #
18
53
  # @return [Any] The return value of the worker perform method.
19
54
  #
20
- def self.execute_from_payload!(payload)
55
+ def self.execute_from_payload!(input_payload)
56
+ with_worker_handling(input_payload, &:execute)
57
+ end
58
+
59
+ # TODO: do not delete redis payload if job has been re-enqueued
60
+ # worker.job_reenqueued
61
+ #
62
+ # Idea: change with_worker_handling to with_worker_handling and build the worker
63
+ # inside the with_worker_handling block.
64
+ #
65
+ # Local middleware used to retrieve the job arg payload from cache
66
+ # if a arg payload reference is present.
67
+ #
68
+ # @param [Hash] payload The full job payload
69
+ #
70
+ # @yield [Hash] The actual payload to use to process the job.
71
+ #
72
+ # @return [Any] The block result
73
+ #
74
+ def self.with_worker_handling(input_payload)
75
+ # Extract payload information
76
+ extracted_payload = extract_payload(input_payload)
77
+ payload = extracted_payload[:payload]
78
+ args_payload_key = extracted_payload[:args_payload_key]
79
+
80
+ # Build worker
21
81
  worker = Cloudtasker::Worker.from_hash(payload) || raise(InvalidWorkerError)
22
- worker.execute
82
+
83
+ # Yied worker
84
+ resp = yield(worker)
85
+
86
+ # Schedule args payload deletion after job has been successfully processed
87
+ # Note: we expire the key instead of deleting it immediately in case the job
88
+ # succeeds but is considered as failed by Cloud Task due to network interruption.
89
+ # In such case the job is likely to be re-processed soon after.
90
+ redis.expire(args_payload_key, ARGS_PAYLOAD_CLEANUP_TTL) if args_payload_key && !worker.job_reenqueued
91
+
92
+ resp
93
+ rescue DeadWorkerError => e
94
+ # Delete stored args payload if job is dead
95
+ redis.expire(args_payload_key, ARGS_PAYLOAD_CLEANUP_TTL) if args_payload_key
96
+ raise(e)
97
+ end
98
+
99
+ #
100
+ # Return the argument payload key (if present) along with the actual worker payload.
101
+ #
102
+ # If the payload was stored in Redis then retrieve it.
103
+ #
104
+ # @return [Hash] Hash
105
+ #
106
+ def self.extract_payload(input_payload)
107
+ # Get references
108
+ payload = JSON.parse(input_payload.to_json, symbolize_names: true)
109
+ args_payload_id = payload.delete(:job_args_payload_id)
110
+ args_payload_key = args_payload_id ? key([REDIS_PAYLOAD_NAMESPACE, args_payload_id].join('/')) : nil
111
+
112
+ # Retrieve the actual worker args payload
113
+ args_payload = args_payload_key ? redis.fetch(args_payload_key) : payload[:job_args]
114
+
115
+ # Return the payload
116
+ {
117
+ args_payload_key: args_payload_key,
118
+ payload: payload.merge(job_args: args_payload)
119
+ }
23
120
  end
24
121
 
25
122
  #
@@ -51,6 +148,47 @@ module Cloudtasker
51
148
  }
52
149
  end
53
150
 
151
+ #
152
+ # Return true if the worker args must be stored in Redis.
153
+ #
154
+ # @return [Boolean] True if the payload must be stored in redis.
155
+ #
156
+ def store_payload_in_redis?
157
+ Cloudtasker.config.redis_payload_storage_threshold &&
158
+ worker.job_args.to_json.bytesize > (Cloudtasker.config.redis_payload_storage_threshold * 1024)
159
+ end
160
+
161
+ #
162
+ # Return the payload to use for job arguments. This payload
163
+ # is merged inside the #worker_payload.
164
+ #
165
+ # If the argument payload must be stored in Redis then returns:
166
+ # `{ job_args_payload_id: <worker_id> }`
167
+ #
168
+ # If the argument payload must be natively handled by the backend
169
+ # then returns:
170
+ # `{ job_args: [...] }`
171
+ #
172
+ # @return [Hash] The worker args payload.
173
+ #
174
+ def worker_args_payload
175
+ @worker_args_payload ||= begin
176
+ if store_payload_in_redis?
177
+ # Store payload in Redis
178
+ self.class.redis.write(
179
+ self.class.key([REDIS_PAYLOAD_NAMESPACE, worker.job_id].join('/')),
180
+ worker.job_args
181
+ )
182
+
183
+ # Return reference to args payload
184
+ { job_args_payload_id: worker.job_id }
185
+ else
186
+ # Return regular job args payload
187
+ { job_args: worker.job_args }
188
+ end
189
+ end
190
+ end
191
+
54
192
  #
55
193
  # Return the task payload that Google Task will eventually
56
194
  # send to the job processor.
@@ -68,9 +206,8 @@ module Cloudtasker
68
206
  worker: worker.job_class_name,
69
207
  job_queue: worker.job_queue,
70
208
  job_id: worker.job_id,
71
- job_args: worker.job_args,
72
209
  job_meta: worker.job_meta.to_h
73
- }
210
+ }.merge(worker_args_payload)
74
211
  end
75
212
 
76
213
  #
@@ -141,8 +141,7 @@ module Cloudtasker
141
141
  # @param [Proc] &block Optional context block.
142
142
  #
143
143
  def log_message(level, msg, &block)
144
- # Merge log-specific context into worker-specific context
145
- payload_block = ->(*_args) { log_block.call.merge(block&.call || {}) }
144
+ payload_block = block || log_block
146
145
 
147
146
  # ActiveSupport::Logger does not support passing a payload through a block on top
148
147
  # of a message.
data/lib/cloudtasker.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/core_ext/string/inflections'
4
- require 'active_support/core_ext/object/try'
5
4
 
6
5
  require 'cloudtasker/version'
7
6
  require 'cloudtasker/config'