zkruby 3.4.3 → 3.4.4.rc3
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/.gitignore +11 -0
- data/.yardopts +2 -0
- data/Gemfile +4 -0
- data/README.rdoc +15 -12
- data/Rakefile +26 -31
- data/jute/lib/hoe/jute.rb +1 -1
- data/jute/lib/jute/task.rb +62 -0
- data/lib/em_zkruby.rb +1 -1
- data/lib/zkruby/async_op.rb +193 -0
- data/lib/zkruby/client.rb +61 -46
- data/lib/zkruby/enum.rb +2 -2
- data/lib/zkruby/eventmachine.rb +54 -131
- data/lib/zkruby/protocol.rb +37 -101
- data/lib/zkruby/rubyio.rb +94 -206
- data/lib/zkruby/session.rb +273 -137
- data/lib/zkruby/util.rb +80 -95
- data/lib/zkruby/version.rb +5 -0
- data/lib/zkruby/zkruby.rb +0 -6
- data/spec/eventmachine_spec.rb +9 -30
- data/spec/server_helper.rb +13 -9
- data/spec/shared/basic.rb +36 -2
- data/spec/shared/binding.rb +2 -0
- data/spec/shared/chroot.rb +30 -0
- data/spec/shared/multi.rb +1 -1
- data/spec/shared/performance.rb +50 -0
- data/spec/shared/watch.rb +2 -2
- data/spec/spec_helper.rb +3 -3
- data/zkruby.gemspec +38 -0
- metadata +109 -112
- data/.gemtest +0 -0
- data/Manifest.txt +0 -39
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -0
data/.gitignore
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= zkruby
|
2
2
|
|
3
|
-
*
|
3
|
+
* http://rubygems.org/gems/zkruby
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
@@ -10,6 +10,9 @@ Pure ruby client for ZooKeeper (http://zookeeper.apache.org)
|
|
10
10
|
|
11
11
|
Supports full ZooKeeper API, synchronous or asynchronous style, watches etc.. with implementations over EventMachine or plain old Ruby IO/Threads
|
12
12
|
|
13
|
+
Other ruby libraries for zookeeper tend to use the underlying C/Java client libraries while zkruby
|
14
|
+
implements the zookeeper wire protocol directly.
|
15
|
+
|
13
16
|
Advantages:
|
14
17
|
* Rubyist API - with block is asynchronous, without block is synchronous
|
15
18
|
* Avoids conflicts between various Ruby threading models and the C/Java apis
|
@@ -18,9 +21,9 @@ Advantages:
|
|
18
21
|
|
19
22
|
Disadvantages:
|
20
23
|
* Duplicated code from Java/C libraries, particularly around herd effect protection
|
21
|
-
*
|
22
|
-
than
|
23
|
-
*
|
24
|
+
* Needs to keep up with changes in wire protocol which are possibly more likely
|
25
|
+
than changes in the client API
|
26
|
+
* Possibly not as optimised in terms of performance (but your client code is ruby anyway)
|
24
27
|
* Not production tested (yet- do you want to be the first?)
|
25
28
|
|
26
29
|
== SYNOPSIS:
|
@@ -77,27 +80,27 @@ Disadvantages:
|
|
77
80
|
|
78
81
|
== DEVELOPERS:
|
79
82
|
|
80
|
-
|
83
|
+
Download ZooKeeper from http://zookeeper.apache.org
|
81
84
|
|
82
|
-
|
85
|
+
Create a conf/zoo.cfg file (copying sample.zoo.cfg is fine)
|
83
86
|
|
84
|
-
|
87
|
+
Checkout the zkruby source into the contrib directory
|
85
88
|
|
86
|
-
|
89
|
+
Copy (if different) src/zookeeper.jute to contrib/zkruby/src/zookeeper.jute
|
87
90
|
|
88
|
-
|
91
|
+
Get gem dependencies
|
89
92
|
|
90
|
-
$
|
93
|
+
$ bundle install
|
91
94
|
|
92
95
|
Generate docs and run the tests/specs
|
93
96
|
|
94
|
-
$ rake
|
97
|
+
$ rake
|
95
98
|
|
96
99
|
== LICENSE:
|
97
100
|
|
98
101
|
(The MIT License)
|
99
102
|
|
100
|
-
Copyright (c)
|
103
|
+
Copyright (c) 2012 Grant Gardner
|
101
104
|
|
102
105
|
Permission is hereby granted, free of charge, to any person obtaining
|
103
106
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -1,39 +1,34 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift File.dirname(__FILE__) + '/jute/lib'
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
$:.unshift "jute/lib"
|
4
3
|
|
5
|
-
require '
|
6
|
-
require '
|
4
|
+
require 'rake/clean'
|
5
|
+
require 'yard'
|
6
|
+
require './yard_ext/enum_handler.rb'
|
7
|
+
require "bundler/gem_tasks"
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
require 'jute/task'
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
warn "%p while trying to load yard extensions: %s" % [ err.class, err.message ]
|
11
|
+
RSpec::Core::RakeTask.new
|
12
|
+
RSpec::Core::RakeTask.new(:perf_spec) do |t|
|
13
|
+
t.rspec_opts = "--tag perf"
|
12
14
|
end
|
13
|
-
|
14
15
|
|
16
|
+
YARD::Rake::YardocTask.new
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
Hoe.plugin :git
|
19
|
-
# Hoe.plugin :inline
|
20
|
-
# Hoe.plugin :racc
|
21
|
-
# Hoe.plugin :rubyforge
|
22
|
-
Hoe.plugin :yard
|
23
|
-
Hoe.plugin :jute
|
24
|
-
|
25
|
-
Hoe.spec 'zkruby' do
|
26
|
-
self.readme_file="README.rdoc"
|
27
|
-
developer('Grant Gardner', 'grant@lastweekend.com.au')
|
28
|
-
dependency 'slf4r' , '~> 0.4.2'
|
29
|
-
dependency 'eventmachine', '~> 0.12.10', :development
|
30
|
-
dependency 'strand', '~> 0.1.0', :development
|
31
|
-
dependency 'logging', '>= 1.4.1', :development
|
32
|
-
dependency 'rspec', '>=2.7.0', :development
|
33
|
-
dependency 'hoe-yard', '>=0.1.2', :development
|
34
|
-
|
35
|
-
self.jute_modules = {
|
18
|
+
Jute::Task.new() do |t|
|
19
|
+
t.modules = {
|
36
20
|
"org.apache.zookeeper.data" => "ZooKeeper::Data",
|
37
21
|
"org.apache.zookeeper.proto" => "ZooKeeper::Proto"}
|
38
22
|
end
|
39
|
-
|
23
|
+
|
24
|
+
task :perf_spec => :jute
|
25
|
+
task :spec => :jute
|
26
|
+
task :build => :jute
|
27
|
+
task :install => :jute
|
28
|
+
task :release => :jute
|
29
|
+
task :yard => :jute
|
30
|
+
|
31
|
+
task :default => [:spec,:yard]
|
32
|
+
|
33
|
+
CLEAN.include "*.out","Gemfile.lock",".yardoc/"
|
34
|
+
CLOBBER.include "doc/","pkg/","lib/jute"
|
data/jute/lib/hoe/jute.rb
CHANGED
@@ -8,8 +8,8 @@ module Hoe::Jute
|
|
8
8
|
#attr_accessor :jute_compiler
|
9
9
|
def initialize_jute
|
10
10
|
self.jute_tasks = [:test,:spec,:package]
|
11
|
-
dependency 'citrus', '~> 2.4.0', :development
|
12
11
|
#dependency 'jute' # if jute is ever a separate gem
|
12
|
+
dependency 'citrus', '~> 2.4.0', :development
|
13
13
|
dependency 'bindata', '~> 1.4.1'
|
14
14
|
end
|
15
15
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'rake/tasklib'
|
3
|
+
require 'jute'
|
4
|
+
|
5
|
+
class Jute::Task < Rake::TaskLib
|
6
|
+
|
7
|
+
attr_accessor :modules
|
8
|
+
attr_accessor :files
|
9
|
+
attr_accessor :pathmap
|
10
|
+
|
11
|
+
def initialize name = :jute
|
12
|
+
|
13
|
+
defaults
|
14
|
+
|
15
|
+
@name = name
|
16
|
+
|
17
|
+
yield self if block_given?
|
18
|
+
|
19
|
+
define_jute_tasks
|
20
|
+
end
|
21
|
+
|
22
|
+
def defaults
|
23
|
+
@files = "src/jute/*.jute"
|
24
|
+
@pathmap = "%{src,lib}X.rb"
|
25
|
+
end
|
26
|
+
|
27
|
+
def define_jute_tasks
|
28
|
+
desc "Compile jute files to ruby classes"
|
29
|
+
task jute_task_name
|
30
|
+
|
31
|
+
raise "modules hash must be defined" unless Hash === @modules
|
32
|
+
FileList.new(@files).each do | source |
|
33
|
+
target = source.pathmap(@pathmap)
|
34
|
+
|
35
|
+
target_dir = target.pathmap("%d")
|
36
|
+
directory target_dir
|
37
|
+
|
38
|
+
file target => [source,target_dir] do
|
39
|
+
compile_jute(source,target)
|
40
|
+
end
|
41
|
+
task jute_task_name => target
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def jute_task_name
|
46
|
+
@name
|
47
|
+
end
|
48
|
+
|
49
|
+
def compile_jute(source,target)
|
50
|
+
|
51
|
+
@jute_compiler = ::Jute::Compiler.new() unless @jute_compiler
|
52
|
+
|
53
|
+
File.open(source) do |input|
|
54
|
+
File.open(target,"w") do |output|
|
55
|
+
puts "Compiling #{input.inspect} to #{output.inspect}"
|
56
|
+
@jute_compiler.compile(input,output,modules)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
data/lib/em_zkruby.rb
CHANGED
@@ -0,0 +1,193 @@
|
|
1
|
+
module ZooKeeper
|
2
|
+
|
3
|
+
# Returned by asynchronous calls
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# op = zk.stat("\apath") { |stat| something_with_stat() }
|
7
|
+
#
|
8
|
+
# op.async_rescue ZK::Error::SESSION_EXPIRED do
|
9
|
+
# puts "Ignoring session expired"
|
10
|
+
# result_when_session_expired()
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# op.async_rescue ZK::Error::CONNECTION_LOST do |ex|
|
14
|
+
# puts "Retrying stat due to connection lost"
|
15
|
+
# op.async_retry()
|
16
|
+
# # the result of this block is ignored!
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# begin
|
20
|
+
# result_of_somthing_with_stat = op.value
|
21
|
+
# rescue StandardError => ex
|
22
|
+
# puts "Oops"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
#
|
26
|
+
class AsyncOp
|
27
|
+
|
28
|
+
# @api private
|
29
|
+
def initialize(event_loop,callback,&operation)
|
30
|
+
@event_loop = event_loop
|
31
|
+
@operation = operation
|
32
|
+
@callback = callback
|
33
|
+
@mutex,@cv = Strand::Mutex.new(), Strand::ConditionVariable.new()
|
34
|
+
begin
|
35
|
+
execute()
|
36
|
+
rescue ZooKeeper::Error => ex
|
37
|
+
# Capture any initial exception
|
38
|
+
# The only expected condition is :session_expired
|
39
|
+
@op_error = ex
|
40
|
+
logger.debug { "Error during initial call #{ex}" }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [Proc,#to_proc] block the error callback as a Proc
|
45
|
+
# @deprecated use {#async_rescue}
|
46
|
+
def errback=(block)
|
47
|
+
async_rescue(&block)
|
48
|
+
end
|
49
|
+
|
50
|
+
#Rescue asynchronous exceptions in a similar manner to normal
|
51
|
+
#ruby. Unfortunately rescue is a reserved word
|
52
|
+
# @param matches [Class,...] subclasses of Exception to match, defaults to {::StandardError}
|
53
|
+
# @param errblock the block to call if an error matches
|
54
|
+
# @return self
|
55
|
+
# @yieldparam [Exception] ex the exception raised by the async operation OR by its callback
|
56
|
+
def async_rescue(*matches,&errblock)
|
57
|
+
matches << StandardError if matches.empty?
|
58
|
+
rescue_blocks << [ matches ,errblock ]
|
59
|
+
if @op_error && matches.any? { |m| m === @op_error }
|
60
|
+
begin
|
61
|
+
@allow_retry = true
|
62
|
+
@op_rescue= [ nil, errblock.call(@op_error) ]
|
63
|
+
rescue StandardError => ex
|
64
|
+
@operation_rescue_result = [ ex, nil ]
|
65
|
+
ensure
|
66
|
+
@resumed = true
|
67
|
+
@op_error = nil
|
68
|
+
@allow_retry = false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
self
|
72
|
+
end
|
73
|
+
alias :on_error :async_rescue
|
74
|
+
alias :op_rescue :async_rescue
|
75
|
+
|
76
|
+
# @deprecated
|
77
|
+
alias :errback :async_rescue
|
78
|
+
|
79
|
+
|
80
|
+
# Must only be called inside a block supplied to {#async_rescue}
|
81
|
+
# Common case is to retry on connection lost
|
82
|
+
# Retrying :session_expired is guaranteed to give infinite loops!
|
83
|
+
def async_retry()
|
84
|
+
raise ProtocolError "cannot retry outside of a rescue block" unless @allow_retry
|
85
|
+
begin
|
86
|
+
execute()
|
87
|
+
nil
|
88
|
+
rescue ZooKeeper::Error => ex
|
89
|
+
error,result = process_response(ex,nil)
|
90
|
+
if resumed?
|
91
|
+
raise error if error
|
92
|
+
return result
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
alias :op_retry :async_retry
|
98
|
+
alias :try_again :async_retry
|
99
|
+
|
100
|
+
# Wait for the async op to finish and returns its value
|
101
|
+
# @return result of the operation's callback or matched rescue handler
|
102
|
+
# @raise [StandardError] any unrescued exception
|
103
|
+
def value();
|
104
|
+
if @op_error
|
105
|
+
raise @op_error
|
106
|
+
elsif @op_rescue
|
107
|
+
error, result = @op_rescue
|
108
|
+
raise error if error
|
109
|
+
return result
|
110
|
+
else
|
111
|
+
wait_value()
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# @api private
|
116
|
+
attr_accessor :backtrace
|
117
|
+
|
118
|
+
# @api private
|
119
|
+
def resume(op_error,response)
|
120
|
+
mutex.synchronize do
|
121
|
+
# remember this mutex is only used to wait for this response anyway
|
122
|
+
# so synchronizing here is not harmful even if processing the response
|
123
|
+
# includes a long running callback. (which can't create deadlocks
|
124
|
+
# by referencing this op!
|
125
|
+
@resumed = true
|
126
|
+
@error, @result = process_response(op_error,response)
|
127
|
+
cv.signal() if resumed?
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
attr_reader :callback, :operation, :event_loop
|
133
|
+
attr_reader :mutex, :cv, :error, :result
|
134
|
+
|
135
|
+
def execute()
|
136
|
+
@op_error = nil
|
137
|
+
@op_rescue = nil
|
138
|
+
@resumed = false
|
139
|
+
operation.call(self)
|
140
|
+
true
|
141
|
+
end
|
142
|
+
|
143
|
+
def resumed?
|
144
|
+
@resumed
|
145
|
+
end
|
146
|
+
|
147
|
+
def rescue_blocks
|
148
|
+
@rescue_blocks ||= []
|
149
|
+
end
|
150
|
+
|
151
|
+
def process_response(op_error,response)
|
152
|
+
logger.debug { "Processing response #{op_error} #{response}" }
|
153
|
+
|
154
|
+
# For ZooKeeper errors, set the backtrace to the original caller, rather than the ZK event loop
|
155
|
+
op_error.set_backtrace(@backtrace) if @backtrace && ZooKeeper::Error === op_error
|
156
|
+
|
157
|
+
begin
|
158
|
+
return [ nil, callback.call(response) ] unless op_error
|
159
|
+
rescue Exception => ex #enable clients to rescue Exceptions
|
160
|
+
op_error = ex
|
161
|
+
end
|
162
|
+
|
163
|
+
matches,rb = rescue_blocks.detect() { |matches,errblock| matches.any? { |m| m === op_error } }
|
164
|
+
return [ op_error, nil ] unless rb
|
165
|
+
|
166
|
+
begin
|
167
|
+
@allow_retry = true
|
168
|
+
return [ nil, rb.call(op_error) ]
|
169
|
+
rescue StandardError => ex
|
170
|
+
return [ ex , nil ]
|
171
|
+
ensure
|
172
|
+
@allow_retry = false
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def wait_value()
|
177
|
+
if event_loop.current?
|
178
|
+
#Waiting in the event loop (eg made a synchronous call inside a callback)
|
179
|
+
#Keep processing events until we are resumed
|
180
|
+
until resumed? || event_loop.dead?
|
181
|
+
event_loop.pop_event_queue()
|
182
|
+
end
|
183
|
+
else
|
184
|
+
mutex.synchronize {
|
185
|
+
cv.wait(mutex) unless resumed?
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
raise error if error
|
190
|
+
result
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
data/lib/zkruby/client.rb
CHANGED
@@ -48,7 +48,7 @@ module ZooKeeper
|
|
48
48
|
|
49
49
|
|
50
50
|
# Combine permissions constants
|
51
|
-
# @param [Perms] perms
|
51
|
+
# @param [Perms...] perms list of permissions to combine, can be {Perms} constants, symbols or ints
|
52
52
|
# @return [Fixnum] integer representing the combined permission
|
53
53
|
def self.perms(*perms)
|
54
54
|
perms.inject(0) { | result, perm | result = result | Perms.get(perm) }
|
@@ -56,7 +56,7 @@ module ZooKeeper
|
|
56
56
|
|
57
57
|
# Convenience method to create a zk Identity
|
58
58
|
# @param [String] scheme
|
59
|
-
# @param [String]
|
59
|
+
# @param [String] id
|
60
60
|
# @return [Data::Identity] the encapsulated identity for the given scheme
|
61
61
|
def self.id(scheme,id)
|
62
62
|
Data::Identity.new(:scheme => scheme, :identity => id)
|
@@ -65,7 +65,7 @@ module ZooKeeper
|
|
65
65
|
# Convenience method to create a zk ACL
|
66
66
|
# ZK.acl(ZK.id("world","anyone"), ZK::Perms.DELETE, ZL::Perms.WRITE)
|
67
67
|
# @param [Data::Identity] id
|
68
|
-
# @param [Perms]
|
68
|
+
# @param [Perms...] perms list of permissions
|
69
69
|
# @return [Data::ACL] an access control list
|
70
70
|
# @see #perms
|
71
71
|
#
|
@@ -108,8 +108,6 @@ module ZooKeeper
|
|
108
108
|
CURRENT = :zookeeper_current
|
109
109
|
# Main method for connecting to a client
|
110
110
|
# @param addresses [Array<String>] list of host:port for the ZK cluster as Array or comma separated String
|
111
|
-
# @option options [Class] :binding binding optional implementation class
|
112
|
-
# either {EventMachine::Binding} or {RubyIO::Binding} but normally autodiscovered
|
113
111
|
# @option options [String] :chroot chroot path.
|
114
112
|
# All client calls will be made relative to this path
|
115
113
|
# @option options [Watcher] :watch the default watcher
|
@@ -118,37 +116,48 @@ module ZooKeeper
|
|
118
116
|
# @yieldparam [Client]
|
119
117
|
# @return [Client]
|
120
118
|
def self.connect(addresses,options={},&block)
|
121
|
-
|
122
|
-
|
119
|
+
|
120
|
+
binding_module = if Strand.event_machine?
|
121
|
+
require 'zkruby/eventmachine'
|
122
|
+
ZooKeeper::EventMachine::Binding
|
123
123
|
else
|
124
|
-
|
125
|
-
|
124
|
+
require 'zkruby/rubyio'
|
125
|
+
ZooKeeper::RubyIO::Binding
|
126
126
|
end
|
127
|
-
binding = binding_type.new()
|
128
|
-
session = Session.new(binding,addresses,options)
|
129
|
-
client = Client.new(binding)
|
130
|
-
binding.start(client,session)
|
131
127
|
|
128
|
+
logger.debug { "Using binding #{binding_module}" }
|
129
|
+
session = Session.new(addresses,options)
|
130
|
+
# Extend the appropriate #connect method into the session
|
131
|
+
session.extend(binding_module)
|
132
|
+
|
133
|
+
client = Client.new(session)
|
134
|
+
|
135
|
+
session.start(client)
|
136
|
+
|
132
137
|
return client unless block_given?
|
133
138
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
client.close() unless session.closed?
|
143
|
-
end
|
139
|
+
storage = Strand.current[CURRENT] ||= []
|
140
|
+
storage.push(client)
|
141
|
+
begin
|
142
|
+
yield client
|
143
|
+
ensure
|
144
|
+
storage.pop
|
145
|
+
#TODO this will throw an exception if expired
|
146
|
+
client.close()
|
144
147
|
end
|
145
148
|
end
|
146
149
|
|
147
|
-
# within the block supplied to {
|
150
|
+
# within the block supplied to {ZooKeeper.connect} this will return the
|
148
151
|
# current ZK client
|
149
152
|
def self.current
|
150
153
|
#We'd use if key? here if strand supported it
|
151
|
-
|
154
|
+
Strand.current[CURRENT].last if Strand.current[CURRENT]
|
155
|
+
end
|
156
|
+
|
157
|
+
# Allow ZK a chance to send its data/ping
|
158
|
+
# particularly required for the eventmachine binding
|
159
|
+
def self.pass
|
160
|
+
Strand.pass
|
152
161
|
end
|
153
162
|
|
154
163
|
class WatchEvent
|
@@ -175,7 +184,8 @@ module ZooKeeper
|
|
175
184
|
end
|
176
185
|
|
177
186
|
|
178
|
-
# @abstract
|
187
|
+
# @abstract
|
188
|
+
# The watch interface
|
179
189
|
class Watcher
|
180
190
|
# @param [KeeperState] state representing the session state
|
181
191
|
# (:connected, :disconnected, :auth_failed, :session_expired)
|
@@ -243,7 +253,7 @@ module ZooKeeper
|
|
243
253
|
# All calls operate asynchronously or synchronously based on whether a block is supplied
|
244
254
|
#
|
245
255
|
# Without a block, requests are executed synchronously and either return results directly or raise
|
246
|
-
#
|
256
|
+
# an {Error}
|
247
257
|
#
|
248
258
|
# With a block, the request returns immediately with a {AsyncOp}. When the server responds the
|
249
259
|
# block is passed the results. Errors will be sent to an error callback if registered on the {AsyncOp}
|
@@ -257,35 +267,36 @@ module ZooKeeper
|
|
257
267
|
# or with state :expired and event :none when the session is finalised
|
258
268
|
class Client
|
259
269
|
|
270
|
+
attr_reader :session
|
260
271
|
include Operations
|
261
272
|
# @api private
|
262
273
|
# See {::ZooKeeper.connect}
|
263
|
-
def initialize(
|
264
|
-
@
|
274
|
+
def initialize(session)
|
275
|
+
@session = session
|
265
276
|
end
|
266
277
|
|
267
278
|
# Session timeout, initially as supplied, but once connected is the negotiated
|
268
279
|
# timeout with the server.
|
269
280
|
def timeout
|
270
|
-
|
281
|
+
session.timeout
|
271
282
|
end
|
272
283
|
|
273
284
|
# The currently registered default watcher
|
274
285
|
def watcher
|
275
|
-
|
286
|
+
session.watcher
|
276
287
|
end
|
277
288
|
|
278
289
|
# Assign the watcher to the session. This watcher will receive session connect/disconnect/expired
|
279
290
|
# events as well as any path based watches registered to the API calls using the literal value "true"
|
280
|
-
# @param [Watcher
|
291
|
+
# @param [Watcher,#process_watch,Proc] watcher
|
281
292
|
def watcher=(watcher)
|
282
|
-
|
293
|
+
session.watcher=watcher
|
283
294
|
end
|
284
295
|
|
285
296
|
# Retrieve the list of children at the given path
|
286
297
|
# @overload children(path,watch=nil)
|
287
298
|
# @param [String] path
|
288
|
-
# @param [Watcher] if supplied sets a child watch on the given path
|
299
|
+
# @param [Watcher,#process_watch,Proc] if supplied sets a child watch on the given path
|
289
300
|
# @return [Data::Stat,Array<String>] stat,children stat of path and the list of child nodes
|
290
301
|
# @raise [Error]
|
291
302
|
# @overload children(path,watch=nil)
|
@@ -321,7 +332,7 @@ module ZooKeeper
|
|
321
332
|
# Retrieve data
|
322
333
|
# @overload get(path,watch=nil)
|
323
334
|
# @param [String] path
|
324
|
-
# @param [Watcher] watch optional data watch to set on this path
|
335
|
+
# @param [Watcher,#process_watch,Proc] watch optional data watch to set on this path
|
325
336
|
# @return [Data::Stat,String] stat,data at path
|
326
337
|
# @raise [Error]
|
327
338
|
# @overload get(path,watch=nil)
|
@@ -342,19 +353,19 @@ module ZooKeeper
|
|
342
353
|
# Retrieve the {Data::Stat} of a path, or nil if the path does not exist
|
343
354
|
# @overload exists(path,watch=nil)
|
344
355
|
# @param [String] path
|
345
|
-
# @param [Watcher]
|
356
|
+
# @param [Watcher,#process_watch,Proc] watch optional exists watch to set on this path
|
346
357
|
# @return [Data::Stat] Stat of the path or nil if the path does not exist
|
347
358
|
# @raise [Error]
|
348
359
|
# @overload exists(path,watch=nil)
|
349
360
|
# @return [AsyncOp] asynchronous operation
|
350
361
|
# @yieldparam [Data:Stat] stat Stat of the path or nil if the path did not exist
|
351
|
-
def exists(path,watch=nil,&
|
362
|
+
def exists(path,watch=nil,&callback)
|
352
363
|
return synchronous_call(:exists,path,watch)[0] unless block_given?
|
353
364
|
path = chroot(path)
|
354
365
|
|
355
366
|
req = Proto::ExistsRequest.new(:path => path, :watch => watch)
|
356
367
|
queue_request(req,:exists,3,Proto::ExistsResponse,:exists,watch,ExistsPacket) do | response |
|
357
|
-
|
368
|
+
callback.call( response.nil? ? nil : response.stat )
|
358
369
|
end
|
359
370
|
end
|
360
371
|
alias :exists? :exists
|
@@ -445,13 +456,13 @@ module ZooKeeper
|
|
445
456
|
|
446
457
|
# Close the session
|
447
458
|
# @overload close()
|
448
|
-
# @raise [Error]
|
459
|
+
# @raise [Error]
|
449
460
|
# @overload close()
|
450
461
|
# @return [AsyncOp] asynchronous operation
|
451
462
|
# @yield [] callback invoked when session is closed
|
452
463
|
def close(&blk)
|
453
464
|
return synchronous_call(:close) unless block_given?
|
454
|
-
|
465
|
+
session.close(&blk)
|
455
466
|
end
|
456
467
|
|
457
468
|
# @api private
|
@@ -501,23 +512,26 @@ module ZooKeeper
|
|
501
512
|
yield txn
|
502
513
|
txn.commit
|
503
514
|
end
|
504
|
-
private
|
505
515
|
|
506
|
-
def session
|
507
|
-
@binding.session
|
508
|
-
end
|
509
516
|
|
517
|
+
private
|
518
|
+
|
519
|
+
# This is where the magic happens!
|
510
520
|
def synchronous_call(method,*args)
|
521
|
+
# Re-enter the calling method in asynchronous style
|
511
522
|
op = self.send(method,*args) do |*results|
|
512
523
|
results
|
513
524
|
end
|
525
|
+
|
526
|
+
# Remove this call from the stored backtrace
|
514
527
|
op.backtrace = op.backtrace[2..-1] if op.backtrace
|
515
528
|
|
529
|
+
# Wait for the asynchronous op to finish and return its value
|
516
530
|
op.value
|
517
531
|
end
|
518
532
|
|
519
533
|
def queue_request(*args,&blk)
|
520
|
-
op =
|
534
|
+
op = session.request(*args,&blk)
|
521
535
|
op.backtrace = caller[1..-1]
|
522
536
|
op
|
523
537
|
end
|
@@ -540,7 +554,8 @@ module ZooKeeper
|
|
540
554
|
# If the transaction fails none of these callbacks will be executed.
|
541
555
|
class Transaction
|
542
556
|
include Operations
|
543
|
-
|
557
|
+
# @api private
|
558
|
+
# See {Client#transaction}
|
544
559
|
def initialize(client,session)
|
545
560
|
@client = client
|
546
561
|
@session = session
|