cloudtasker 0.12.rc6 → 0.12.rc7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00be8d2e572e3129ef5330cdbb555aa0485edd15b191d5d8ccc5d28a98a0eab8
4
- data.tar.gz: 79067d7953556f03e9a81ee9d977cf2734a9843df8f9a4613dbd8d81274bc599
3
+ metadata.gz: 96524dc4a6825a3760a462277f7b0a710d779521e759bce44374ea044dbd649d
4
+ data.tar.gz: 908f1497b7ad316549f4f347c363b183181f0e0a17a92bda3f50ac9c34e2247c
5
5
  SHA512:
6
- metadata.gz: 99d9b7ba99390e6bc10e9ed917262782a7c47814df6412c847dedd06a8a06a6b9ad4eaf5ddbcde03534ec88f34fcb1cda03621a3c2a0202031d087e6cd915c5f
7
- data.tar.gz: e15a6a167f15dbd8c390c7d393a80297b6cd805c85d4e5ca16f961ff147c909bebbfcf9c326708e7d7508caa3ff47b726f071e618e3979a1340848420311ddbb
6
+ metadata.gz: e18579d27321e09f1e997ad3fdd3e3e0d1d0c344cd5fc553b64f665937fbe510cd11957381eee1fee5c100e2f14d8390434f26c60307fec4ecf4a681b15885d8
7
+ data.tar.gz: 3b688838f1a518f2f5c7b914a590f035d8a828c2c2b67838173a1712b8765e2ce34cddb8ddf1214aaefc26ce8ec43b7cc1dbe4eb450ccb24feb9e06bcd19bac7
data/CHANGELOG.md CHANGED
@@ -1,16 +1,18 @@
1
1
  # Changelog
2
2
 
3
- ## Latest RC [v0.12.rc6](https://github.com/keypup-io/cloudtasker/tree/v0.12.rc6) (2021-03-31)
3
+ ## Latest RC [v0.12.rc7](https://github.com/keypup-io/cloudtasker/tree/v0.12.rc7) (2021-03-31)
4
4
 
5
- [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.11.0...v0.12.rc6)
5
+ [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.11.0...v0.12.rc7)
6
6
 
7
7
  **Improvements:**
8
8
  - ActiveJob: do not double log errors (ActiveJob has its own error logging)
9
+ - Cron jobs: Use Redis Sets instead of key pattern matching for resource listing
9
10
  - Error logging: Use worker logger so as to include context (job args etc.)
10
11
  - Error logging: Do not log exception and stack trace separately, combine them instead.
11
12
  - Batch callbacks: Retry jobs when completion callback fails
12
- - Redis: Use Redis Sets instead of key pattern matching for listing methods (Cron jobs and Local Server)
13
+ - Batch state: use native Redis hashes to store batch state instead of a serialized hash in a string key
13
14
  - Batch progress: restrict calculation to direct children by default. Allow depth to be specified. Calculating progress using all tree jobs created significant delays on large batches.
15
+ - Local server: Use Redis Sets instead of key pattern matching for resource listing
14
16
  - Worker: raise DeadWorkerError instead of MissingWorkerArgumentsError when arguments are missing. This is more consistent with what middlewares expect.
15
17
 
16
18
  **Fixed bugs:**
@@ -17,6 +17,10 @@ module Cloudtasker
17
17
  # because the jobs will be either retried or dropped
18
18
  IGNORED_ERRORED_CALLBACKS = %i[on_child_error on_child_dead].freeze
19
19
 
20
+ # The maximum number of seconds to wait for a batch state lock
21
+ # to be acquired.
22
+ BATCH_MAX_LOCK_WAIT = 60
23
+
20
24
  #
21
25
  # Return the cloudtasker redis client
22
26
  #
@@ -176,7 +180,9 @@ module Cloudtasker
176
180
  # @return [Hash] The state of each child worker.
177
181
  #
178
182
  def batch_state
179
- redis.fetch(batch_state_gid)
183
+ migrate_batch_state_to_redis_hash
184
+
185
+ redis.hgetall(batch_state_gid)
180
186
  end
181
187
 
182
188
  #
@@ -208,6 +214,24 @@ module Cloudtasker
208
214
  )
209
215
  end
210
216
 
217
+ #
218
+ # This method migrates the batch state to be a Redis hash instead
219
+ # of a hash stored in a string key.
220
+ #
221
+ def migrate_batch_state_to_redis_hash
222
+ return unless redis.type(batch_state_gid) == 'string'
223
+
224
+ # Migrate batch state to Redis hash if it is still using a legacy string key
225
+ # We acquire a lock then check again
226
+ redis.with_lock(batch_state_gid, max_wait: BATCH_MAX_LOCK_WAIT) do
227
+ if redis.type(batch_state_gid) == 'string'
228
+ state = redis.fetch(batch_state_gid)
229
+ redis.del(batch_state_gid)
230
+ redis.hset(batch_state_gid, state) if state.any?
231
+ end
232
+ end
233
+ end
234
+
211
235
  #
212
236
  # Save the batch.
213
237
  #
@@ -218,8 +242,11 @@ module Cloudtasker
218
242
  # complete (success or failure).
219
243
  redis.write(batch_gid, worker.to_h)
220
244
 
245
+ # Stop there if no jobs to save
246
+ return if jobs.empty?
247
+
221
248
  # Save list of child workers
222
- redis.write(batch_state_gid, jobs.map { |e| [e.job_id, 'scheduled'] }.to_h)
249
+ redis.hset(batch_state_gid, jobs.map { |e| [e.job_id, 'scheduled'] }.to_h)
223
250
  end
224
251
 
225
252
  #
@@ -228,28 +255,27 @@ module Cloudtasker
228
255
  # @param [String] job_id The batch id.
229
256
  # @param [String] status The status of the sub-batch.
230
257
  #
231
- # @return [<Type>] <description>
232
- #
233
258
  def update_state(batch_id, status)
234
- redis.with_lock(batch_state_gid) do
235
- state = batch_state
236
- state[batch_id.to_sym] = status.to_s if state.key?(batch_id.to_sym)
237
- redis.write(batch_state_gid, state)
259
+ migrate_batch_state_to_redis_hash
260
+
261
+ # Update the batch state batch_id entry with the new status
262
+ redis.with_lock("#{batch_state_gid}/#{batch_id}", max_wait: BATCH_MAX_LOCK_WAIT) do
263
+ redis.hset(batch_state_gid, batch_id, status) if redis.hexists(batch_state_gid, batch_id)
238
264
  end
239
265
  end
240
266
 
241
267
  #
242
268
  # Return true if all the child workers have completed.
243
269
  #
244
- # @return [<Type>] <description>
270
+ # @return [Boolean] True if the batch is complete.
245
271
  #
246
272
  def complete?
247
- redis.with_lock(batch_state_gid) do
248
- state = redis.fetch(batch_state_gid)
249
- return true unless state
273
+ migrate_batch_state_to_redis_hash
250
274
 
275
+ # Check that all child jobs have completed
276
+ redis.with_lock(batch_state_gid, max_wait: BATCH_MAX_LOCK_WAIT) do
251
277
  # Check that all children are complete
252
- state.values.all? { |e| COMPLETION_STATUSES.include?(e) }
278
+ redis.hvals(batch_state_gid).all? { |e| COMPLETION_STATUSES.include?(e) }
253
279
  end
254
280
  end
255
281
 
@@ -331,11 +357,10 @@ module Cloudtasker
331
357
  # Remove all batch and sub-batch keys from Redis.
332
358
  #
333
359
  def cleanup
334
- # Capture batch state
335
- state = batch_state
360
+ migrate_batch_state_to_redis_hash
336
361
 
337
362
  # Delete child batches recursively
338
- state.to_h.keys.each { |id| self.class.find(id)&.cleanup }
363
+ redis.hkeys(batch_state_gid).each { |id| self.class.find(id)&.cleanup }
339
364
 
340
365
  # Delete batch redis entries
341
366
  redis.del(batch_gid)
@@ -402,7 +427,7 @@ module Cloudtasker
402
427
  # Perform job
403
428
  yield
404
429
 
405
- # Save batch (if child worker has been enqueued)
430
+ # Save batch (if child workers have been enqueued)
406
431
  setup
407
432
 
408
433
  # Complete batch
@@ -75,14 +75,18 @@ module Cloudtasker
75
75
  # end
76
76
  #
77
77
  # @param [String] cache_key The cache key to access.
78
+ # @param [Integer] max_wait The number of seconds after which the lock will be cleared anyway.
78
79
  #
79
- def with_lock(cache_key)
80
+ def with_lock(cache_key, max_wait: nil)
80
81
  return nil unless cache_key
81
82
 
83
+ # Set max wait
84
+ max_wait = (max_wait || LOCK_DURATION).to_i
85
+
82
86
  # Wait to acquire lock
83
87
  lock_key = [LOCK_KEY_PREFIX, cache_key].join('/')
84
88
  client.with do |conn|
85
- sleep(LOCK_WAIT_DURATION) until conn.set(lock_key, true, nx: true, ex: LOCK_DURATION)
89
+ sleep(LOCK_WAIT_DURATION) until conn.set(lock_key, true, nx: true, ex: max_wait)
86
90
  end
87
91
 
88
92
  # yield content
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cloudtasker
4
- VERSION = '0.12.rc6'
4
+ VERSION = '0.12.rc7'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudtasker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.rc6
4
+ version: 0.12.rc7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arnaud Lachaume