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.1"

Instance Method Summary collapse

Constructor Details

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

Constructor

Parameters:

  • domain_name (String)

    SimpleDB domain name

  • options (Hash) (defaults to: {})


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, additional_attributes = []) ⇒ Object

lock resource_name It blocks until lock is succeeded.

Parameters:

  • resource_name (String)
  • additional_attributes (Array) (defaults to: [])

    include additional attributes



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

def lock(resource_name, additional_attributes = [])
  wait_secs = 0.5
  while true
    lock = try_lock(resource_name, additional_attributes)
    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

Parameters:

  • age_in_seconds (Fixnum) (defaults to: nil)

    select resources older than this seconds



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

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

Returns:

  • (Time)

    locked time, nil if it is not locked



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

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, additional_attributes = []) ⇒ TrueClass

Try to lock resource_name

Parameters:

  • resource_name (String)

    name to lock

  • additional_attributes (Array) (defaults to: [])

    include additional attributes

Returns:

  • (TrueClass)

    true when locked, unless false



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
77
# File 'lib/sdb_lock.rb', line 50

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

  @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

Parameters:

  • resource_name (String)

    name to unlock



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

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.

Parameters:

  • age_in_seconds (Fixnum)

    select resources older than this seconds

Returns:

  • (Array<String>)

    unlocked resource names



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

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