zk 1.5.1 → 1.5.2

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.
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'ZK::Locker::ExclusiveLocker' do
4
+ let(:locker) { ZK::Locker.exclusive_locker(zk, path) }
5
+ let(:locker2) { ZK::Locker.exclusive_locker(zk2, path) }
6
+
7
+ describe :assert! do
8
+ it_should_behave_like 'LockerBase#assert!'
9
+
10
+ it %[should raise LockAssertionFailedError if there is an exclusive lock with a number lower than ours] do
11
+ # this should *really* never happen
12
+
13
+ rlp = locker.root_lock_path
14
+
15
+ zk.mkdir_p(rlp)
16
+
17
+ bogus_path = zk.create("#{rlp}/#{ZK::Locker::EXCLUSIVE_LOCK_PREFIX}", :sequential => true, :ephemeral => true)
18
+
19
+ th = Thread.new do
20
+ locker2.lock(true)
21
+ end
22
+
23
+ logger.debug { "calling wait_until_blocked" }
24
+ locker2.wait_until_blocked(2)
25
+ locker2.should be_waiting
26
+
27
+ wait_until { zk.exists?(locker2.lock_path) }
28
+
29
+ zk.exists?(locker2.lock_path).should be_true
30
+
31
+ zk.delete(bogus_path)
32
+
33
+ th.join(5).should == th
34
+
35
+ locker2.lock_path.should_not == bogus_path
36
+
37
+ zk.create(bogus_path, :ephemeral => true)
38
+
39
+ lambda { locker2.assert! }.should raise_error(ZK::Exceptions::LockAssertionFailedError)
40
+ end
41
+ end
42
+
43
+ describe :acquirable? do
44
+ it %[should work if the lock root doesn't exist] do
45
+ zk.rm_rf(ZK::Locker.default_root_lock_node)
46
+ locker.should be_acquirable
47
+ end
48
+
49
+ it %[should check local state of lockedness] do
50
+ locker.lock.should be_true
51
+ locker.should be_acquirable
52
+ end
53
+
54
+ it %[should check if any participants would prevent us from acquiring the lock] do
55
+ locker.lock.should be_true
56
+ locker2.should_not be_acquirable
57
+ end
58
+ end
59
+
60
+ describe :lock do
61
+ describe 'non-blocking' do
62
+ before do
63
+ @rval = locker.lock
64
+ @rval2 = locker2.lock
65
+ end
66
+
67
+ it %[should acquire the first lock] do
68
+ @rval.should be_true
69
+ end
70
+
71
+ it %[should not acquire the second lock] do
72
+ @rval2.should be_false
73
+ end
74
+
75
+ it %[should acquire the second lock after the first lock is released] do
76
+ locker.unlock.should be_true
77
+ locker2.lock.should be_true
78
+ end
79
+ end
80
+
81
+ describe 'blocking' do
82
+ before do
83
+ zk.mkdir_p(root_lock_path)
84
+ end
85
+
86
+ it %[should block waiting for the lock] do
87
+ ary = []
88
+ read_lock_path = zk.create("/_zklocking/#{path}/read", '', :mode => :ephemeral_sequential)
89
+
90
+ locker.lock.should be_false
91
+
92
+ th = Thread.new do
93
+ locker.lock(true)
94
+ ary << :locked
95
+ end
96
+
97
+ locker.wait_until_blocked(5)
98
+
99
+ ary.should be_empty
100
+ locker.should_not be_locked
101
+
102
+ zk.delete(read_lock_path)
103
+
104
+ th.join(2).should == th
105
+
106
+ ary.length.should == 1
107
+ locker.should be_locked
108
+ end
109
+ end # blocking
110
+ end # lock
111
+ end # ExclusiveLocker
112
+
113
+ describe do
114
+ include_context 'locker non-chrooted'
115
+ it_should_behave_like 'ZK::Locker::ExclusiveLocker'
116
+ end
117
+
118
+ describe :chrooted => true do
119
+ include_context 'locker chrooted'
120
+ it_should_behave_like 'ZK::Locker::ExclusiveLocker'
121
+ end
122
+
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ # this is a remnant of the old Locker class, but a good test of what's expected
4
+ # from ZK::Client#locker
5
+ #
6
+ describe 'ZK::Client#locker' do
7
+ include_context 'connection opts'
8
+
9
+ before(:each) do
10
+ @zk = ZK.new(*connection_args)
11
+ @zk2 = ZK.new(*connection_args)
12
+ @zk3 = ZK.new(*connection_args)
13
+ @connections = [@zk, @zk2, @zk3]
14
+ wait_until { @connections.all? { |c| c.connected? } }
15
+ logger.debug { "all connections connected" }
16
+ @path_to_lock = "/lock_tester"
17
+ end
18
+
19
+ after(:each) do
20
+ @zk.close!
21
+ @zk2.close!
22
+ @zk3.close!
23
+ wait_until { @connections.all? { |c| c.closed? } }
24
+ end
25
+
26
+ it "should be able to acquire the lock if no one else is locking it" do
27
+ @zk.locker(@path_to_lock).lock.should be_true
28
+ end
29
+
30
+ it "should not be able to acquire the lock if someone else is locking it" do
31
+ @zk.locker(@path_to_lock).lock.should be_true
32
+ @zk2.locker(@path_to_lock).lock.should be_false
33
+ end
34
+
35
+ it "should be able to acquire the lock after the first one releases it" do
36
+ lock1 = @zk.locker(@path_to_lock)
37
+ lock2 = @zk2.locker(@path_to_lock)
38
+
39
+ lock1.lock.should be_true
40
+ lock2.lock.should be_false
41
+ lock1.unlock
42
+ lock2.lock.should be_true
43
+ end
44
+
45
+ it "should be able to acquire the lock if the first locker goes away" do
46
+ lock1 = @zk.locker(@path_to_lock)
47
+ lock2 = @zk2.locker(@path_to_lock)
48
+
49
+ lock1.lock.should be_true
50
+ lock2.lock.should be_false
51
+ @zk.close!
52
+ lock2.lock.should be_true
53
+ end
54
+
55
+ it "should be able to handle multi part path locks" do
56
+ @zk.locker("my/multi/part/path").lock.should be_true
57
+ end
58
+
59
+ it "should blocking lock" do
60
+ array = []
61
+ first_lock = @zk.locker("mylock")
62
+ first_lock.lock.should be_true
63
+ array << :first_lock
64
+
65
+ thread = Thread.new do
66
+ @zk.locker("mylock").with_lock do
67
+ array << :second_lock
68
+ end
69
+ array.length.should == 2
70
+ end
71
+
72
+ array.length.should == 1
73
+ first_lock.unlock
74
+ thread.join(10)
75
+ array.length.should == 2
76
+ end
77
+ end
78
+
79
+
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for :shared_exclusive_integration do
4
+ before do
5
+ @sh_lock = ZK::Locker.shared_locker(zk, path)
6
+ @ex_lock = ZK::Locker.exclusive_locker(zk2, path)
7
+ end
8
+
9
+ describe 'shared lock acquired first' do
10
+ it %[should block exclusive locks from acquiring until released] do
11
+ @sh_lock.lock.should be_true
12
+ @ex_lock.lock.should be_false
13
+
14
+ mutex = Monitor.new
15
+ cond = mutex.new_cond
16
+
17
+ th = Thread.new do
18
+ logger.debug { "@ex_lock trying to acquire acquire lock" }
19
+ @ex_lock.with_lock do
20
+ th[:got_lock] = @ex_lock.locked?
21
+ logger.debug { "@ex_lock.locked? #{@ex_lock.locked?}" }
22
+
23
+ mutex.synchronize do
24
+ cond.broadcast
25
+ end
26
+ end
27
+ end
28
+
29
+ mutex.synchronize do
30
+ logger.debug { "unlocking the shared lock" }
31
+ @sh_lock.unlock.should be_true
32
+ cond.wait_until { th[:got_lock] } # make sure they actually received the lock (avoid race)
33
+ th[:got_lock].should be_true
34
+ logger.debug { "ok, they got the lock" }
35
+ end
36
+
37
+ th.join(5).should == th
38
+
39
+ logger.debug { "thread joined, exclusive lock should be releasd" }
40
+
41
+ @ex_lock.should_not be_locked
42
+ end
43
+ end
44
+
45
+ describe 'exclusive lock acquired first' do
46
+ it %[should block shared lock from acquiring until released] do
47
+ @ex_lock.lock.should be_true
48
+ @sh_lock.lock.should be_false
49
+
50
+ mutex = Monitor.new
51
+ cond = mutex.new_cond
52
+
53
+ th = Thread.new do
54
+ logger.debug { "@ex_lock trying to acquire acquire lock" }
55
+ @sh_lock.with_lock do
56
+ th[:got_lock] = @sh_lock.locked?
57
+ logger.debug { "@sh_lock.locked? #{@sh_lock.locked?}" }
58
+
59
+ mutex.synchronize do
60
+ cond.broadcast
61
+ end
62
+ end
63
+ end
64
+
65
+ mutex.synchronize do
66
+ logger.debug { "unlocking the shared lock" }
67
+ @ex_lock.unlock.should be_true
68
+ cond.wait_until { th[:got_lock] } # make sure they actually received the lock (avoid race)
69
+ th[:got_lock].should be_true
70
+ logger.debug { "ok, they got the lock" }
71
+ end
72
+
73
+ th.join(5).should == th
74
+
75
+ logger.debug { "thread joined, exclusive lock should be releasd" }
76
+
77
+ @sh_lock.should_not be_locked
78
+ end
79
+ end
80
+
81
+ describe 'shared-exclusive-shared' do
82
+ before do
83
+ zk3.should_not be_nil
84
+ @sh_lock2 = ZK::Locker.shared_locker(zk3, path)
85
+ end
86
+
87
+ it %[should act something like a queue] do
88
+ @array = []
89
+
90
+ @sh_lock.lock.should be_true
91
+ @sh_lock.should be_locked
92
+
93
+ ex_th = Thread.new do
94
+ begin
95
+ @ex_lock.lock(true) # blocking lock
96
+ @ex_lock.assert!
97
+ @array << :ex_lock
98
+ ensure
99
+ @ex_lock.unlock
100
+ end
101
+ end
102
+
103
+ logger.debug { "about to wait for @ex_lock to be blocked" }
104
+
105
+ @ex_lock.wait_until_blocked(5)
106
+ @ex_lock.should be_waiting
107
+
108
+ logger.debug { "@ex_lock is waiting" }
109
+
110
+ @ex_lock.should_not be_locked
111
+
112
+ # this is the important one, does the second shared lock get blocked by
113
+ # the exclusive lock
114
+ @sh_lock2.lock.should_not be_true
115
+
116
+ sh2_th = Thread.new do
117
+ begin
118
+ @sh_lock2.lock(true)
119
+ @sh_lock2.assert!
120
+ @array << :sh_lock2
121
+ ensure
122
+ @sh_lock2.unlock
123
+ end
124
+ end
125
+
126
+ logger.debug { "about to wait for @sh_lock2 to be blocked" }
127
+
128
+ @sh_lock2.wait_until_blocked(5)
129
+ @sh_lock2.should be_waiting
130
+
131
+ logger.debug { "@sh_lock2 is waiting" }
132
+
133
+ # ok, now unlock the first in the chain
134
+ @sh_lock.assert!
135
+ @sh_lock.unlock.should be_true
136
+
137
+ ex_th.join(5).should == ex_th
138
+ sh2_th.join(5).should == sh2_th
139
+
140
+ @array.length.should == 2
141
+ @array.should == [:ex_lock, :sh_lock2]
142
+ end
143
+ end
144
+ end # shared_exclusive_integration
145
+
146
+ describe do
147
+ include_context 'locker non-chrooted'
148
+
149
+ it_should_behave_like :shared_exclusive_integration
150
+ end
151
+
152
+ describe :chrooted => true do
153
+ include_context 'locker chrooted'
154
+
155
+ it_should_behave_like :shared_exclusive_integration
156
+ end
157
+
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'ZK::Locker::SharedLocker' do
4
+ let(:locker) { ZK::Locker::SharedLocker.new(zk, path) }
5
+ let(:locker2) { ZK::Locker::SharedLocker.new(zk2, path) }
6
+
7
+ describe :assert! do
8
+ it_should_behave_like 'LockerBase#assert!'
9
+
10
+ it %[should raise LockAssertionFailedError if there is an exclusive lock with a number lower than ours] do
11
+ # this should *really* never happen
12
+ locker.lock.should be_true
13
+ shl_path = locker.lock_path
14
+
15
+ locker2.lock.should be_true
16
+
17
+ locker.unlock.should be_true
18
+ locker.should_not be_locked
19
+
20
+ zk.exists?(shl_path).should be_false
21
+
22
+ locker2.lock_path.should_not == shl_path
23
+
24
+ # convert the first shared lock path into a exclusive one
25
+
26
+ exl_path = shl_path.sub(%r%/sh(\d+)\Z%, '/ex\1')
27
+
28
+ zk.create(exl_path, :ephemeral => true)
29
+
30
+ lambda { locker2.assert! }.should raise_error(ZK::Exceptions::LockAssertionFailedError)
31
+ end
32
+ end
33
+
34
+ describe :acquirable? do
35
+ describe %[with default options] do
36
+ it %[should work if the lock root doesn't exist] do
37
+ zk.rm_rf(ZK::Locker.default_root_lock_node)
38
+ locker.should be_acquirable
39
+ end
40
+
41
+ it %[should check local state of lockedness] do
42
+ locker.lock.should be_true
43
+ locker.should be_acquirable
44
+ end
45
+
46
+ it %[should check if any participants would prevent us from acquiring the lock] do
47
+ ex_lock = ZK::Locker.exclusive_locker(zk, path)
48
+ ex_lock.lock.should be_true
49
+ locker.should_not be_acquirable
50
+ end
51
+ end
52
+ end
53
+
54
+ describe :lock do
55
+ describe 'non-blocking success' do
56
+ before do
57
+ @rval = locker.lock
58
+ @rval2 = locker2.lock
59
+ end
60
+
61
+ it %[should acquire the first lock] do
62
+ @rval.should be_true
63
+ locker.should be_locked
64
+ end
65
+
66
+ it %[should acquire the second lock] do
67
+ @rval2.should be_true
68
+ locker2.should be_locked
69
+ end
70
+ end
71
+
72
+ describe 'non-blocking failure' do
73
+ before do
74
+ zk.mkdir_p(root_lock_path)
75
+ @write_lock_path = zk.create("#{root_lock_path}/#{ZK::Locker::EXCLUSIVE_LOCK_PREFIX}", '', :mode => :ephemeral_sequential)
76
+ @rval = locker.lock
77
+ end
78
+
79
+ it %[should return false] do
80
+ @rval.should be_false
81
+ end
82
+
83
+ it %[should not be locked] do
84
+ locker.should_not be_locked
85
+ end
86
+ end
87
+
88
+ describe 'blocking success' do
89
+ before do
90
+ zk.mkdir_p(root_lock_path)
91
+ @write_lock_path = zk.create("#{root_lock_path}/#{ZK::Locker::EXCLUSIVE_LOCK_PREFIX}", '', :mode => :ephemeral_sequential)
92
+ $stderr.sync = true
93
+ end
94
+
95
+ it %[should acquire the lock after the write lock is released] do
96
+ ary = []
97
+
98
+ locker.lock.should be_false
99
+
100
+ th = Thread.new do
101
+ locker.lock(true)
102
+ ary << :locked
103
+ end
104
+
105
+ locker.wait_until_blocked(5)
106
+ locker.should be_waiting
107
+ locker.should_not be_locked
108
+ ary.should be_empty
109
+
110
+ zk.delete(@write_lock_path)
111
+
112
+ th.join(2).should == th
113
+
114
+ ary.should_not be_empty
115
+ ary.length.should == 1
116
+
117
+ locker.should be_locked
118
+ end
119
+ end
120
+ end # lock
121
+
122
+ it_should_behave_like 'LockerBase#unlock'
123
+ end # SharedLocker
124
+
125
+
126
+ describe do
127
+ include_context 'locker non-chrooted'
128
+
129
+ it_should_behave_like 'ZK::Locker::SharedLocker'
130
+ end
131
+
132
+ describe :chrooted => true do
133
+ include_context 'locker chrooted'
134
+
135
+ it_should_behave_like 'ZK::Locker::SharedLocker'
136
+ end
137
+