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