Module: DynamoidLockable

Defined in:
lib/dynamoid_lockable.rb,
lib/dynamoid_lockable/version.rb

Defined Under Namespace

Modules: ClassMethods Classes: CouldNotAcquireLock, CouldNotUnlock, Error, LockingError

Constant Summary collapse

DEFAULT_LOCK_TIME =
15 * 60
VERSION =
'1.1.0'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(other) ⇒ Object



145
146
147
# File 'lib/dynamoid_lockable.rb', line 145

def self.included(other)
  other.extend(ClassMethods)
end

Instance Method Details

#create_relock_thread(name) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/dynamoid_lockable.rb', line 53

def create_relock_thread(name)
  locker_name = self.class.locking_name

  Thread.new do
    config = self.class.lock_config(name)
    loop do
      lock(name, locker_name: locker_name)

      sleep config[:relock_every]
    end
  end
end

#ensure_lockable_field(name) ⇒ Object



80
81
82
# File 'lib/dynamoid_lockable.rb', line 80

def ensure_lockable_field(name)
  self.class.ensure_lockable_field(name)
end

#lock(name, locker_name: self.class.locking_name) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/dynamoid_lockable.rb', line 17

def lock(name, locker_name: self.class.locking_name)
  ensure_lockable_field(name)

  locked_until = Time.now + self.class.lock_config(name)[:duration]

  result = self.class
    .lockable(name, locker_name: locker_name)
    .upsert(
      hash_key,
      range_value,
      "#{name}_locked_until": locked_until,
      "#{name}_locked_by": locker_name,
    )

  raise CouldNotAcquireLock unless result

  # Prevents having to call reload, which we've seen take 20seconds
  # but still populates the value in memory, in case the code block
  # calls save and persists this copy again
  send("#{name}_locked_until=", locked_until)
  send("#{name}_locked_by=", locker_name)

  true
end

#perform_with_lock(name) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/dynamoid_lockable.rb', line 66

def perform_with_lock(name)
  ensure_lockable_field(name)
  config = self.class.lock_config(name)

  result = lock(name)

  relock_thread = create_relock_thread(name) if config[:relock_every]&.positive?

  yield
ensure
  relock_thread&.exit
  unlock(name) if result # Don't try to unlock if you didn't acquire lock
end

#unlock(name) ⇒ Object

Raises:



42
43
44
45
46
47
48
49
50
51
# File 'lib/dynamoid_lockable.rb', line 42

def unlock(name)
  ensure_lockable_field(name)

  result = self.class.unlockable(name)
    .upsert(hash_key, range_value, "#{name}_locked_until": nil)

  raise CouldNotUnlock unless result

  true
end