Class: SdbLock

Inherits:
Object
  • Object
show all
Defined in:
lib/sdb_lock.rb,
lib/sdb_lock/version.rb

Overview

Lock using SimpleDB conditional put.

Create instance. lock = SdbLock.new(‘my_app_lock’, access_key_id: YOUR_AWS_ACCESS_KEY, secret_access_key: YOUR_AWS_SECRET)

Or if you set up AWS account in another way. lock = SdbLock.new(‘my_app_lock’)

Try lock, unlock. lock_gained = lock.try_lock(“abc”) lock.unlock(“abc”) if lock_gained

Try lock with block. It unlocks after block execution is finished. executed = lock.try_lock(“abc”) do

# some work

end

Unlock old ones. lock.unlock_old(60) # Unlock all of older than 60 secs

Constant Summary collapse

LOCK_TIME =

Attribute name to be used to save locked time

'lock_time'
MAX_WAIT_SECS =

Max wait secs for #lock

2
VERSION =
"0.1.0"

Instance Method Summary collapse

Constructor Details

#initialize(domain_name, options = {}) ⇒ SdbLock

Constructor



36
37
38
39
40
41
42
43
# File 'lib/sdb_lock.rb', line 36

def initialize(domain_name, options = {})
  @sdb = ::Aws::SimpleDB::Client.new(options)
  @domain_name = domain_name
  unless domains.include? @domain_name
    @sdb.create_domain(domain_name: @domain_name)
    @domains = @sdb.list_domains.domain_names
  end
end

Instance Method Details

#lock(resource_name) ⇒ Object

lock resource_name It blocks until lock is succeeded.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/sdb_lock.rb', line 82

def lock(resource_name)
  wait_secs = 0.5
  while true
    lock = try_lock(resource_name)
    break if lock
    sleep([wait_secs, MAX_WAIT_SECS].min)
    wait_secs *= 2
  end

  if block_given?
    begin
      yield
    ensure
      unlock(resource_name)
    end
  else
    true
  end
end

#locked_resources(age_in_seconds = nil) ⇒ Object

All locked resources



139
140
141
142
143
144
145
146
147
# File 'lib/sdb_lock.rb', line 139

def locked_resources(age_in_seconds = nil)
  if age_in_seconds
    cond = older_than(age_in_seconds)
  else
    cond = "`#{LOCK_TIME}` is not null"
  end
  statement = "SELECT * FROM #{@domain_name} WHERE #{cond}"
  @sdb.select(select_expression: statement).items.map { |i| i.name }
end

#locked_time(resource_name) ⇒ Time

Locked time for resource_name



127
128
129
130
131
132
133
134
# File 'lib/sdb_lock.rb', line 127

def locked_time(resource_name)
  attributes = item(resource_name)
  unless attributes.empty?
    attributes.each do |a|
      break Time.at(a.value.to_i) if a.name == LOCK_TIME
    end
  end
end

#try_lock(resource_name) ⇒ TrueClass

Try to lock resource_name



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/sdb_lock.rb', line 49

def try_lock(resource_name)
  attributes = [
    {
      name: LOCK_TIME,
      value: format_time(Time.now)
    }
  ]

  @sdb.put_attributes(
    domain_name: @domain_name,
    item_name: resource_name,
    attributes: attributes,
    expected: {
      name: LOCK_TIME,
      exists: false
    }
  )
  if block_given?
    begin
      yield
    ensure
      unlock(resource_name)
    end
  end
  true
rescue ::Aws::SimpleDB::Errors::ConditionalCheckFailed
  false
end

#unlock(resource_name, expected_lock_time = nil) ⇒ Object

Unlock resource_name



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/sdb_lock.rb', line 104

def unlock(resource_name, expected_lock_time = nil)
  expected = if expected_lock_time
    {
      name: LOCK_TIME,
      value: expected_lock_time,
      exists: true
    }
  else
    {}
  end

  @sdb.delete_attributes(
    domain_name: @domain_name,
    item_name: resource_name,
    expected: expected
  )
  true
rescue ::Aws::SimpleDB::Errors::ConditionalCheckFailed
  false
end

#unlock_old(age_in_seconds) ⇒ Array<String>

Unlock old resources. It is needed if any program failed to unlock by an unexpected exception or network failure etc.



155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/sdb_lock.rb', line 155

def unlock_old(age_in_seconds)
  targets = locked_resources(age_in_seconds)
  unlocked = []
  targets.each do |resource_name|
    value = item(resource_name).each do |attribute|
      break attribute.value if attribute.name == LOCK_TIME
    end
    next if !value || value > format_time(Time.now - age_in_seconds)
    succ = unlock(resource_name, value)
    unlocked << resource_name if succ
  end
  unlocked
end