zookeeper-ng 1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/.ctags_paths +1 -0
  3. data/.dotfiles/ruby-gemset +1 -0
  4. data/.dotfiles/ruby-version +1 -0
  5. data/.dotfiles/rvmrc +2 -0
  6. data/.gitignore +19 -0
  7. data/.gitmodules +3 -0
  8. data/.travis.yml +25 -0
  9. data/CHANGELOG +395 -0
  10. data/Gemfile +30 -0
  11. data/Guardfile +8 -0
  12. data/LICENSE +23 -0
  13. data/Manifest +29 -0
  14. data/README.markdown +85 -0
  15. data/Rakefile +121 -0
  16. data/cause-abort.rb +117 -0
  17. data/ext/.gitignore +6 -0
  18. data/ext/Rakefile +41 -0
  19. data/ext/c_zookeeper.rb +398 -0
  20. data/ext/common.h +17 -0
  21. data/ext/dbg.h +53 -0
  22. data/ext/depend +5 -0
  23. data/ext/event_lib.c +740 -0
  24. data/ext/event_lib.h +175 -0
  25. data/ext/extconf.rb +103 -0
  26. data/ext/generate_gvl_code.rb +321 -0
  27. data/ext/patches/zkc-3.3.5-network.patch +24 -0
  28. data/ext/patches/zkc-3.4.5-fetch-and-add.patch +16 -0
  29. data/ext/patches/zkc-3.4.5-logging.patch +41 -0
  30. data/ext/patches/zkc-3.4.5-out-of-order-ping.patch +163 -0
  31. data/ext/patches/zkc-3.4.5-overflow.patch +11 -0
  32. data/ext/patches/zkc-3.4.5-yosemite-htonl-fix.patch +102 -0
  33. data/ext/zkc-3.4.5.tar.gz +0 -0
  34. data/ext/zkrb.c +1075 -0
  35. data/ext/zkrb_wrapper.c +775 -0
  36. data/ext/zkrb_wrapper.h +350 -0
  37. data/ext/zkrb_wrapper_compat.c +15 -0
  38. data/ext/zkrb_wrapper_compat.h +11 -0
  39. data/ext/zookeeper_base.rb +256 -0
  40. data/java/java_base.rb +503 -0
  41. data/lib/zookeeper.rb +115 -0
  42. data/lib/zookeeper/acls.rb +44 -0
  43. data/lib/zookeeper/callbacks.rb +108 -0
  44. data/lib/zookeeper/client.rb +30 -0
  45. data/lib/zookeeper/client_methods.rb +282 -0
  46. data/lib/zookeeper/common.rb +122 -0
  47. data/lib/zookeeper/common/queue_with_pipe.rb +110 -0
  48. data/lib/zookeeper/compatibility.rb +138 -0
  49. data/lib/zookeeper/constants.rb +97 -0
  50. data/lib/zookeeper/continuation.rb +223 -0
  51. data/lib/zookeeper/core_ext.rb +58 -0
  52. data/lib/zookeeper/em_client.rb +55 -0
  53. data/lib/zookeeper/exceptions.rb +135 -0
  54. data/lib/zookeeper/forked.rb +19 -0
  55. data/lib/zookeeper/latch.rb +34 -0
  56. data/lib/zookeeper/logger.rb +39 -0
  57. data/lib/zookeeper/logger/forwarding_logger.rb +84 -0
  58. data/lib/zookeeper/monitor.rb +19 -0
  59. data/lib/zookeeper/rake_tasks.rb +165 -0
  60. data/lib/zookeeper/request_registry.rb +153 -0
  61. data/lib/zookeeper/stat.rb +21 -0
  62. data/lib/zookeeper/version.rb +4 -0
  63. data/notes.txt +14 -0
  64. data/scripts/upgrade-1.0-sed-alike.rb +46 -0
  65. data/spec/c_zookeeper_spec.rb +51 -0
  66. data/spec/chrooted_connection_spec.rb +83 -0
  67. data/spec/compatibilty_spec.rb +8 -0
  68. data/spec/default_watcher_spec.rb +41 -0
  69. data/spec/em_spec.rb +51 -0
  70. data/spec/ext/zookeeper_base_spec.rb +19 -0
  71. data/spec/forked_connection_spec.rb +124 -0
  72. data/spec/latch_spec.rb +24 -0
  73. data/spec/log4j.properties +17 -0
  74. data/spec/shared/all_success_return_values.rb +10 -0
  75. data/spec/shared/connection_examples.rb +1077 -0
  76. data/spec/spec_helper.rb +61 -0
  77. data/spec/support/00_logging.rb +38 -0
  78. data/spec/support/10_spawn_zookeeper.rb +24 -0
  79. data/spec/support/progress_formatter.rb +15 -0
  80. data/spec/support/zookeeper_spec_helpers.rb +96 -0
  81. data/spec/zookeeper_spec.rb +24 -0
  82. data/zookeeper.gemspec +38 -0
  83. data/zoomonkey/duplicates +3 -0
  84. data/zoomonkey/zoomonkey.rb +194 -0
  85. metadata +157 -0
@@ -0,0 +1,223 @@
1
+ module Zookeeper
2
+ # @private
3
+ # sigh, slightly different than the userland callbacks, the continuation
4
+ # provides sync call semantics around an async api
5
+ class Continuation
6
+ include Constants
7
+ include Logger
8
+
9
+ OPERATION_TIMEOUT = 30 # seconds
10
+
11
+ # for keeping track of which continuations are pending, and which ones have
12
+ # been submitted and are awaiting a repsonse
13
+ #
14
+ # `state_check` are high-priority checks that query the connection about
15
+ # its current state, they always run before other continuations
16
+ #
17
+ class Registry < Struct.new(:pending, :state_check, :in_flight)
18
+ extend Forwardable
19
+
20
+ def_delegators :@mutex, :lock, :unlock
21
+
22
+ def initialize
23
+ super([], [], {})
24
+ @mutex = Mutex.new
25
+ end
26
+
27
+ def synchronize
28
+ @mutex.lock
29
+ begin
30
+ yield self
31
+ ensure
32
+ @mutex.unlock rescue nil
33
+ end
34
+ end
35
+
36
+ # does not lock the mutex, returns true if there are pending jobs
37
+ def anything_to_do?
38
+ (pending.length + state_check.length) > 0
39
+ end
40
+
41
+ # returns the pending continuations, resetting the list
42
+ # this method is synchronized
43
+ def next_batch()
44
+ @mutex.lock
45
+ begin
46
+ state_check.slice!(0, state_check.length) + pending.slice!(0,pending.length)
47
+ ensure
48
+ @mutex.unlock rescue nil
49
+ end
50
+ end
51
+ end # Registry
52
+
53
+ # *sigh* what is the index in the *args array of the 'callback' param
54
+ CALLBACK_ARG_IDX = {
55
+ :get => 2,
56
+ :set => 3,
57
+ :exists => 2,
58
+ :create => 3,
59
+ :delete => 3,
60
+ :get_acl => 2,
61
+ :set_acl => 3,
62
+ :get_children => 2,
63
+ :state => 0,
64
+ :add_auth => 2
65
+ }
66
+
67
+ # maps the method name to the async return hash keys it should use to
68
+ # deliver the results
69
+ METH_TO_ASYNC_RESULT_KEYS = {
70
+ :get => [:rc, :data, :stat],
71
+ :set => [:rc, :stat],
72
+ :exists => [:rc, :stat],
73
+ :create => [:rc, :string],
74
+ :delete => [:rc],
75
+ :get_acl => [:rc, :acl, :stat],
76
+ :set_acl => [:rc],
77
+ :get_children => [:rc, :strings, :stat],
78
+ :add_auth => [:rc]
79
+ }
80
+
81
+ attr_accessor :meth, :block, :rval
82
+
83
+ attr_reader :args
84
+
85
+ def initialize(meth, *args)
86
+ @meth = meth
87
+ @args = args.freeze
88
+ @mutex = Monitor.new
89
+ @cond = @mutex.new_cond
90
+ @rval = nil
91
+
92
+ # make this error reporting more robust if necessary, right now, just set to state
93
+ @error = nil
94
+ end
95
+
96
+ # the caller calls this method and receives the response from the async loop
97
+ # this method has a hard-coded 30 second timeout as a safety feature. No
98
+ # call should take more than 20s (as the session timeout is set to 20s)
99
+ # so if any call takes longer than that, something has gone horribly wrong.
100
+ #
101
+ # @raise [ContinuationTimeoutError] if a response is not received within 30s
102
+ #
103
+ def value
104
+ time_to_stop = Time.now + OPERATION_TIMEOUT
105
+ now = nil
106
+
107
+ @mutex.synchronize do
108
+ while true
109
+ now = Time.now
110
+ break if @rval or @error or (now > time_to_stop)
111
+
112
+ deadline = time_to_stop.to_f - now.to_f
113
+ @cond.wait(deadline)
114
+ end
115
+
116
+ if (now > time_to_stop) and !@rval and !@error
117
+ raise Exceptions::ContinuationTimeoutError, "response for meth: #{meth.inspect}, args: #{@args.inspect}, not received within #{OPERATION_TIMEOUT} seconds"
118
+ end
119
+
120
+ case @error
121
+ when nil
122
+ # ok, nothing to see here, carry on
123
+ when :shutdown
124
+ raise Exceptions::NotConnected, "the connection is shutting down"
125
+ when ZOO_EXPIRED_SESSION_STATE
126
+ raise Exceptions::SessionExpired, "connection has expired"
127
+ else
128
+ raise Exceptions::NotConnected, "connection state is #{STATE_NAMES[@error]}"
129
+ end
130
+
131
+ case @rval.length
132
+ when 1
133
+ return @rval.first
134
+ else
135
+ return @rval
136
+ end
137
+ end
138
+ end
139
+
140
+ # receive the response from the server, set @rval, notify caller
141
+ def call(hash)
142
+ logger.debug { "continuation req_id #{req_id}, got hash: #{hash.inspect}" }
143
+ @rval = hash.values_at(*METH_TO_ASYNC_RESULT_KEYS.fetch(meth))
144
+ logger.debug { "delivering result #{@rval.inspect}" }
145
+ deliver!
146
+ end
147
+
148
+ def user_callback?
149
+ !!@args.at(callback_arg_idx)
150
+ end
151
+
152
+ # this method is called by the event thread to submit the request
153
+ # passed the CZookeeper instance, makes the async call and deals with the results
154
+ #
155
+ # BTW: in case you were wondering this is a completely stupid
156
+ # implementation, but it's more important to get *something* working and
157
+ # passing specs, then refactor to make everything sane
158
+ #
159
+ #
160
+ def submit(czk)
161
+ state = czk.zkrb_state # check the state of the connection
162
+
163
+ if @meth == :state # if the method is a state call
164
+ @rval = [state] # we're done, no error
165
+ return deliver!
166
+
167
+ elsif state != ZOO_CONNECTED_STATE # otherwise, we must be connected
168
+ @error = state # so set the error
169
+ return deliver! # and we're out
170
+ end
171
+
172
+ rc, *_ = czk.__send__(:"zkrb_#{@meth}", *async_args)
173
+
174
+ if user_callback? or (rc != ZOK) # async call, or we failed to submit it
175
+ @rval = [rc] # create the repsonse
176
+ deliver! # wake the caller and we're out
177
+ end
178
+ end
179
+
180
+ def req_id
181
+ @args.first
182
+ end
183
+
184
+ def state_call?
185
+ @meth == :state
186
+ end
187
+
188
+ # interrupt the sleeping thread with a NotConnected error
189
+ def shutdown!
190
+ @mutex.synchronize do
191
+ return if @rval or @error
192
+ @error = :shutdown
193
+ @cond.broadcast
194
+ end
195
+ end
196
+
197
+ protected
198
+
199
+ # an args array with the only difference being that if there's a user
200
+ # callback provided, we don't handle delivering the end result
201
+ def async_args
202
+ return [] if @meth == :state # special-case :P
203
+ ary = @args.dup
204
+
205
+ logger.debug { "async_args, meth: #{meth} ary: #{ary.inspect}, #{callback_arg_idx}" }
206
+
207
+ ary[callback_arg_idx] ||= self
208
+
209
+ ary
210
+ end
211
+
212
+ def callback_arg_idx
213
+ CALLBACK_ARG_IDX.fetch(meth) { raise ArgumentError, "unknown method #{meth.inspect}" }
214
+ end
215
+
216
+ def deliver!
217
+ @mutex.synchronize do
218
+ @cond.signal
219
+ end
220
+ end
221
+ end # Base
222
+ end
223
+
@@ -0,0 +1,58 @@
1
+ # @private
2
+ module ::Kernel
3
+ unless method_defined?(:silence_warnings)
4
+ def silence_warnings
5
+ with_warnings(nil) { yield }
6
+ end
7
+ end
8
+
9
+ unless method_defined?(:with_warnings)
10
+ def with_warnings(flag)
11
+ old_verbose, $VERBOSE = $VERBOSE, flag
12
+ yield
13
+ ensure
14
+ $VERBOSE = old_verbose
15
+ end
16
+ end
17
+ end
18
+
19
+ # @private
20
+ class ::Exception
21
+ unless method_defined?(:to_std_format)
22
+ def to_std_format
23
+ ary = ["#{self.class}: #{message}"]
24
+ ary.concat(backtrace || [])
25
+ ary.join("\n\t")
26
+ end
27
+ end
28
+ end
29
+
30
+ # this is borrowed from the excellent Logging gem: https://github.com/TwP/logging
31
+ # @private
32
+ class ::Module
33
+ # @private
34
+ # Returns a predictable logger name for the current module or class. If
35
+ # used within an anonymous class, the first non-anonymous class name will
36
+ # be used as the logger name. If used within a meta-class, the name of the
37
+ # actual class will be used as the logger name. If used within an
38
+ # anonymous module, the string 'anonymous' will be returned.
39
+ def _zk_logger_name
40
+ return name unless name.nil? or name.empty?
41
+
42
+ # check if this is a metaclass (or eigenclass)
43
+ if ancestors.include? Class
44
+ inspect =~ %r/#<Class:([^#>]+)>/
45
+ return $1
46
+ end
47
+
48
+ # see if we have a superclass
49
+ if respond_to? :superclass
50
+ return superclass.logger_name
51
+ end
52
+
53
+ # we are an anonymous module
54
+ return 'anonymous'
55
+ end
56
+ end
57
+
58
+
@@ -0,0 +1,55 @@
1
+ require 'zookeeper'
2
+ require 'eventmachine'
3
+
4
+ module ZookeeperEM
5
+ class Client < Zookeeper::Client
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
+
@@ -0,0 +1,135 @@
1
+ module Zookeeper
2
+ module Exceptions
3
+ include Constants
4
+
5
+ class ZookeeperException < StandardError
6
+
7
+ unless defined?(CONST_MISSING_WARNING)
8
+
9
+ CONST_MISSING_WARNING = <<-EOS
10
+
11
+ ------------------------------------------------------------------------------------------
12
+ WARNING! THE ZOOKEEPER NAMESPACE HAS CHANGED AS OF 1.0!
13
+
14
+ Please update your code to use the new hierarchy!
15
+
16
+ The constant that got you this was ZookeeperExceptions::ZookeeperException::%s
17
+
18
+ stacktrace:
19
+ %s
20
+
21
+ ------------------------------------------------------------------------------------------
22
+
23
+ EOS
24
+ end
25
+
26
+ # NOTE(slyphon): Since 0.4 all of the ZookeeperException subclasses were
27
+ # defined inside of ZookeeperException, which always seemed well, icky.
28
+ # if someone references one of these we'll print out a warning and
29
+ # then give them the constant
30
+ #
31
+ def self.const_missing(const)
32
+ if Zookeeper::Exceptions.const_defined?(const)
33
+
34
+ stacktrace = caller[0..-2].reject {|n| n =~ %r%/rspec/% }.map { |n| "\t#{n}" }.join("\n")
35
+
36
+ Zookeeper.deprecation_warning(CONST_MISSING_WARNING % [const.to_s, stacktrace])
37
+
38
+
39
+ Zookeeper::Exceptions.const_get(const).tap do |const_val|
40
+ self.const_set(const, const_val)
41
+ end
42
+ else
43
+ super
44
+ end
45
+ end
46
+ end
47
+
48
+
49
+ class EverythingOk < ZookeeperException; end
50
+ class SystemError < ZookeeperException; end
51
+ class RunTimeInconsistency < ZookeeperException; end
52
+ class DataInconsistency < ZookeeperException; end
53
+ class ConnectionLoss < ZookeeperException; end
54
+ class MarshallingError < ZookeeperException; end
55
+ class Unimplemented < ZookeeperException; end
56
+ class OperationTimeOut < ZookeeperException; end
57
+ class BadArguments < ZookeeperException; end
58
+ class InvalidState < ZookeeperException; end
59
+ class ApiError < ZookeeperException; end
60
+ class NoNode < ZookeeperException; end
61
+ class NoAuth < ZookeeperException; end
62
+ class BadVersion < ZookeeperException; end
63
+ class NoChildrenForEphemerals < ZookeeperException; end
64
+ class NodeExists < ZookeeperException; end
65
+ class NotEmpty < ZookeeperException; end
66
+ class SessionExpired < ZookeeperException; end
67
+ class InvalidCallback < ZookeeperException; end
68
+ class InvalidACL < ZookeeperException; end
69
+ class AuthFailed < ZookeeperException; end
70
+ class Closing < ZookeeperException; end
71
+ class Nothing < ZookeeperException; end
72
+ class SessionMoved < ZookeeperException; end
73
+
74
+ # these are Ruby client exceptions
75
+ class ConnectionClosed < ZookeeperException; end
76
+ class NotConnected < ZookeeperException; end
77
+ class ShuttingDownException < ZookeeperException; end
78
+ class DataTooLargeException < ZookeeperException; end
79
+
80
+ # raised when an operation is performed on an instance without a valid
81
+ # zookeeper handle. (C version)
82
+ class HandleClosedException < ZookeeperException; end
83
+
84
+ # maybe use this for continuation
85
+ class InterruptedException < ZookeeperException ; end
86
+
87
+ # raised when a continuation operation takes more time than is reasonable and
88
+ # the thread should be awoken. (i.e. prevents a call that never returns)
89
+ class ContinuationTimeoutError < ZookeeperException; end
90
+
91
+ # raised when the user tries to use a connection after a fork()
92
+ # without calling reopen() in the C client
93
+ #
94
+ # (h/t: @pletern http://git.io/zIsq1Q)
95
+ class InheritedConnectionError < ZookeeperException; end
96
+
97
+ # yes, make an alias, this is the way zookeeper refers to it
98
+ ExpiredSession = SessionExpired unless defined?(ExpiredSession)
99
+
100
+ def self.by_code(code)
101
+ case code
102
+ when ZOK then EverythingOk
103
+ when ZSYSTEMERROR then SystemError
104
+ when ZRUNTIMEINCONSISTENCY then RunTimeInconsistency
105
+ when ZDATAINCONSISTENCY then DataInconsistency
106
+ when ZCONNECTIONLOSS then ConnectionLoss
107
+ when ZMARSHALLINGERROR then MarshallingError
108
+ when ZUNIMPLEMENTED then Unimplemented
109
+ when ZOPERATIONTIMEOUT then OperationTimeOut
110
+ when ZBADARGUMENTS then BadArguments
111
+ when ZINVALIDSTATE then InvalidState
112
+ when ZAPIERROR then ApiError
113
+ when ZNONODE then NoNode
114
+ when ZNOAUTH then NoAuth
115
+ when ZBADVERSION then BadVersion
116
+ when ZNOCHILDRENFOREPHEMERALS then NoChildrenForEphemerals
117
+ when ZNODEEXISTS then NodeExists
118
+ when ZNOTEMPTY then NotEmpty
119
+ when ZSESSIONEXPIRED then SessionExpired
120
+ when ZINVALIDCALLBACK then InvalidCallback
121
+ when ZINVALIDACL then InvalidACL
122
+ when ZAUTHFAILED then AuthFailed
123
+ when ZCLOSING then Closing
124
+ when ZNOTHING then Nothing
125
+ when ZSESSIONMOVED then SessionMoved
126
+ else ZookeeperException.new("no exception defined for code #{code}")
127
+ end
128
+ end
129
+
130
+ def self.raise_on_error(code)
131
+ exc = self.by_code(code)
132
+ raise exc unless exc == EverythingOk
133
+ end
134
+ end # Exceptions
135
+ end # Zookeeper