zkruby 3.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,141 @@
1
+ module ZooKeeper
2
+
3
+ class Client
4
+
5
+ # Recursive make path
6
+ #
7
+ # This will send parallel creates for ALL nodes up to the root
8
+ # and then ignore any NODE_EXISTS errors.
9
+ #
10
+ # You generally want to call this after receiving a NO_NODE error from a
11
+ # simple {#create}
12
+ #
13
+ # @param [String] path
14
+ # @param [Data::ACL] acl the access control list to apply to any created nodes
15
+ # @return
16
+ # @raise [Error]
17
+ def mkpath(path,acl=ZK::ACL_OPEN_UNSAFE,&callback)
18
+
19
+ return synchronous_call(:mkpath,path,acl) unless block_given?
20
+
21
+ connection_lost = false
22
+
23
+ path_comp = path.split("/")
24
+
25
+ # we only care about the result of the final create op
26
+ last_op = nil
27
+
28
+ (1-path_comp.length..-1).each do |i|
29
+
30
+ sub_path = path_comp[0..i].join("/")
31
+
32
+ op = create(sub_path,"",acl) { if i == -1 then callback.call() else :true end }
33
+
34
+ op.errback do |err|
35
+ if i == -1
36
+ if ZK::Error::NODE_EXISTS === err
37
+ callback.call()
38
+ elsif ZK::Error::CONNECTION_LOST === err || ( ZK::Error::NO_NODE && connection_lost )
39
+ # try again
40
+ mkpath(path,acl)
41
+ callback.call()
42
+ else
43
+ raise err
44
+ end
45
+ elsif ZK::Error::CONNECTION_LOST === err
46
+ connection_lost = true
47
+ :connection_lost
48
+ else
49
+ # we don't care about any other errors, but we will log them
50
+ logger.warn { "Error creating #{sub_path}, #{err}" }
51
+ end
52
+ end
53
+ last_op = op if (i == -1)
54
+ end
55
+
56
+ return WrappedOp.new(last_op)
57
+ end
58
+
59
+ # Recursive delete
60
+ #
61
+ # Although this method itself can be called synchronously all the zk activity
62
+ # is asynchronous, ie all subnodes are removed in parallel
63
+ #
64
+ # Will retry on connection loss, or if some other activity is adding nodes in parallel.
65
+ # If you get a session expiry in the middle of this you will end up with a
66
+ # partially completed recursive delete. In all other circumstances it will eventually
67
+ # complete.
68
+ #
69
+ # @overload rmpath(path,version)
70
+ # @param [String] path this node and all its children will be recursively deleted
71
+ # @param [FixNum] version the expected version to be deleted (-1 to match any version).
72
+ # Only applies to the top level path
73
+ # @return
74
+ # @raise [Error]
75
+ # @overload rmpath(path,version)
76
+ # @return [AsyncOp]
77
+ # @yield [] callback invoked if delete is successful
78
+ def rmpath(path,version = -1, &callback)
79
+
80
+ return synchronous_call(:rmpath,path,version) unless block_given?
81
+
82
+ del_op = delete(path,version) { callback.call() }
83
+
84
+ del_op.errback do |err|
85
+ # we don't leave this method unless we get an exception
86
+ # or have completed and called the callback
87
+ case err
88
+ when ZK::Error::NO_NODE
89
+ when ZK::Error::CONNECTION_LOST
90
+ rmpath(path,version)
91
+ when ZK::Error::NOT_EMPTY
92
+
93
+ stat, child_list = children(path)
94
+
95
+ unless child_list.empty?
96
+ child_ops = {}
97
+ child_list.each do |child|
98
+ child_path = "#{path}/#{child}"
99
+
100
+ rm_op = rmpath(child_path,-1) { :success }
101
+
102
+ rm_op.errback { |err| child_results[child_path] = err }
103
+
104
+ child_ops[child_path] = rm_op
105
+ end
106
+
107
+ # Wait until all our children are done (or error'd)
108
+ child_ops.each { |child_path,op| op.value }
109
+ rmpath(path,version)
110
+ end
111
+ else
112
+ raise err
113
+ end
114
+ callback.call()
115
+ end
116
+
117
+ return WrappedOp.new(del_op)
118
+ end
119
+ end #Client
120
+
121
+ class WrappedOp < AsyncOp
122
+ def initialize(delegate_op)
123
+ @delegate_op = delegate_op
124
+ @errback = nil
125
+ end
126
+
127
+ private
128
+ def wait_value()
129
+ begin
130
+ @delegate_op.value()
131
+ rescue StandardError > err
132
+ @errback ? @errback.call(err) : raise
133
+ end
134
+ end
135
+
136
+ def set_error_handler(errback)
137
+ @errback = errback
138
+ end
139
+ end
140
+
141
+ end #ZooKeeper
@@ -0,0 +1,27 @@
1
+ # A pure ruby implementation of the zk client library
2
+ #
3
+ # It implements the client side of the ZooKeeper TCP protocol directly rather
4
+ # than calling the zk client libraries
5
+ #
6
+ module ZooKeeper
7
+ # Major/Minor numbers track zookeeper itself, final digit is our build number
8
+ VERSION = "3.4.3"
9
+ @bindings = []
10
+ def self.add_binding(binding)
11
+ @bindings << binding unless @bindings.include?(binding)
12
+ end
13
+ end
14
+
15
+ # Shorthand
16
+ ZK=ZooKeeper
17
+
18
+ require 'slf4r'
19
+ require 'zkruby/enum'
20
+ require 'zkruby/bindata'
21
+ require 'jute/zookeeper'
22
+ require 'zkruby/multi'
23
+ require 'zkruby/protocol'
24
+ require 'zkruby/session'
25
+ require 'zkruby/client'
26
+ # Utilities
27
+ require 'zkruby/util'
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ class ZKBTest < BinData::Record
4
+ int8 :testint
5
+ zk_boolean :bool
6
+ end
7
+
8
+ describe ZK::ZKBoolean do
9
+
10
+ it "should behave like 'false' after reading 00" do
11
+ hex = "0000"
12
+ bin = [ hex ].pack("H*")
13
+
14
+ b = ZKBTest.read(bin)
15
+
16
+ b.bool.should == false
17
+ # NOTE!! this is unfortunate consequence of Ruby's view of FALSE
18
+ b.bool.should_not be_false
19
+
20
+ b.to_binary_s.unpack("H*")[0].should == hex
21
+ end
22
+
23
+ it "should behave like 'true' after reading 01" do
24
+ hex = "0001"
25
+ bin = [ hex ].pack("H*")
26
+
27
+ b = ZKBTest.read(bin)
28
+
29
+ b.bool.should == true
30
+ # NOTE!! this is unfortunate consequence of Ruby's view of FALSE
31
+ b.bool.should be_true
32
+
33
+ b.to_binary_s.unpack("H*")[0].should == hex
34
+ end
35
+
36
+ it "should behave like a boolean after creating" do
37
+
38
+ b = ZKBTest.new()
39
+ b.bool= true
40
+ b.bool.should == true
41
+
42
+ b.bool= false
43
+ b.bool.should == false
44
+
45
+ b = ZKBTest.new( :bool => true )
46
+ b.bool.should == true
47
+
48
+ b = ZKBTest.new( :bool => false )
49
+ b.bool.should == false
50
+ end
51
+ end
@@ -0,0 +1,84 @@
1
+ require 'zkruby/enum.rb'
2
+
3
+ class TestEnum
4
+ include Enumeration
5
+
6
+ enum :one, 1
7
+ enum :two, -2
8
+ end
9
+
10
+ class TestError < StandardError
11
+ include Enumeration
12
+ enum :badone, 10
13
+ enum :oops,20
14
+ end
15
+
16
+ describe Enumeration do
17
+ it "should equate to itself" do
18
+ TestEnum::ONE.should == TestEnum::ONE
19
+ end
20
+
21
+ it "should have case equals comparisons to symbols and ints" do
22
+ t = TestEnum.fetch(1)
23
+ t.should === :one
24
+ t.should === 1
25
+ end
26
+
27
+ it "should be useable in a mock" do
28
+ m = mock("watchevent")
29
+ m.should_receive(:test).with(TestEnum::TWO)
30
+ m.test(TestEnum::TWO)
31
+ end
32
+
33
+ describe "errors" do
34
+ # Ruby 1.9 inadvertently dropped requirement that the argument
35
+ # to rescue must be a class or module, but despite its usefulness
36
+ # it is not supposed to be that way and JRuby doesn't work that way
37
+ it "should be an exception class" do
38
+ TestError.get(:badone).should equal (TestError::BADONE)
39
+ TestError.get(:badone).class.should == Class
40
+ TestError::BADONE.should < StandardError
41
+ TestError::BADONE.new().should be_kind_of(TestError)
42
+ TestError::OOPS.should === TestError.get(:oops).exception()
43
+ TestError::OOPS.to_sym.should == :oops
44
+ end
45
+
46
+ it "should be raisable with get and rescuable by a constant" do
47
+ begin
48
+ raise TestError.get(:badone), "mymessage"
49
+ rescue TestError::BADONE => ex
50
+ ex.message.should == "mymessage"
51
+ end
52
+ end
53
+
54
+ it "should be raisable with constant and rescuable with fetch" do
55
+ begin
56
+ raise TestError::OOPS, "a message"
57
+ rescue TestError::fetch(20) => ex
58
+ # do nothing
59
+ ex.message.should == "a message"
60
+ end
61
+ end
62
+
63
+ it "should catch the superclass" do
64
+ begin
65
+ raise TestError::BADONE, "a message"
66
+ rescue TestError
67
+ end
68
+ end
69
+
70
+ it "should be raisable with unknown symbol" do
71
+ begin
72
+ raise TestError.lookup(:unknown)
73
+ rescue TestError => err
74
+ end
75
+ end
76
+
77
+ it "should be raisable with unknown int" do
78
+ begin
79
+ raise TestError.lookup(-132)
80
+ rescue TestError => err
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,50 @@
1
+ require 'server_helper'
2
+ require 'shared/binding'
3
+ require 'zkruby/eventmachine'
4
+
5
+ module EMHelper
6
+ alias :restart_cluster_orig :restart_cluster
7
+ def restart_cluster(delay=0)
8
+ if EM.reactor_running?
9
+ cv = Strand::ConditionVariable.new()
10
+ op = Proc.new do
11
+ begin
12
+ restart_cluster_orig(delay)
13
+ rescue Exception => ex
14
+ logger.error ("Exception restarting cluster #{ex}")
15
+ end
16
+ true
17
+ end
18
+ cb = Proc.new { |result| cv.signal() }
19
+ defer = EM.defer(op,cb)
20
+ cv.wait()
21
+ else
22
+ restart_cluster_orig(delay)
23
+ end
24
+ end
25
+
26
+ def sleep(delay)
27
+ Strand.sleep(delay)
28
+ end
29
+ end
30
+
31
+ describe ZooKeeper::EventMachine::Binding do
32
+
33
+ include Slf4r::Logger
34
+ include EMHelper
35
+
36
+ around(:each) do |example|
37
+ EventMachine.run {
38
+ Strand.new() do
39
+ begin
40
+ example.run
41
+ ensure
42
+ EM::stop
43
+ end
44
+ end
45
+ }
46
+ end
47
+
48
+ it_should_behave_like "a zookeeper client binding"
49
+
50
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ def tohex(s)
4
+ s.unpack("H*")[0]
5
+ end
6
+
7
+ describe ZooKeeper::Proto::MultiRequest do
8
+
9
+ it "an empty multi should match the java output" do
10
+
11
+ java_hex = "ffffffff01ffffffff"
12
+ java_bin = [ java_hex ].pack("H*")
13
+
14
+ read_request = ZooKeeper::Proto::MultiRequest.read(java_bin)
15
+
16
+ read_hex = tohex(read_request.to_binary_s)
17
+
18
+ m = ZooKeeper::Proto::MultiRequest.new()
19
+
20
+ m.requests << { :header => { :_type => -1, :done => true, :err => -1 } }
21
+
22
+ m_hex = tohex(m.to_binary_s)
23
+ m_hex.should == java_hex
24
+ m.should == read_request
25
+ end
26
+
27
+ it "should match the java output with records" do
28
+
29
+ java_hex = "0000000d00ffffffff00000005636865636b000000010000000100ffffffff000000066372656174650000000b6372656174652064617461000000010000001f000000046175746800000000000000000000000200ffffffff0000000664656c657465000000110000000500ffffffff000000077365744461746100000008736574206461746100000013ffffffff01ffffffff"
30
+
31
+ java_bin = [ java_hex ].pack("H*")
32
+
33
+ read_request = ZooKeeper::Proto::MultiRequest.read(java_bin)
34
+
35
+ read_hex = tohex(read_request.to_binary_s)
36
+
37
+ read_hex.should == java_hex
38
+
39
+ m = ZooKeeper::Proto::MultiRequest.new()
40
+
41
+ check = ZK::Proto::CheckVersionRequest.new( :path => "check", :version => 1 )
42
+ m.requests << { :header => { :_type => 13, :done => false, :err => -1 }, :request => check }
43
+
44
+ create = ZK::Proto::CreateRequest.new( :path => "create", :data => "create data", :acl => ZK::CREATOR_ALL_ACL, :flags => 0)
45
+ m.requests << { :header => { :_type => 1, :done => false, :err => -1 }, :request => create }
46
+
47
+ delete = ZK::Proto::DeleteRequest.new( :path => "delete", :version => 17)
48
+ m.requests << { :header => { :_type => 2, :done => false, :err => -1 }, :request => delete }
49
+
50
+
51
+ set = ZK::Proto::SetDataRequest.new( :path => "setData", :data => "set data", :version => 19)
52
+ m.requests << { :header => { :_type => 5, :done => false, :err => -1 }, :request => set }
53
+
54
+ m.requests << { :header => { :_type => -1, :done => true, :err => -1 } }
55
+
56
+ m_hex = tohex(m.to_binary_s)
57
+ m_hex.should == java_hex
58
+ m.should == read_request
59
+ end
60
+
61
+ it "should decode a response" do
62
+
63
+ response_hex = "000000010000000000000000062f6d756c7469ffffffff01ffffffff"
64
+
65
+ response_bin = [ response_hex ].pack("H*")
66
+
67
+ m = ZooKeeper::Proto::MultiResponse.read(response_bin)
68
+ m.responses.size.should == 2
69
+ m.responses[0].done?.should be_false
70
+ m.responses[0].header.err.should == 0
71
+ m.responses[0].header._type.should == 1
72
+ m.responses[0].response.path.should == "/multi"
73
+ m.responses[1].done?.should be_true
74
+
75
+ end
76
+
77
+ it "should decode an error response" do
78
+ response_hex = "ffffffff000000000000000000ffffffff00ffffff9bffffff9bffffffff01ffffffff"
79
+
80
+ response_bin = [ response_hex ].pack("H*")
81
+
82
+ m = ZooKeeper::Proto::MultiResponse.read(response_bin)
83
+ m.responses.size.should == 3
84
+ m.responses[0].header._type.should == -1
85
+ m.responses[0].header.err.should == 0
86
+ m.responses[0].done?.should be_false
87
+ m.responses[0].response.err.should == 0
88
+ m.responses[1].header._type.should == -1
89
+ m.responses[1].header.err.should == -101
90
+ m.responses[1].response.err.should == -101
91
+ m.responses[2].done?.should be_true
92
+ end
93
+ end