distlock 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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