Class: SidekiqUniqueJobs::Locksmith

Inherits:
Object
  • Object
show all
Includes:
Connection
Defined in:
lib/sidekiq_unique_jobs/locksmith.rb

Overview

Lock manager class that handles all the various locks

Constant Summary collapse

API_VERSION =

rubocop:disable ClassLength

'1'
EXPIRES_IN =
10

Instance Method Summary collapse

Methods included from Connection

#redis

Constructor Details

#initialize(item, redis_pool = nil) ⇒ Locksmith

Returns a new instance of Locksmith.

Parameters:

  • item (Hash)

    a Sidekiq job hash

  • redis_pool (Sidekiq::RedisConnection, ConnectionPool) (defaults to: nil)

    the redis connection

Options Hash (item):

  • :lock_expiration (Integer)

    the configured expiration

  • :jid (String)

    the sidekiq job id

  • :unique_digest (String)

    the unique digest (See: UniqueArgs#unique_digest)



18
19
20
21
22
23
24
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 18

def initialize(item, redis_pool = nil)
  @concurrency   = 1 # removed in a0cff5bc42edbe7190d6ede7e7f845074d2d7af6
  @expiration    = item[LOCK_EXPIRATION_KEY]
  @jid           = item[JID_KEY]
  @unique_digest = item[UNIQUE_DIGEST_KEY]
  @redis_pool    = redis_pool
end

Instance Method Details

#available_countInteger

The number of available resourced for this lock

Returns:

  • (Integer)

    the number of available resources



55
56
57
58
59
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 55

def available_count
  return concurrency unless exists?

  redis(redis_pool) { |conn| conn.llen(available_key) }
end

#createString

Creates the necessary keys in redis to attempt a lock

Returns:

  • (String)

    the Sidekiq job_id



28
29
30
31
32
33
34
35
36
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 28

def create
  Scripts.call(
    :create,
    redis_pool,
    keys: [exists_key, grabbed_key, available_key, version_key, UNIQUE_SET, unique_digest],
    argv: [jid, expiration, API_VERSION, concurrency],
  )
  expire
end

#deleteObject

Deletes the lock unless it has an expiration set



62
63
64
65
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 62

def delete
  return if expiration
  delete!
end

#delete!Object

Deletes the lock regardless of if it has an expiration set



68
69
70
71
72
73
74
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 68

def delete!
  Scripts.call(
    :delete,
    redis_pool,
    keys: [exists_key, grabbed_key, available_key, version_key, UNIQUE_SET, unique_digest],
  )
end

#exists?true, false

Checks if the exists key is created in redis

Returns:

  • (true, false)


49
50
51
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 49

def exists?
  redis(redis_pool) { |conn| conn.exists(exists_key) }
end

#expireObject



38
39
40
41
42
43
44
45
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 38

def expire
  Scripts.call(
    :expire,
    redis_pool,
    keys: [exists_key, available_key, version_key],
    argv: [expiration],
  )
end

#lock(timeout = nil) { ... } ⇒ Object Also known as: wait

Create a lock for the item

Parameters:

  • timeout (Integer) (defaults to: nil)

    the number of seconds to wait for a lock. nil means wait indefinitely

Yields:

  • the block to execute if a lock is successful

Returns:

  • the Sidekiq job_id (jid)



81
82
83
84
85
86
87
88
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 81

def lock(timeout = nil, &block)
  create

  grab_token(timeout) do |token|
    touch_grabbed_token(token)
    return_token_or_block_value(token, &block)
  end
end

#locked?(token = nil) ⇒ true, false

Checks if this instance is considered locked

Parameters:

  • token (String) (defaults to: nil)

    the unique token to check for a lock. nil will default to the jid provided in the initializer

Returns:

  • (true, false)


103
104
105
106
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 103

def locked?(token = nil)
  token ||= jid
  redis(redis_pool) { |conn| conn.hexists(grabbed_key, token) }
end

#signal(token = nil) ⇒ Integer

Signal that the token should be released

Parameters:

  • token (String) (defaults to: nil)

    the unique token to check for a lock. nil will default to the jid provided in the initializer.

Returns:

  • (Integer)

    the number of available lock resources



112
113
114
115
116
117
118
119
120
121
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 112

def signal(token = nil)
  token ||= jid

  Scripts.call(
    :signal,
    redis_pool,
    keys: [exists_key, grabbed_key, available_key, version_key, UNIQUE_SET, unique_digest],
    argv: [token, expiration],
  )
end

#unlockfalse, String

Removes the lock keys from Redis

Returns:

  • (false)

    unless locked?

  • (String)

    Sidekiq job_id (jid) if successful



94
95
96
97
# File 'lib/sidekiq_unique_jobs/locksmith.rb', line 94

def unlock
  return false unless locked?
  signal(jid)
end