distlock 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/distlock.gemspec CHANGED
@@ -23,5 +23,8 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency "rake", "~> 0.9"
24
24
  s.add_development_dependency "rspec", "~> 2.0"
25
25
 
26
- s.add_runtime_dependency "zookeeper", "~> 0.4"
26
+ # todo remove these
27
+ # s.add_development_dependency "zookeeper", "~> 0.4"
28
+ # s.add_development_dependency "redis"
29
+ # s.add_development_dependency "system_timer"
27
30
  end
data/lib/distlock.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'logger'
2
2
  require 'distlock/version'
3
3
  require 'distlock/lock_error'
4
- require 'distlock/zk/zk'
5
4
 
6
5
  module Distlock
7
6
  def Distlock.logger
@@ -13,8 +12,32 @@ module Distlock
13
12
  end
14
13
 
15
14
  # factory method for creating instances of lock managers
16
- def Distlock.new_instance(lock_type = :zk_exclusive_lock, options={})
17
- locker = Distlock::ZK::ExclusiveLock.new(options)
15
+ def Distlock.new_instance(options={})
16
+
17
+ # attempt to require zookeeper blindly
18
+ # if require fails, fallback to redis
19
+ # if fails fall back to noop
20
+
21
+ locker=nil
22
+
23
+ begin
24
+ require 'distlock/zk/zk'
25
+ locker = Distlock::ZK::ExclusiveLock.new(options)
26
+ rescue LoadError => e
27
+ Distlock.logger.debug "failed to require - #{e}"
28
+
29
+ begin
30
+ require 'distlock/redis/redis'
31
+ locker = Distlock::Redis::ExclusiveLock.new(options)
32
+ rescue LoadError => e
33
+ Distlock.logger.debug "failed to require - #{e}"
34
+
35
+ # noop
36
+ require 'distlock/noop/noop'
37
+ locker = Distlock::Noop::ExclusiveLock.new(options)
38
+ end
39
+ end
40
+
18
41
  locker.logger = Distlock.logger
19
42
  locker
20
43
  end
@@ -0,0 +1,21 @@
1
+ module Distlock
2
+ module Noop
3
+ class ExclusiveLock
4
+
5
+ def initialize(options={})
6
+ end
7
+
8
+ def with_lock(path=nil)
9
+ yield if block_given?
10
+ end
11
+
12
+ def logger
13
+ @logger ||= Logger.new(STDOUT)
14
+ end
15
+
16
+ def logger=(logger)
17
+ @logger = logger
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ require 'distlock/noop/exclusive_lock'
2
+
3
+ module Distlock
4
+ module Noop
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ require 'logger'
2
+
3
+ module Distlock
4
+ module Redis
5
+ module Common
6
+ def redis
7
+ @redis ||= begin
8
+ redis = ::Redis.new
9
+ end
10
+ end
11
+
12
+ def logger
13
+ @logger ||= Logger.new(STDOUT)
14
+ end
15
+
16
+ def logger=(logger)
17
+ @logger = logger
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,82 @@
1
+ module Distlock
2
+ module Redis
3
+
4
+ #
5
+ # see here for retry count based example -
6
+ # https://github.com/PatrickTulskie/redis-lock/blob/master/lib/redis/lock.rb
7
+ #
8
+ class ExclusiveLock
9
+ include Distlock::Redis::Common
10
+
11
+ DEFAULT_LEASE_FOR = 300 # 5 mins
12
+ DEFAULT_RETRY_FOR = 60 # 1 min
13
+ DEFAULT_RETRY_IN = 1 # 1 sec
14
+
15
+ def initialize(options={})
16
+ end
17
+
18
+ def my_lock
19
+ @my_lock
20
+ end
21
+
22
+ def lock(path, lease_for = DEFAULT_LEASE_FOR, retry_for = DEFAULT_RETRY_FOR)
23
+ now = Time.now
24
+ retry_until = now + retry_for
25
+
26
+ @my_lock = path
27
+
28
+ lock_value = generate_lock_value(lease_for)
29
+
30
+ while Time.now < retry_until
31
+ if redis.setnx(path, lock_value)
32
+ logger.debug "acquired lock (setnx) - #{my_lock}, #{lock_value}"
33
+ return true
34
+ end
35
+
36
+ current_lock = redis.get(my_lock)
37
+ if (current_lock.to_s.split('-').first.to_i) < Time.now.to_i
38
+ updated_lock = redis.getset(my_lock, lock_value)
39
+ if updated_lock == current_lock
40
+ logger.debug "acquired lock (getset) - #{my_lock}, #{lock_value}"
41
+ return true
42
+ end
43
+ end
44
+
45
+ sleep DEFAULT_RETRY_IN
46
+ end
47
+
48
+ raise LockError.new("failed to get the lock")
49
+ end
50
+
51
+ def unlock
52
+ lock_value = redis.get(my_lock)
53
+ unless lock_value
54
+ logger.debug "no lock to release"
55
+ return true
56
+ end
57
+
58
+ lease_expires, owner = lock_value.split('-')
59
+ if (lease_expires.to_i > Time.now.to_i) && (owner.to_i == Process.pid)
60
+ redis.del(my_lock)
61
+ logger.debug "released lock - #{my_lock}, #{lock_value}"
62
+ return true
63
+ end
64
+ end
65
+
66
+ def generate_lock_value(lease_for=DEFAULT_LEASE_FOR, id=Process.pid)
67
+ "#{Time.now.to_i + lease_for + 1}-#{id}"
68
+ end
69
+
70
+ def with_lock(path='/distlock/redis/exclusive_lock/default', lease_for=DEFAULT_LEASE_FOR, retry_for=DEFAULT_RETRY_FOR)
71
+ begin
72
+ lock(path, lease_for, retry_for)
73
+ yield if block_given?
74
+ ensure
75
+ # TODO - store lock path so we don't need to pass it here
76
+ # unlock(path)
77
+ unlock
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,8 @@
1
+ require 'redis'
2
+ require 'distlock/redis/common'
3
+ require 'distlock/redis/exclusive_lock'
4
+
5
+ module Distlock
6
+ module Redis
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module Distlock
2
- VERSION = "0.0.9"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -88,10 +88,6 @@ module Distlock
88
88
 
89
89
  # TODO - pass children in as parameter?
90
90
  children = zk.get_children(:path => path)[:children].sort{|a,b|a.split('-').last <=> b.split('-').last}
91
-
92
- puts lock
93
- puts path
94
- puts children.inspect
95
91
 
96
92
  lock_last = lock.split('/').last
97
93
  lock_idx = children.index(lock_last)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: distlock
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 9
10
- version: 0.0.9
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Simon Horne
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-04-13 00:00:00 -04:00
18
+ date: 2012-05-03 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -48,21 +48,6 @@ dependencies:
48
48
  version: "2.0"
49
49
  type: :development
50
50
  version_requirements: *id002
51
- - !ruby/object:Gem::Dependency
52
- name: zookeeper
53
- prerelease: false
54
- requirement: &id003 !ruby/object:Gem::Requirement
55
- none: false
56
- requirements:
57
- - - ~>
58
- - !ruby/object:Gem::Version
59
- hash: 3
60
- segments:
61
- - 0
62
- - 4
63
- version: "0.4"
64
- type: :runtime
65
- version_requirements: *id003
66
51
  description: Distributed Locking
67
52
  email:
68
53
  - simon@soulware.co.uk
@@ -81,6 +66,11 @@ files:
81
66
  - lib/dist_lock.rb
82
67
  - lib/distlock.rb
83
68
  - lib/distlock/lock_error.rb
69
+ - lib/distlock/noop/exclusive_lock.rb
70
+ - lib/distlock/noop/noop.rb
71
+ - lib/distlock/redis/common.rb
72
+ - lib/distlock/redis/exclusive_lock.rb
73
+ - lib/distlock/redis/redis.rb
84
74
  - lib/distlock/version.rb
85
75
  - lib/distlock/zk/common.rb
86
76
  - lib/distlock/zk/exclusive_lock.rb