Module: SidekiqUniqueJobsJidLookup

Defined in:
app/services/sidekiq_unique_jobs_jid_lookup.rb

Overview

Resolves Sidekiq JIDs that currently hold a sidekiq-unique-jobs lock for the same
worker + args. Used when +perform_async+ returns +nil+ (reject-on-conflict) so
the user can be redirected to the in-flight job instead of a generic error.

See +config/initializers/sidekiq.rb+ global +until_and_while_executing+ /
+on_conflict: :reject+ — the client does not receive the "other" jid on rejection.

Class Method Summary collapse

Class Method Details

.active_jid_for_args(worker_class, *perform_args) ⇒ Object



10
11
12
13
14
15
16
17
18
# File 'app/services/sidekiq_unique_jobs_jid_lookup.rb', line 10

def self.active_jid_for_args(worker_class, *perform_args)
  item = prepared_item(worker_class, perform_args)
  digest = effective_lock_digest(worker_class, item)
  lock = SidekiqUniqueJobs::Lock.new(digest)
  pick_active_jid(lock.all_jids)
rescue StandardError => e
  Rails.logger.warn("[SidekiqUniqueJobsJidLookup] #{e.class}: #{e.message}")
  nil
end

.active_sidekiq_status?(jid) ⇒ Boolean

Returns:

  • (Boolean)


55
56
57
# File 'app/services/sidekiq_unique_jobs_jid_lookup.rb', line 55

def self.active_sidekiq_status?(jid)
  Sidekiq::Status.status(jid).in?(%i[queued working])
end

.deep_stringify_array(args) ⇒ Object



59
60
61
# File 'app/services/sidekiq_unique_jobs_jid_lookup.rb', line 59

def self.deep_stringify_array(args)
  args.map { |arg| deep_stringify_keys(arg) }
end

.deep_stringify_keys(obj) ⇒ Object



63
64
65
66
67
68
69
70
71
72
# File 'app/services/sidekiq_unique_jobs_jid_lookup.rb', line 63

def self.deep_stringify_keys(obj)
  case obj
  when Hash
    obj.transform_keys(&:to_s).transform_values { |v| deep_stringify_keys(v) }
  when Array
    obj.map { |v| deep_stringify_keys(v) }
  else
    obj
  end
end

.effective_lock_digest(worker_class, prepared_item) ⇒ Object

WhileExecuting mutates the digest with a :RUN suffix before keys are written (see
SidekiqUniqueJobs::Lock::WhileExecuting#append_unique_key_suffix).



34
35
36
37
38
39
40
41
42
# File 'app/services/sidekiq_unique_jobs_jid_lookup.rb', line 34

def self.effective_lock_digest(worker_class, prepared_item)
  digest = prepared_item[SidekiqUniqueJobs::LOCK_DIGEST]
  lock = resolved_lock_type(worker_class)
  if lock.to_s == 'while_executing'
    suffix = SidekiqUniqueJobs::Lock::WhileExecuting::RUN_SUFFIX
    digest = "#{digest}#{suffix}" unless digest.end_with?(suffix)
  end
  digest
end

.pick_active_jid(jids) ⇒ Object



49
50
51
52
53
# File 'app/services/sidekiq_unique_jobs_jid_lookup.rb', line 49

def self.pick_active_jid(jids)
  return nil if jids.blank?

  jids.find { |jid| jid.present? && active_sidekiq_status?(jid) }
end

.prepared_item(worker_class, perform_args) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
# File 'app/services/sidekiq_unique_jobs_jid_lookup.rb', line 20

def self.prepared_item(worker_class, perform_args)
  opts = worker_class.get_sidekiq_options.stringify_keys
  queue = (opts['queue'] || 'default').to_s
  item = {
    'class' => worker_class.name,
    'queue' => queue,
    'args' => deep_stringify_array(perform_args)
  }
  SidekiqUniqueJobs::Job.prepare(item)
  item
end

.resolved_lock_type(worker_class) ⇒ Object



44
45
46
47
# File 'app/services/sidekiq_unique_jobs_jid_lookup.rb', line 44

def self.resolved_lock_type(worker_class)
  worker_class.get_sidekiq_options.symbolize_keys[:lock] ||
    Sidekiq.default_job_options.symbolize_keys[:lock]
end