canvas_sync 0.22.0.beta7 → 0.22.0

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: 460a37fe879ff078150579638df606c33dfd35870b69955c42dc7bee7afb4833
4
- data.tar.gz: e02f9568f6d80f0f0bf4ab6ff21800ed8bcd36d5cd1803f03cf6a186b3fda690
3
+ metadata.gz: b659edf35a8c6f427f847142d4627920cc19effabfe7fb5ad2ccb960884cf47c
4
+ data.tar.gz: 3264f9b298a5fcbf251a6ccd927ce20133598ed1b9fdd8adf3a728ef90a4e080
5
5
  SHA512:
6
- metadata.gz: 1a08031744764b9f14a8da562ffee5ca83a85e4efa9b5a5b9725605cce53853b90d3dfd9b01bb5ec5ac5aeb248bc6be88fe07b1de9b5b2e5b0b6c4d25d8cd942
7
- data.tar.gz: a71f36915f50bbb74dc746ac2f9c7a8f3489995f8dc67648a58ceb20e85cd71c17072b7b967e415c1be4d3f349c378afcc7b1ed132e2682711cca1c98958d7bc
6
+ metadata.gz: a9c7add460f50301b9f404880e9f6ac2fc57996f1e4c8928f2c52b9bfa5f4973f6cc3c531729763a40de6cce127b86aed70b88ea11fc2119e9fe1856f81a4996
7
+ data.tar.gz: 7d5951ebd7f02917344ea94c23d4f7e5d467d7d803be2944ab1d073aa207acca55a065aa8e2b9145f129154c1ed10b9fb2d14b0692c1e1d8ec21a99f042dab8a
@@ -45,7 +45,7 @@ module CanvasSync::JobBatches
45
45
 
46
46
  def call(_worker, msg, _queue, _redis_pool = nil)
47
47
  if (batch = Thread.current[CURRENT_BATCH_THREAD_KEY]) && should_handle_batch?(msg)
48
- batch.increment_job_queue(msg['jid']) if (msg[:bid] = batch.bid)
48
+ batch.increment_job_queue(msg['jid']) if (msg['bid'] = batch.bid)
49
49
  end
50
50
  yield
51
51
  end
@@ -41,7 +41,7 @@ module CanvasSync::JobUniqueness
41
41
 
42
42
  def deserialize(data)
43
43
  super
44
- @uniqueness_cache_data = data['uniqueness_cache_data'].symbolize_keys
44
+ @uniqueness_cache_data = data['uniqueness_cache_data']&.symbolize_keys
45
45
  end
46
46
 
47
47
  def uniqueness_lock_context
@@ -19,7 +19,7 @@ module CanvasSync::JobUniqueness
19
19
 
20
20
  class SidekiqLockContext < LockContext
21
21
  def job_scheduled_at
22
- @job_instance["at"]
22
+ @job_instance&.[]("at")
23
23
  end
24
24
 
25
25
  def reenqueue(schedule_in:)
@@ -38,7 +38,7 @@ module CanvasSync::JobUniqueness
38
38
  queue: msg['queue'],
39
39
  args: msg['args'],
40
40
  # kwargs: msg['kwargs'],
41
- **(msg['uniqueness_cache_data'] || {}),
41
+ **(msg['uniqueness_cache_data']&.symbolize_keys || {}),
42
42
  }, job_instance: msg)
43
43
  end
44
44
 
@@ -58,7 +58,7 @@ module CanvasSync::JobUniqueness
58
58
  def call(_worker, msg, _queue, _redis_pool = nil, &blk)
59
59
  ctx = lock_context(msg)
60
60
  return blk.call unless ctx
61
- msg['uniqueness_cache_data'] = ctx.cache_data
61
+ msg['uniqueness_cache_data'] = ctx.cache_data.stringify_keys
62
62
  ctx.handle_lifecycle!(:enqueue, &blk)
63
63
  end
64
64
  end
@@ -6,18 +6,23 @@ module CanvasSync::JobUniqueness
6
6
  context_class.new(data, **kwargs)
7
7
  end
8
8
 
9
+ attr_reader :lock_id
10
+
9
11
  # { job_clazz, jid, queue, args?, kwargs?, base_key? }
10
12
  def initialize(data, job_instance: nil, config: nil)
11
13
  @base_key = data[:base_key]
12
14
  @context_data = data
13
15
  @job_instance = job_instance
14
16
  @config = config || @context_data[:config]
17
+
18
+ # TODO Consider (somewhere) updating the lock_id to the BID of the wrapping Batch (when applicable)
19
+ @lock_id ||= data[:lid] || Thread.current[:unique_jobs_previous_context]&.lock_id || job_id
15
20
  end
16
21
 
17
22
  # This is primarily for rehydrating in a Batch Callback, so it is unlikely that args and kwargs are needed.
18
- # Honestly, base_key and job_clazz are probably the only needed values
19
23
  def serialize
20
24
  {
25
+ lid: lock_id,
21
26
  clazz: self.class.to_s,
22
27
  job_clazz: @context_data[:job_clazz].to_s,
23
28
  jid: @context_data[:jid],
@@ -29,6 +34,7 @@ module CanvasSync::JobUniqueness
29
34
  # Properties to cache on the serialized Job object to prevent issues arising from code changes between enqueue and perform
30
35
  def cache_data
31
36
  {
37
+ lid: lock_id,
32
38
  base_key: base_key,
33
39
  job_score: job_score,
34
40
  # TODO Should config also be cached on the Job at time of enqueue?
@@ -38,6 +44,7 @@ module CanvasSync::JobUniqueness
38
44
 
39
45
  def debug_data
40
46
  {
47
+ lid: lock_id,
41
48
  context_class: self.class.to_s,
42
49
  job_class: job_class.to_s,
43
50
  queue: job_queue,
@@ -146,7 +153,6 @@ module CanvasSync::JobUniqueness
146
153
  # @yieldreturn [void] the yield is irrelevant, it only provides a mechanism in
147
154
  # one specific situation to yield back to the middleware.
148
155
  def call_conflict_strategy(origin)
149
- new_job_id = nil
150
156
  strategy = conflict_strategy_for(origin)
151
157
 
152
158
  strategy.call
@@ -14,7 +14,7 @@ module CanvasSync::JobUniqueness
14
14
 
15
15
  def initialize(key, lock_context, redis_pool = nil)
16
16
  @lock_context = lock_context
17
- @job_id = lock_context.job_id
17
+ @job_id = lock_context.lock_id # Yes, .lock_id is intentional
18
18
  @item = lock_context
19
19
  @key = SidekiqUniqueJobs::Key.new(key)
20
20
 
@@ -28,7 +28,7 @@ module CanvasSync::JobUniqueness
28
28
  :"limit" => lcfg[:limit],
29
29
  })
30
30
 
31
- @redis_pool = redis_pool
31
+ @redis_pool = redis_pool
32
32
  end
33
33
 
34
34
  def locked_jids
@@ -4,12 +4,12 @@ module CanvasSync::JobUniqueness
4
4
  valid_for :perform
5
5
 
6
6
  def call
7
- Thread.current[:unique_jobs_previous_jid] = lock_context.job_id
7
+ Thread.current[:unique_jobs_previous_context] = lock_context
8
8
  rescheduled = lock_context.reenqueue(
9
9
  schedule_in: schedule_in,
10
10
  )
11
11
  ensure
12
- Thread.current[:unique_jobs_previous_jid] = nil
12
+ Thread.current[:unique_jobs_previous_context] = nil
13
13
  end
14
14
 
15
15
  def schedule_in
@@ -43,7 +43,7 @@ module CanvasSync::JobUniqueness
43
43
  end
44
44
 
45
45
  def wrap_in_batch(&blk)
46
- if Thread.current[:unique_jobs_previous_jid] # Ensure we don't re-wrap in a batch when rescheduling
46
+ if Thread.current[:unique_jobs_previous_context] # Ensure we don't re-wrap in a batch when rescheduling
47
47
  return blk.call
48
48
  end
49
49
 
@@ -62,7 +62,7 @@ module CanvasSync::JobUniqueness
62
62
  })
63
63
  end
64
64
 
65
- CanvasSync::JobUniqueness.logger.debug("Wrapped job in Locking Batch #{batch.bid} for #{key}")
65
+ CanvasSync::JobUniqueness.logger.debug("Wrapped job (#{lock_context.lock_id}) in Locking Batch #{batch.bid} for #{key}")
66
66
 
67
67
  batch.jobs do
68
68
  return blk.call
@@ -74,7 +74,7 @@ module CanvasSync::JobUniqueness
74
74
  CanvasSync::JobUniqueness.logger.debug("Context data: #{opts[:lock_context]}")
75
75
  strategy_class = opts[:lock_strategy].constantize
76
76
  lock_context = LockContext.from_serialized(opts[:lock_context])
77
- CanvasSync::JobUniqueness.logger.debug("Rehydrated LockContext: #{lock_context.job_id} #{lock_context.debug_data}")
77
+ CanvasSync::JobUniqueness.logger.debug("Rehydrated LockContext: #{lock_context.lock_id} #{lock_context.debug_data}")
78
78
  strategy = strategy_class.new(lock_context)
79
79
  # TODO Should this route through LockContext#handle_lifecycle!?
80
80
  strategy.batch_callback(opts[:event].to_sym, batch_status)
@@ -83,13 +83,9 @@ module CanvasSync::JobUniqueness
83
83
  def lock!(purpose, wait: nil)
84
84
  locked = nil
85
85
  if purpose == :enqueue
86
- if Thread.current[:unique_jobs_previous_jid].present?
87
- locked = locksmith.swap_locks(Thread.current[:unique_jobs_previous_jid])
88
- else
89
- locked = locksmith.lock()
90
- end
86
+ locked = locksmith.lock()
91
87
  elsif purpose == :perform
92
- locked = locksmith.execute { lock_context.job_id }
88
+ locked = locksmith.execute { lock_context.lock_id }
93
89
  end
94
90
 
95
91
  CanvasSync::JobUniqueness.logger.debug { "Requested lock of #{key} for #{purpose} - (#{locked || 'Not Obtained!'})" }
@@ -98,7 +94,7 @@ module CanvasSync::JobUniqueness
98
94
  end
99
95
 
100
96
  def unlock()
101
- CanvasSync::JobUniqueness.logger.debug { "Trying to unlock #{key} for JID #{lock_context.job_id}" }
97
+ CanvasSync::JobUniqueness.logger.debug { "Trying to unlock #{key} for LID #{lock_context.lock_id}" }
102
98
  result = locksmith.unlock
103
99
  CanvasSync::JobUniqueness.logger.debug { "Unlocked #{key} - (#{result || 'Not Unlocked!'})" }
104
100
  end
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.22.0.beta7".freeze
2
+ VERSION = "0.22.0".freeze
3
3
  end
@@ -74,7 +74,7 @@ RSpec.describe CanvasSync::JobBatches::Compat::Sidekiq do
74
74
  it 'assigns bid to msg' do
75
75
  msg = { 'jid' => jid }
76
76
  subject.call(nil, msg, nil) {}
77
- expect(msg[:bid]).to eq(bid)
77
+ expect(msg['bid']).to eq(bid)
78
78
  end
79
79
  end
80
80
  end
@@ -6,7 +6,7 @@ RSpec.describe CanvasSync::JobUniqueness::OnConflict::Reschedule do
6
6
 
7
7
  it "calls reenqueue" do
8
8
  expect(lock_context).to receive(:reenqueue) do |*args, **kwargs|
9
- expect(Thread.current[:unique_jobs_previous_jid]).to be_present
9
+ expect(Thread.current[:unique_jobs_previous_context]).to be_present
10
10
  end
11
11
  on_conflict.call
12
12
  end
@@ -21,4 +21,43 @@ RSpec.describe CanvasSync::JobUniqueness::OnConflict::Reschedule do
21
21
  lock_context2.handle_lifecycle!(:enqueue) {}
22
22
  lock_context2.handle_lifecycle!(:perform) {}
23
23
  end
24
+
25
+ it "maintains a consistent lock_id" do
26
+ TestWorker.clear
27
+ TestWorker.unique_job_options.merge!({ strategy: :while_executing, on_conflict: :reschedule })
28
+
29
+ lock_context.handle_lifecycle!(:enqueue) {}
30
+ lock_context.handle_lifecycle!(:perform) {}
31
+
32
+ TestWorker.perform_async
33
+ j1 = TestWorker.jobs[0]
34
+ TestWorker.perform_one
35
+ j2 = TestWorker.jobs[0]
36
+
37
+ expect(j1).not_to eq(j2)
38
+ expect(j1['uniqueness_cache_data']['lid']).to be_present
39
+ expect(j1['uniqueness_cache_data']['lid']).to eq(j2['uniqueness_cache_data']['lid'])
40
+ end
41
+
42
+ it "unlocks after rescheduling" do
43
+ TestWorker.clear
44
+ TestWorker.unique_job_options.merge!({ strategy: :while_executing, on_conflict: :reschedule })
45
+
46
+ lock_context.handle_lifecycle!(:enqueue) {}
47
+ lock_context.handle_lifecycle!(:perform) {}
48
+
49
+ TestWorker.perform_async
50
+ expect(TestWorker.jobs.size).to eq(1)
51
+ TestWorker.perform_one
52
+ expect(TestWorker.jobs.size).to eq(1)
53
+
54
+ lock_context.lock_strategy.send(:unlock)
55
+ Sidekiq::Worker.drain_all
56
+ expect(TestWorker.jobs.size).to eq(0)
57
+
58
+ # Run another worker to validate that the lock was released
59
+ TestWorker.perform_async
60
+ TestWorker.perform_one
61
+ expect(TestWorker.jobs.size).to eq(0)
62
+ end
24
63
  end
@@ -1,10 +1,13 @@
1
1
  require_relative "../spec_helper"
2
2
  require "canvas_sync/job_uniqueness/job_uniqueness"
3
3
 
4
+ require 'redis'
5
+ Redis.silence_deprecations = true
6
+
4
7
  Dir[File.join(File.dirname(__FILE__), "./support/**/*.rb")].sort.each { |f| require f }
5
8
 
6
9
  Sidekiq::Testing.server_middleware do |chain|
7
- chain.insert_before CanvasSync::JobBatches::Compat::Sidekiq::ServerMiddleware, CanvasSync::JobUniqueness::Compat::Sidekiq::ServerMiddleware
10
+ chain.insert_after CanvasSync::JobBatches::Compat::Sidekiq::ServerMiddleware, CanvasSync::JobUniqueness::Compat::Sidekiq::ServerMiddleware
8
11
  end
9
12
 
10
13
  RSpec.configure do |config|
@@ -9,11 +9,11 @@ RSpec.describe CanvasSync::JobUniqueness::Strategy::Base do
9
9
 
10
10
  describe '#wrap_in_batch' do
11
11
  it 'does not wrap if re-enqueuing' do
12
- Thread.current[:unique_jobs_previous_jid] = "j100"
12
+ Thread.current[:unique_jobs_previous_context] = context
13
13
  expect(CanvasSync::JobBatches::Batch).not_to receive(:new)
14
14
  strategy.send(:wrap_in_batch, &->{ })
15
15
  ensure
16
- Thread.current[:unique_jobs_previous_jid] = nil
16
+ Thread.current[:unique_jobs_previous_context] = nil
17
17
  end
18
18
 
19
19
  context 'when the job fails' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canvas_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.0.beta7
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Instructure CustomDev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-25 00:00:00.000000000 Z
11
+ date: 2024-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -870,9 +870,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
870
870
  version: '0'
871
871
  required_rubygems_version: !ruby/object:Gem::Requirement
872
872
  requirements:
873
- - - ">"
873
+ - - ">="
874
874
  - !ruby/object:Gem::Version
875
- version: 1.3.1
875
+ version: '0'
876
876
  requirements: []
877
877
  rubygems_version: 3.1.6
878
878
  signing_key: