zookeeper 0.4.4 → 0.9.3

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.
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