zookeeper 0.4.4 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +10 -0
  2. data/CHANGELOG +95 -0
  3. data/Gemfile +17 -0
  4. data/Manifest +8 -2
  5. data/README.markdown +59 -0
  6. data/Rakefile +137 -7
  7. data/ext/.gitignore +6 -0
  8. data/ext/Rakefile +51 -0
  9. data/ext/c_zookeeper.rb +212 -0
  10. data/ext/dbg.h +53 -0
  11. data/ext/depend +5 -0
  12. data/ext/extconf.rb +44 -15
  13. data/ext/generate_gvl_code.rb +316 -0
  14. data/ext/zkc-3.3.5.tar.gz +0 -0
  15. data/ext/zkrb_wrapper.c +731 -0
  16. data/ext/zkrb_wrapper.h +330 -0
  17. data/ext/zkrb_wrapper_compat.c +15 -0
  18. data/ext/zkrb_wrapper_compat.h +11 -0
  19. data/ext/zookeeper_base.rb +211 -0
  20. data/ext/zookeeper_c.c +268 -97
  21. data/ext/zookeeper_lib.c +157 -92
  22. data/ext/zookeeper_lib.h +12 -6
  23. data/java/zookeeper_base.rb +477 -0
  24. data/lib/zookeeper/acls.rb +10 -1
  25. data/lib/zookeeper/callbacks.rb +5 -3
  26. data/lib/zookeeper/common/queue_with_pipe.rb +78 -0
  27. data/lib/zookeeper/common.rb +174 -0
  28. data/lib/zookeeper/constants.rb +31 -28
  29. data/lib/zookeeper/em_client.rb +55 -0
  30. data/lib/zookeeper/exceptions.rb +10 -2
  31. data/lib/zookeeper/stat.rb +11 -2
  32. data/lib/zookeeper/version.rb +6 -0
  33. data/lib/zookeeper.rb +155 -122
  34. data/notes.txt +14 -0
  35. data/spec/c_zookeeper_spec.rb +50 -0
  36. data/spec/chrooted_connection_spec.rb +81 -0
  37. data/spec/default_watcher_spec.rb +41 -0
  38. data/spec/em_spec.rb +51 -0
  39. data/spec/log4j.properties +17 -0
  40. data/spec/shared/all_success_return_values.rb +10 -0
  41. data/spec/shared/connection_examples.rb +1018 -0
  42. data/spec/spec_helper.rb +119 -0
  43. data/spec/support/progress_formatter.rb +15 -0
  44. data/spec/zookeeper_spec.rb +24 -0
  45. data/zookeeper.gemspec +37 -25
  46. metadata +78 -34
  47. data/README +0 -42
  48. data/ext/zkc-3.3.2.tar.gz +0 -0
@@ -0,0 +1,174 @@
1
+ require 'zookeeper/exceptions'
2
+
3
+ module ZookeeperCommon
4
+ # sigh, i guess define this here?
5
+ ZKRB_GLOBAL_CB_REQ = -1
6
+
7
+ def event_dispatch_thread?
8
+ @dispatcher && (@dispatcher == Thread.current)
9
+ end
10
+
11
+ protected
12
+ def get_next_event(blocking=true)
13
+ @event_queue.pop(!blocking).tap do |event|
14
+ logger.debug { "#{self.class}##{__method__} delivering event #{event.inspect}" }
15
+ end
16
+ rescue ThreadError
17
+ nil
18
+ end
19
+
20
+ def setup_call(meth_name, opts)
21
+ req_id = nil
22
+ @mutex.synchronize {
23
+ req_id = @current_req_id
24
+ @current_req_id += 1
25
+ setup_completion(req_id, meth_name, opts) if opts[:callback]
26
+ setup_watcher(req_id, opts) if opts[:watcher]
27
+ }
28
+ req_id
29
+ end
30
+
31
+ def setup_watcher(req_id, call_opts)
32
+ @watcher_reqs[req_id] = { :watcher => call_opts[:watcher],
33
+ :context => call_opts[:watcher_context] }
34
+ end
35
+
36
+ # as a hack, to provide consistency between the java implementation and the C
37
+ # implementation when dealing w/ chrooted connections, we override this in
38
+ # ext/zookeeper_base.rb to wrap the callback in a chroot-path-stripping block.
39
+ #
40
+ # we don't use meth_name here, but we need it in the C implementation
41
+ #
42
+ def setup_completion(req_id, meth_name, call_opts)
43
+ @completion_reqs[req_id] = { :callback => call_opts[:callback],
44
+ :context => call_opts[:callback_context] }
45
+ end
46
+
47
+ def get_watcher(req_id)
48
+ @mutex.synchronize {
49
+ (req_id == ZKRB_GLOBAL_CB_REQ) ? @watcher_reqs[req_id] : @watcher_reqs.delete(req_id)
50
+ }
51
+ end
52
+
53
+ def get_completion(req_id)
54
+ @mutex.synchronize { @completion_reqs.delete(req_id) }
55
+ end
56
+
57
+ def setup_dispatch_thread!
58
+ logger.debug { "starting dispatch thread" }
59
+ @dispatcher ||= Thread.new do
60
+ while true
61
+ begin
62
+ dispatch_next_callback(get_next_event(true))
63
+ rescue QueueWithPipe::ShutdownException
64
+ logger.info { "dispatch thread exiting, got shutdown exception" }
65
+ break
66
+ rescue Exception => e
67
+ $stderr.puts ["#{e.class}: #{e.message}", e.backtrace.map { |n| "\t#{n}" }.join("\n")].join("\n")
68
+ end
69
+ end
70
+ signal_dispatch_thread_exit!
71
+ end
72
+ end
73
+
74
+ # this method is part of the reopen/close code, and is responsible for
75
+ # shutting down the dispatch thread.
76
+ #
77
+ # @dispatcher will be nil when this method exits
78
+ #
79
+ def stop_dispatch_thread!
80
+ logger.debug { "#{self.class}##{__method__}" }
81
+
82
+ if @dispatcher
83
+ if @dispatcher.join(0)
84
+ @dispatcher = nil
85
+ return
86
+ end
87
+
88
+ @mutex.synchronize do
89
+ event_queue.graceful_close!
90
+
91
+ # we now release the mutex so that dispatch_next_callback can grab it
92
+ # to do what it needs to do while delivering events
93
+ #
94
+ # wait for a maximum of 2 sec for dispatcher to signal exit (should be
95
+ # fast)
96
+ @dispatch_shutdown_cond.wait(2)
97
+
98
+ # wait for another 2 sec for the thread to join
99
+ unless @dispatcher.join(2)
100
+ logger.error { "Dispatch thread did not join cleanly, continuing" }
101
+ end
102
+ @dispatcher = nil
103
+ end
104
+ end
105
+ end
106
+
107
+ def signal_dispatch_thread_exit!
108
+ @mutex.synchronize do
109
+ logger.debug { "dispatch thread exiting!" }
110
+ @dispatch_shutdown_cond.broadcast
111
+ end
112
+ end
113
+
114
+ def dispatch_next_callback(hash)
115
+ return nil unless hash
116
+
117
+ Zookeeper.logger.debug { "get_next_event returned: #{prettify_event(hash).inspect}" }
118
+
119
+ is_completion = hash.has_key?(:rc)
120
+
121
+ hash[:stat] = ZookeeperStat::Stat.new(hash[:stat]) if hash.has_key?(:stat)
122
+ hash[:acl] = hash[:acl].map { |acl| ZookeeperACLs::ACL.new(acl) } if hash[:acl]
123
+
124
+ callback_context = is_completion ? get_completion(hash[:req_id]) : get_watcher(hash[:req_id])
125
+
126
+ # When connectivity to the server has been lost (as indicated by SESSION_EVENT)
127
+ # we want to rerun the callback at a later time when we eventually do have
128
+ # a valid response.
129
+ if hash[:type] == ZookeeperConstants::ZOO_SESSION_EVENT
130
+ is_completion ? setup_completion(hash[:req_id], callback_context) : setup_watcher(hash[:req_id], callback_context)
131
+ end
132
+ if callback_context
133
+ callback = is_completion ? callback_context[:callback] : callback_context[:watcher]
134
+
135
+ hash[:context] = callback_context[:context]
136
+
137
+ # TODO: Eventually enforce derivation from Zookeeper::Callback
138
+ if callback.respond_to?(:call)
139
+ callback.call(hash)
140
+ else
141
+ # puts "dispatch_next_callback found non-callback => #{callback.inspect}"
142
+ end
143
+ else
144
+ logger.warn { "Duplicate event received (no handler for req_id #{hash[:req_id]}, event: #{hash.inspect}" }
145
+ end
146
+ true
147
+ end
148
+
149
+ def assert_supported_keys(args, supported)
150
+ unless (args.keys - supported).empty?
151
+ raise ZookeeperExceptions::ZookeeperException::BadArguments, # this heirarchy is kind of retarded
152
+ "Supported arguments are: #{supported.inspect}, but arguments #{args.keys.inspect} were supplied instead"
153
+ end
154
+ end
155
+
156
+ def assert_required_keys(args, required)
157
+ unless (required - args.keys).empty?
158
+ raise ZookeeperExceptions::ZookeeperException::BadArguments,
159
+ "Required arguments are: #{required.inspect}, but only the arguments #{args.keys.inspect} were supplied."
160
+ end
161
+ end
162
+
163
+ private
164
+ def prettify_event(hash)
165
+ hash.dup.tap do |h|
166
+ # pretty up the event display
167
+ h[:type] = ZookeeperConstants::EVENT_TYPE_NAMES.fetch(h[:type]) if h[:type]
168
+ h[:state] = ZookeeperConstants::STATE_NAMES.fetch(h[:state]) if h[:state]
169
+ h[:req_id] = :global_session if h[:req_id] == -1
170
+ end
171
+ end
172
+ end
173
+
174
+ require 'zookeeper/common/queue_with_pipe'
@@ -6,6 +6,7 @@ module ZookeeperConstants
6
6
  # session state
7
7
  ZOO_EXPIRED_SESSION_STATE = -112
8
8
  ZOO_AUTH_FAILED_STATE = -113
9
+ ZOO_CLOSED_STATE = 0
9
10
  ZOO_CONNECTING_STATE = 1
10
11
  ZOO_ASSOCIATING_STATE = 2
11
12
  ZOO_CONNECTED_STATE = 3
@@ -17,38 +18,40 @@ module ZookeeperConstants
17
18
  ZOO_CHILD_EVENT = 4
18
19
  ZOO_SESSION_EVENT = -1
19
20
  ZOO_NOTWATCHING_EVENT = -2
20
-
21
- def print_events
22
- puts "ZK events:"
23
- ZookeeperConstants::constants.each do |c|
24
- puts "\t #{c}" if c =~ /^ZOO..*EVENT$/
25
- end
26
- end
27
21
 
28
- def print_states
29
- puts "ZK states:"
30
- ZookeeperConstants::constants.each do |c|
31
- puts "\t #{c}" if c =~ /^ZOO..*STATE$/
32
- end
33
- end
22
+ # only used by the C extension
23
+ ZOO_LOG_LEVEL_ERROR = 1
24
+ ZOO_LOG_LEVEL_WARN = 2
25
+ ZOO_LOG_LEVEL_INFO = 3
26
+ ZOO_LOG_LEVEL_DEBUG = 4
27
+
28
+ # used to find the name for a numeric event
29
+ # @private
30
+ EVENT_TYPE_NAMES = {
31
+ 1 => 'created',
32
+ 2 => 'deleted',
33
+ 3 => 'changed',
34
+ 4 => 'child',
35
+ -1 => 'session',
36
+ -2 => 'notwatching',
37
+ }
34
38
 
39
+ # used to pretty print the state name
40
+ # @private
41
+ STATE_NAMES = {
42
+ -112 => 'expired_session',
43
+ -113 => 'auth_failed',
44
+ 0 => 'closed',
45
+ 1 => 'connecting',
46
+ 2 => 'associating',
47
+ 3 => 'connected',
48
+ }
49
+
35
50
  def event_by_value(v)
36
- return unless v
37
- ZookeeperConstants::constants.each do |c|
38
- next unless c =~ /^ZOO..*EVENT$/
39
- if eval("ZookeeperConstants::#{c}") == v
40
- return c
41
- end
42
- end
51
+ (name = EVENT_TYPE_NAMES[v]) ? "ZOO_#{name.upcase}_EVENT" : ''
43
52
  end
44
-
53
+
45
54
  def state_by_value(v)
46
- return unless v
47
- ZookeeperConstants::constants.each do |c|
48
- next unless c =~ /^ZOO..*STATE$/
49
- if eval("ZookeeperConstants::#{c}") == v
50
- return c
51
- end
52
- end
55
+ (name = STATE_NAMES[v]) ? "ZOO_#{name.upcase}_STATE" : ''
53
56
  end
54
57
  end
@@ -0,0 +1,55 @@
1
+ require 'zookeeper'
2
+ require 'eventmachine'
3
+
4
+ module ZookeeperEM
5
+ class Client < Zookeeper
6
+ # @private
7
+ # the EM Connection instance we receive once we call EM.watch on our selectable_io
8
+ attr_reader :em_connection
9
+
10
+ def initialize(*a, &b)
11
+ @on_close = EM::DefaultDeferrable.new
12
+ @on_attached = EM::DefaultDeferrable.new
13
+ @em_connection = nil
14
+ logger.debug { "ZookeeperEM::Client obj_id %x: init" % [object_id] }
15
+ super(*a, &b)
16
+ on_attached.succeed
17
+ end
18
+
19
+ # EM::DefaultDeferrable that will be called back when our em_connection has been detached
20
+ # and we've completed the close operation
21
+ def on_close(&block)
22
+ @on_close.callback(&block) if block
23
+ @on_close
24
+ end
25
+
26
+ # called after we've successfully registered our selectable_io to be
27
+ # managed by the EM reactor
28
+ def on_attached(&block)
29
+ @on_attached.callback(&block) if block
30
+ @on_attached
31
+ end
32
+
33
+ def dispatch_next_callback(hash)
34
+ EM.schedule do
35
+ if running? and not closed?
36
+ logger.debug { "#{self.class}##{__method__} dispatch_next_callback: #{hash.inspect}: reactor_thread? #{EM.reactor_thread?}, running? #{running?}, closed? #{closed?}" }
37
+ super(hash)
38
+ end
39
+ end
40
+ end
41
+
42
+ def close(&block)
43
+ on_close(&block)
44
+ super()
45
+ on_close.succeed
46
+ end
47
+
48
+ # Because eventmachine is single-threaded, and events are dispatched on the
49
+ # reactor thread we just delegate this to EM.reactor_thread?
50
+ def event_dispatch_thread?
51
+ EM.reactor_thread?
52
+ end
53
+ end # Client
54
+ end # ZookeeperEM
55
+
@@ -27,7 +27,7 @@ module ZookeeperExceptions
27
27
  ZNOTHING = -117
28
28
  ZSESSIONMOVED = -118
29
29
 
30
- class ZookeeperException < Exception
30
+ class ZookeeperException < StandardError
31
31
  class EverythingOk < ZookeeperException; end
32
32
  class SystemError < ZookeeperException; end
33
33
  class RunTimeInconsistency < ZookeeperException; end
@@ -52,7 +52,15 @@ module ZookeeperExceptions
52
52
  class Closing < ZookeeperException; end
53
53
  class Nothing < ZookeeperException; end
54
54
  class SessionMoved < ZookeeperException; end
55
- class ConnectionClosed < ZookeeperException; end # this is a Ruby client exception
55
+
56
+ # these are Ruby client exceptions
57
+ class ConnectionClosed < ZookeeperException; end
58
+ class NotConnected < ZookeeperException; end
59
+ class ShuttingDownException < ZookeeperException; end
60
+ class DataTooLargeException < ZookeeperException; end
61
+
62
+ # yes, make an alias, this is the way zookeeper refers to it
63
+ ExpiredSession = SessionExpired
56
64
 
57
65
  def self.by_code(code)
58
66
  case code
@@ -1,12 +1,21 @@
1
1
  module ZookeeperStat
2
2
  class Stat
3
- attr_reader :version, :exists, :czxid, :mzxid, :ctime, :mtime, :cverzion, :aversion, :ephemeralOwner, :dataLength, :numChildren, :pzxid
3
+ attr_reader :version, :exists, :czxid, :mzxid, :ctime, :mtime, :cversion, :aversion, :ephemeralOwner, :dataLength, :numChildren, :pzxid
4
+
5
+ alias :ephemeral_owner :ephemeralOwner
6
+ alias :num_children :numChildren
7
+ alias :data_length :dataLength
8
+
4
9
  def initialize(val)
5
10
  @exists = !!val
6
11
  @czxid, @mzxid, @ctime, @mtime, @version, @cversion, @aversion,
7
12
  @ephemeralOwner, @dataLength, @numChildren, @pzxid = val if val.is_a?(Array)
8
13
  val.each { |k,v| instance_variable_set "@#{k}", v } if val.is_a?(Hash)
9
- raise ArgumentError unless (val.is_a?(Hash) or val.is_a?(Array) or val == nil)
14
+ raise ArgumentError unless (val.is_a?(Hash) or val.is_a?(Array) or val.nil?)
15
+ end
16
+
17
+ def exists?
18
+ @exists
10
19
  end
11
20
  end
12
21
  end
@@ -0,0 +1,6 @@
1
+ # this "namespacing" is retarded. fix this in 1.0
2
+ # @private
3
+ module ZookeeperVersion
4
+ VERSION = '0.9.3'
5
+ DRIVER_VERSION = '3.3.5'
6
+ end