zk-server 1.0.1 → 1.1.0
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/bin/zk-server +7 -0
- data/lib/zk-server/base.rb +13 -18
- data/lib/zk-server/cluster.rb +96 -0
- data/lib/zk-server/command.rb +69 -0
- data/lib/zk-server/config.rb +174 -7
- data/lib/zk-server/sub_process.rb +36 -9
- data/lib/zk-server/version.rb +1 -1
- data/lib/zk-server.rb +4 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/wait_watchers.rb +55 -0
- data/spec/zk-server/cluster_spec.rb +34 -0
- data/zk-server.gemspec +1 -0
- metadata +30 -7
data/bin/zk-server
ADDED
data/lib/zk-server/base.rb
CHANGED
@@ -7,10 +7,10 @@ module ZK
|
|
7
7
|
include Logging
|
8
8
|
|
9
9
|
def_delegators :config,
|
10
|
-
:base_dir, :data_dir, :log4j_props_path, :
|
11
|
-
:
|
12
|
-
:
|
13
|
-
|
10
|
+
:base_dir, :data_dir, :log4j_props_path, :command_args,
|
11
|
+
:client_port, :stdio_redirect_path, :zoo_cfg_path,
|
12
|
+
:zoo_myid_path, :myid
|
13
|
+
|
14
14
|
# the {Config} object that will be used to configure this Process
|
15
15
|
attr_accessor :config
|
16
16
|
|
@@ -18,7 +18,7 @@ module ZK
|
|
18
18
|
@child_startup_timeout = opts.delete(:child_startup_timeout) || 6
|
19
19
|
|
20
20
|
@run_called = false
|
21
|
-
@config = Config.new(opts)
|
21
|
+
@config = opts[:config] || Config.new(opts)
|
22
22
|
|
23
23
|
@mutex = Monitor.new
|
24
24
|
@exit_cond = @mutex.new_cond
|
@@ -69,6 +69,7 @@ module ZK
|
|
69
69
|
def create_files!
|
70
70
|
mkdir_p base_dir
|
71
71
|
mkdir_p data_dir
|
72
|
+
write_myid!
|
72
73
|
write_zoo_cfg!
|
73
74
|
write_log4j_properties!
|
74
75
|
mkdir_p(File.dirname(stdio_redirect_path))
|
@@ -80,21 +81,15 @@ module ZK
|
|
80
81
|
end
|
81
82
|
end
|
82
83
|
|
84
|
+
def write_myid!
|
85
|
+
File.open(zoo_myid_path, 'w') do |io|
|
86
|
+
io.puts myid
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
83
90
|
def write_zoo_cfg!
|
84
91
|
File.open(zoo_cfg_path, 'w') do |fp|
|
85
|
-
fp.puts
|
86
|
-
tickTime=#{tick_time}
|
87
|
-
dataDir=#{data_dir}
|
88
|
-
clientPort=#{client_port}
|
89
|
-
maxClientCnxns=#{max_client_cnxns}
|
90
|
-
EOS
|
91
|
-
|
92
|
-
fp.puts("forceSync=#{force_sync}") if force_sync
|
93
|
-
fp.puts("snapCount=#{snap_count}") if snap_count
|
94
|
-
zoo_cfg_hash.each do |k,v|
|
95
|
-
fp.puts("#{k}=#{v}")
|
96
|
-
end
|
97
|
-
|
92
|
+
fp.puts(config.to_config_file_str)
|
98
93
|
fp.fsync
|
99
94
|
end
|
100
95
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ZK
|
2
|
+
module Server
|
3
|
+
# Cluster simplifies the case when you want to test a 3+ node cluster
|
4
|
+
# You give the base_dir, and number of nodes, and it will construct a
|
5
|
+
# cluster for you. It's less configurable than the SubProcess class, but
|
6
|
+
# that's the price of convenience!
|
7
|
+
class Cluster
|
8
|
+
FOLLOWER_PORT_OFFSET = 100
|
9
|
+
LEADER_PORT_OFFSET = 200
|
10
|
+
DEFAULT_BASE_PORT = 21811
|
11
|
+
|
12
|
+
attr_accessor :base_dir
|
13
|
+
|
14
|
+
# defaults to 21811, used as the lowest port number for the cluster,
|
15
|
+
# all others will be offsets of this number
|
16
|
+
attr_accessor :base_port
|
17
|
+
|
18
|
+
# access to the SubProcess instances that make up this cluster
|
19
|
+
attr_reader :processes
|
20
|
+
|
21
|
+
# how many nodes in the cluster
|
22
|
+
attr_reader :num_members
|
23
|
+
|
24
|
+
def initialize(num_members, opts={})
|
25
|
+
@num_members = num_members
|
26
|
+
@base_dir = Config.default_base_dir
|
27
|
+
@base_port = DEFAULT_BASE_PORT
|
28
|
+
@processes = nil
|
29
|
+
@running = false
|
30
|
+
opts.each { |k,v| __send__(:"#{k}=", v) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def running?
|
34
|
+
!!@running
|
35
|
+
end
|
36
|
+
|
37
|
+
def run
|
38
|
+
return if running?
|
39
|
+
@running = true
|
40
|
+
|
41
|
+
processes.each { |p| p.run }
|
42
|
+
rescue Exception
|
43
|
+
processes.each { |p| p.shutdown }
|
44
|
+
raise
|
45
|
+
end
|
46
|
+
|
47
|
+
def shutdown
|
48
|
+
return unless running?
|
49
|
+
@running = false
|
50
|
+
|
51
|
+
pary, @processes = @processes, nil
|
52
|
+
|
53
|
+
pary.each(&:shutdown)
|
54
|
+
pary
|
55
|
+
end
|
56
|
+
|
57
|
+
def clobber!
|
58
|
+
processes.each(&:clobber!)
|
59
|
+
end
|
60
|
+
|
61
|
+
def all_running?
|
62
|
+
processes.all?(&:running?)
|
63
|
+
end
|
64
|
+
|
65
|
+
def ping_all?
|
66
|
+
processes.all?(&:ping?)
|
67
|
+
end
|
68
|
+
|
69
|
+
def processes
|
70
|
+
@processes ||= [].tap do |ary|
|
71
|
+
num_members.times do |idx|
|
72
|
+
ary << SubProcess.new.tap do |sp|
|
73
|
+
c = sp.config
|
74
|
+
|
75
|
+
c.myid = idx
|
76
|
+
c.base_dir = File.join(@base_dir, "server-#{idx}")
|
77
|
+
c.client_port = base_port + idx
|
78
|
+
c.zoo_cfg_hash = server_hash
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# terrible name, the list of 'servers' lines in the config
|
85
|
+
#
|
86
|
+
def server_hash
|
87
|
+
@server_hash ||= {}.tap do |h|
|
88
|
+
num_members.times do |idx|
|
89
|
+
h["server.#{idx}"] = "127.0.0.1:#{base_port + FOLLOWER_PORT_OFFSET + idx}:#{base_port + LEADER_PORT_OFFSET + idx}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end # Cluster
|
95
|
+
end # Server
|
96
|
+
end # ZK
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module ZK
|
4
|
+
module Server
|
5
|
+
class Command
|
6
|
+
include Logging
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@mutex = Mutex.new
|
10
|
+
@cond = ConditionVariable.new
|
11
|
+
@shutdown_requested = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def spawn_shutdown_handler
|
15
|
+
@shutdown_thread ||= Thread.new do
|
16
|
+
@mutex.synchronize do
|
17
|
+
@cond.wait until @shutdown_requested
|
18
|
+
logger.debug { "shutdown thread awakened! shutting down server!" }
|
19
|
+
@server.shutdown if @server
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
opts = Slop.parse(:help => true, :strict => true) do
|
26
|
+
banner "zk-server [opts] runs a ZooKeeper server in the foreground"
|
27
|
+
on :d, :base_dir=, "defaults to #{ZK::Server::Config.default_base_dir}"
|
28
|
+
on :force_sync, 'force fsync on every snapshot'
|
29
|
+
on :skip_acl, 'skip acl checks'
|
30
|
+
on :p, :port=, 'port to listen on', :as => :integer
|
31
|
+
on :jvm_flags=, 'additional JVM flags to pass'
|
32
|
+
on :snap_count=, 'how often to take a snapshot, default 100_000', :as => :integer
|
33
|
+
end
|
34
|
+
|
35
|
+
return if opts.help?
|
36
|
+
|
37
|
+
hash = opts.to_hash
|
38
|
+
|
39
|
+
hash.delete(:help)
|
40
|
+
hash.reject! { |k,v| v.nil? }
|
41
|
+
|
42
|
+
config = ZK::Server::Config.new(hash)
|
43
|
+
|
44
|
+
spawn_shutdown_handler
|
45
|
+
|
46
|
+
|
47
|
+
%w[HUP INT].each do |sig|
|
48
|
+
trap(sig) do
|
49
|
+
@mutex.synchronize do
|
50
|
+
$stderr.puts "trapped #{sig}, shutting down"
|
51
|
+
@shutdown_requested = true
|
52
|
+
@cond.broadcast
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@server = ZK::Server.new(:config => config)
|
58
|
+
@server.run
|
59
|
+
|
60
|
+
unless @server.join
|
61
|
+
$stderr.puts "server exited with status #{@server.status.inspect}"
|
62
|
+
st = @server.status
|
63
|
+
exit st.exited? ? st.exitstatus : 42
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
data/lib/zk-server/config.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
module ZK
|
2
2
|
module Server
|
3
|
+
# Note that this supports all of the 3.3.5 options, but __will not__ do any
|
4
|
+
# sanity checking for you. All options specifyable here (especially those that
|
5
|
+
# require directories to be created outside of {#base_dir}) may not be handled
|
6
|
+
# properly by {Base} and subclasses.
|
7
|
+
#
|
3
8
|
class Config
|
4
9
|
DEFAULT_JVM_FLAGS = %w[
|
5
|
-
-server
|
10
|
+
-server
|
6
11
|
-Xmx256m
|
7
|
-
-Dzookeeper.serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
|
12
|
+
-Dzookeeper.serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
|
8
13
|
].freeze
|
9
14
|
|
10
15
|
# the com.sun.managemnt.jmxremote.port arg will be filled in dynamically
|
@@ -23,12 +28,32 @@ module ZK
|
|
23
28
|
# basis for all other path generation. Defaults to `File.join(Dir.getwd, 'zookeeper')`
|
24
29
|
attr_accessor :base_dir
|
25
30
|
|
31
|
+
# defaults to `#{base_dir}/data`. use this to override the default
|
32
|
+
#
|
33
|
+
# > the location where ZooKeeper will store the in-memory database
|
34
|
+
# > snapshots and, unless specified otherwise, the transaction log of
|
35
|
+
# > updates to the database.
|
36
|
+
#
|
37
|
+
# be aware that the {ZK::Server::Base} class will not create any
|
38
|
+
# directories but {#base_dir}.
|
39
|
+
#
|
40
|
+
attr_writer :data_dir
|
41
|
+
|
42
|
+
# defaults to nil, and zookeeper will just use the default value (being the
|
43
|
+
# same as {#data_dir}
|
44
|
+
#
|
45
|
+
# > This option will direct the machine to write the transaction log to the
|
46
|
+
# > dataLogDir rather than the dataDir. This allows a dedicated log device
|
47
|
+
# > to be used, and helps avoid competition between logging and snaphots.
|
48
|
+
#
|
49
|
+
attr_accessor :data_log_dir
|
50
|
+
|
26
51
|
# a hash that will be used to provide extra values for the zoo.cfg file.
|
27
|
-
# keys are written as-is to the file, so they should be camel-cased.
|
52
|
+
# keys are written as-is to the file, so they should be camel-cased.
|
28
53
|
#
|
29
54
|
# dataDir will be set relative to {#base_dir} and clientPort will either use
|
30
55
|
# the default of 2181, or can be adjusted by {#client_port=}
|
31
|
-
#
|
56
|
+
#
|
32
57
|
attr_accessor :zoo_cfg_hash
|
33
58
|
|
34
59
|
# what port should the server listen on for connections? (default 2181)
|
@@ -42,6 +67,36 @@ module ZK
|
|
42
67
|
# defaults to 2000
|
43
68
|
attr_accessor :tick_time
|
44
69
|
|
70
|
+
# what cluster id should this zookeeper use for itself? (default 1)
|
71
|
+
attr_accessor :myid
|
72
|
+
|
73
|
+
# default is nil, and will not be specified in the config
|
74
|
+
#
|
75
|
+
# > Clients can submit requests faster than ZooKeeper can process them,
|
76
|
+
# > especially if there are a lot of clients. To prevent ZooKeeper from
|
77
|
+
# > running out of memory due to queued requests, ZooKeeper will throttle
|
78
|
+
# > clients so that there is no more than globalOutstandingLimit
|
79
|
+
# > outstanding requests in the system. The default limit is 1,000.
|
80
|
+
#
|
81
|
+
attr_accessor :global_outstanding_limit
|
82
|
+
|
83
|
+
# necessary for cluster nodes (default 5)
|
84
|
+
#
|
85
|
+
# from the zookeeper admin guide:
|
86
|
+
# > Amount of time, in ticks (see tickTime), to allow followers to connect
|
87
|
+
# > and sync to a leader. Increased this value as needed, if the amount of data
|
88
|
+
# > managed by ZooKeeper is large.
|
89
|
+
#
|
90
|
+
attr_accessor :init_limit
|
91
|
+
|
92
|
+
# necessary for cluster nodes (default 2)
|
93
|
+
#
|
94
|
+
# > Amount of time, in ticks (see tickTime), to allow followers to sync
|
95
|
+
# > with ZooKeeper. If followers fall too far behind a leader, they will be
|
96
|
+
# > dropped.
|
97
|
+
#
|
98
|
+
attr_accessor :sync_limit
|
99
|
+
|
45
100
|
# from the [admin guide](http://zookeeper.apache.org/doc/r3.3.5/zookeeperAdmin.html)
|
46
101
|
#
|
47
102
|
# > ZooKeeper logs transactions to a transaction log. After snapCount
|
@@ -50,7 +105,7 @@ module ZK
|
|
50
105
|
#
|
51
106
|
# For testing, to speed up disk IO, I generally set this to 1_000_000 and
|
52
107
|
# force_sync to false. YMMV, understand what this does before messing with it
|
53
|
-
# if you care about your data.
|
108
|
+
# if you care about your data.
|
54
109
|
#
|
55
110
|
# default: unset
|
56
111
|
attr_accessor :snap_count
|
@@ -59,9 +114,74 @@ module ZK
|
|
59
114
|
# an fsync() call for each snapshot write. It is however DANGEROUS if you
|
60
115
|
# care about the data. (I set it to false for running tests)
|
61
116
|
#
|
117
|
+
# if true: 'yes', false: 'no', nil not specified in the config
|
118
|
+
#
|
62
119
|
# default: no value set
|
63
120
|
attr_accessor :force_sync
|
64
121
|
|
122
|
+
# default: nil
|
123
|
+
#
|
124
|
+
# > To avoid seeks ZooKeeper allocates space in the transaction log file in
|
125
|
+
# > blocks of preAllocSize kilobytes. The default block size is 64M. One
|
126
|
+
# > reason for changing the size of the blocks is to reduce the block size
|
127
|
+
# > if snapshots are taken more often. (Also, see snapCount).
|
128
|
+
attr_accessor :pre_alloc_size
|
129
|
+
|
130
|
+
# default: nil
|
131
|
+
#
|
132
|
+
# > the address (ipv4, ipv6 or hostname) to listen for client
|
133
|
+
# > connections; that is, the address that clients attempt to connect to.
|
134
|
+
# > This is optional, by default we bind in such a way that any connection
|
135
|
+
# > to the clientPort for any address/interface/nic on the server will be
|
136
|
+
# > accepted.
|
137
|
+
#
|
138
|
+
attr_accessor :client_port_address
|
139
|
+
|
140
|
+
# default: nil
|
141
|
+
#
|
142
|
+
# > the minimum session timeout in milliseconds that the server will allow
|
143
|
+
# > the client to negotiate. Defaults to 2 times the tickTime
|
144
|
+
#
|
145
|
+
attr_accessor :min_session_timeout
|
146
|
+
|
147
|
+
# default: nil
|
148
|
+
#
|
149
|
+
# > the maximum session timeout in milliseconds that the server will allow
|
150
|
+
# > the client to negotiate. Defaults to 20 times the tickTime.
|
151
|
+
attr_accessor :max_session_timeout
|
152
|
+
|
153
|
+
# default: nil
|
154
|
+
#
|
155
|
+
# If true, the value 'yes' will be used for this value, false will write 'no'
|
156
|
+
# to the config file. The default (nil) is not to specify a value in the config.
|
157
|
+
#
|
158
|
+
# > Leader accepts client connections. Default value is "yes". The leader
|
159
|
+
# > machine coordinates updates. For higher update throughput at thes
|
160
|
+
# > slight expense of read throughput the leader can be configured to not
|
161
|
+
# > accept clients and focus on coordination. The default to this option is
|
162
|
+
# > yes, which means that a leader will accept client connections
|
163
|
+
#
|
164
|
+
attr_accessor :leader_serves
|
165
|
+
|
166
|
+
# default: nil
|
167
|
+
#
|
168
|
+
# by default this is not specified
|
169
|
+
#
|
170
|
+
# > Sets the timeout value for opening connections for leader election
|
171
|
+
# > notifications.
|
172
|
+
#
|
173
|
+
attr_accessor :cnx_timeout
|
174
|
+
|
175
|
+
# default: nil
|
176
|
+
#
|
177
|
+
# if true: 'yes', false: 'no', nil not specified in the config
|
178
|
+
#
|
179
|
+
# this is listed as a DANGEROUS setting
|
180
|
+
#
|
181
|
+
# > Skips ACL checks. This results in a boost in throughput, but opens up
|
182
|
+
# > full access to the data tree to everyone.
|
183
|
+
attr_accessor :skip_acl
|
184
|
+
|
65
185
|
# if truthy, will enable jmx (defaults to false)
|
66
186
|
# note that our defualt jmx config has all security and auth turned off
|
67
187
|
# if you want to customize this, then use jvm_flags and set this to false
|
@@ -75,8 +195,13 @@ module ZK
|
|
75
195
|
# default is {DEEFAULT_JVM_FLAGS}
|
76
196
|
attr_accessor :jvm_flags
|
77
197
|
|
198
|
+
def self.default_base_dir
|
199
|
+
File.join(Dir.getwd, 'zookeeper')
|
200
|
+
end
|
201
|
+
|
78
202
|
def initialize(opts={})
|
79
|
-
|
203
|
+
$stderr.puts "#{self.class}#initialize #{opts.inspect}"
|
204
|
+
@base_dir = self.class.default_base_dir
|
80
205
|
@zoo_cfg_hash = {}
|
81
206
|
@tick_time = 2000
|
82
207
|
@client_port = 2181
|
@@ -85,6 +210,9 @@ module ZK
|
|
85
210
|
@jmx_port = 22222
|
86
211
|
@enable_jmx = false
|
87
212
|
@jvm_flags = DEFAULT_JVM_FLAGS.dup
|
213
|
+
@myid = 1
|
214
|
+
@init_limit = 5
|
215
|
+
@sync_limit = 2
|
88
216
|
|
89
217
|
@max_client_cnxns = 100
|
90
218
|
|
@@ -96,6 +224,11 @@ module ZK
|
|
96
224
|
File.join(base_dir, 'zoo.cfg')
|
97
225
|
end
|
98
226
|
|
227
|
+
# @private
|
228
|
+
def zoo_myid_path
|
229
|
+
File.join(base_dir, 'data', 'myid')
|
230
|
+
end
|
231
|
+
|
99
232
|
# @private
|
100
233
|
def log4j_props_path
|
101
234
|
File.join(base_dir, 'log4j.properties')
|
@@ -113,7 +246,7 @@ module ZK
|
|
113
246
|
|
114
247
|
# @private
|
115
248
|
def data_dir
|
116
|
-
File.join(base_dir, 'data')
|
249
|
+
@data_dir ||= File.join(base_dir, 'data')
|
117
250
|
end
|
118
251
|
|
119
252
|
# @private
|
@@ -132,6 +265,40 @@ module ZK
|
|
132
265
|
cmd += jvm_flags
|
133
266
|
cmd += %W[-cp #{classpath.join(':')} #{ZOO_MAIN} #{zoo_cfg_path}]
|
134
267
|
end
|
268
|
+
|
269
|
+
# renders this config as a string that can be written to zoo.cfg
|
270
|
+
def to_config_file_str
|
271
|
+
config = {
|
272
|
+
'dataDir' => data_dir,
|
273
|
+
'skipACL' => skip_acl,
|
274
|
+
'tickTime' => tick_time,
|
275
|
+
'initLimit' => init_limit,
|
276
|
+
'syncLimit' => sync_limit,
|
277
|
+
'forceSync' => force_sync,
|
278
|
+
'snapCount' => snap_count,
|
279
|
+
'clientPort' => client_port,
|
280
|
+
'dataLogDir' => data_log_dir,
|
281
|
+
'preAllocSize' => pre_alloc_size,
|
282
|
+
'leaderServes' => leader_serves,
|
283
|
+
'maxClientCnxns' => max_client_cnxns,
|
284
|
+
'clientPortAddress' => client_port_address,
|
285
|
+
'minSessionTimeout' => min_session_timeout,
|
286
|
+
'maxSessionTimeout' => max_session_timeout,
|
287
|
+
'globalOutstandingLimit' => global_outstanding_limit,
|
288
|
+
}
|
289
|
+
|
290
|
+
config = config.merge(zoo_cfg_hash)
|
291
|
+
|
292
|
+
config.delete_if { |k,v| v.nil? }
|
293
|
+
|
294
|
+
%w[leaderServes skipACL forceSync].each do |yorn_key|
|
295
|
+
if config.has_key?(yorn_key)
|
296
|
+
config[yorn_key] = config[yorn_key] ? 'yes' : 'no'
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
config.sort.map {|kv| kv.join("=") }.join("\n")
|
301
|
+
end
|
135
302
|
end
|
136
303
|
end
|
137
304
|
end
|
@@ -8,7 +8,7 @@ module ZK
|
|
8
8
|
# to store our data under (configurable).
|
9
9
|
#
|
10
10
|
class SubProcess < Base
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :status
|
12
12
|
|
13
13
|
# how long should we wait for the child to start responding to 'ruok'?
|
14
14
|
attr_accessor :child_startup_timeout
|
@@ -17,14 +17,28 @@ module ZK
|
|
17
17
|
@exit_watching_thread = nil
|
18
18
|
|
19
19
|
@pid = nil
|
20
|
-
@
|
20
|
+
@status = nil
|
21
21
|
|
22
22
|
super
|
23
23
|
end
|
24
24
|
|
25
|
+
# wait for sub-process to exit
|
26
|
+
def join
|
27
|
+
@mutex.synchronize do
|
28
|
+
logger.debug { "spawned process #{@pid}" }
|
29
|
+
@exit_cond.wait_until { @status }
|
30
|
+
end
|
31
|
+
@status.success?
|
32
|
+
end
|
33
|
+
|
25
34
|
# true if the process was started and is still running
|
26
35
|
def running?
|
27
|
-
spawned? and !@
|
36
|
+
spawned? and !@status and alive?
|
37
|
+
end
|
38
|
+
|
39
|
+
def alive?
|
40
|
+
return false unless spawned?
|
41
|
+
false|kill(0)
|
28
42
|
rescue Errno::ESRCH
|
29
43
|
false
|
30
44
|
end
|
@@ -37,16 +51,16 @@ module ZK
|
|
37
51
|
# shutdown the child, wait for it to exit, ensure it is dead
|
38
52
|
def shutdown
|
39
53
|
if @pid
|
40
|
-
return if @
|
54
|
+
return if @status
|
41
55
|
|
42
56
|
@mutex.synchronize do
|
43
57
|
%w[HUP TERM KILL].each do |signal|
|
44
58
|
logger.debug { "sending #{signal} to #{@pid}" }
|
45
59
|
|
46
|
-
return unless running? # jruby doesn't seem to get @
|
60
|
+
return unless running? # jruby doesn't seem to get @status ?
|
47
61
|
|
48
62
|
begin
|
49
|
-
|
63
|
+
kill(signal)
|
50
64
|
rescue Errno::ESRCH
|
51
65
|
return true
|
52
66
|
end
|
@@ -55,7 +69,13 @@ module ZK
|
|
55
69
|
end
|
56
70
|
end
|
57
71
|
|
58
|
-
|
72
|
+
unless @exit_watching_thread.join(3) == @exit_watching_thread
|
73
|
+
logger.warn { "exit watching thread didn't exit after 3 sec" }
|
74
|
+
end
|
75
|
+
|
76
|
+
@pid = @exit_watching_thread = nil
|
77
|
+
|
78
|
+
logger.debug { "@status: #{@status}" }
|
59
79
|
end
|
60
80
|
true
|
61
81
|
end
|
@@ -65,6 +85,11 @@ module ZK
|
|
65
85
|
@pid
|
66
86
|
end
|
67
87
|
|
88
|
+
def kill(signal)
|
89
|
+
raise "No pid yet!" unless @pid
|
90
|
+
Process.kill(signal, @pid)
|
91
|
+
end
|
92
|
+
|
68
93
|
# start the child, using the {#config}. we create the files necessary,
|
69
94
|
# fork the child, and wait 5s for the child to start responding to pings
|
70
95
|
#
|
@@ -99,7 +124,7 @@ module ZK
|
|
99
124
|
protected
|
100
125
|
def spawn_exit_watching_thread
|
101
126
|
@exit_watching_thread ||= Thread.new do
|
102
|
-
_, @
|
127
|
+
_, @status = Process.wait2(@pid)
|
103
128
|
@mutex.synchronize do
|
104
129
|
@exit_cond.broadcast
|
105
130
|
end
|
@@ -124,7 +149,9 @@ module ZK
|
|
124
149
|
@pid ||= (
|
125
150
|
args = command_args()
|
126
151
|
args << { :err => [:child, :out], :out => [stdio_redirect_path, 'w'] }
|
127
|
-
::Kernel.spawn({}, *command_args)
|
152
|
+
::Kernel.spawn({}, *command_args).tap do |pid|
|
153
|
+
logger.debug { "Spawned process #{pid}" }
|
154
|
+
end
|
128
155
|
)
|
129
156
|
end
|
130
157
|
|
data/lib/zk-server/version.rb
CHANGED
data/lib/zk-server.rb
CHANGED
@@ -9,6 +9,8 @@ require 'socket'
|
|
9
9
|
|
10
10
|
Bundler.require
|
11
11
|
|
12
|
+
require 'slop'
|
13
|
+
|
12
14
|
# require 'zk'
|
13
15
|
|
14
16
|
#ZK.logger = Logger.new($stderr).tap { |l| l.level = Logger::DEBUG }
|
@@ -130,6 +132,8 @@ require 'zk-server/logging'
|
|
130
132
|
require 'zk-server/config'
|
131
133
|
require 'zk-server/base'
|
132
134
|
require 'zk-server/sub_process'
|
135
|
+
require 'zk-server/cluster'
|
136
|
+
require 'zk-server/command'
|
133
137
|
|
134
138
|
if defined?(::JRUBY_VERSION)
|
135
139
|
require 'zk-server/java_embedded'
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
module WaitWatchers
|
2
|
+
class TimeoutError < StandardError; end
|
3
|
+
|
4
|
+
# method to wait until block passed returns truthy (false will not work) or
|
5
|
+
# timeout (default is 2 seconds) is reached raises TiemoutError on timeout
|
6
|
+
#
|
7
|
+
# @returns the truthy value
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# @a = nil
|
12
|
+
#
|
13
|
+
# th = Thread.new do
|
14
|
+
# sleep(1)
|
15
|
+
# @a = :fudge
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# wait_until(2) { @a }.should == :fudge
|
19
|
+
#
|
20
|
+
def wait_until(timeout=2)
|
21
|
+
if ENV['TRAVIS'] and timeout and timeout < 5
|
22
|
+
timeout = 5
|
23
|
+
end
|
24
|
+
|
25
|
+
time_to_stop = Time.now + timeout
|
26
|
+
while true
|
27
|
+
rval = yield
|
28
|
+
return rval if rval
|
29
|
+
raise TimeoutError, "timeout of #{timeout}s exceeded" if Time.now > time_to_stop
|
30
|
+
Thread.pass
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# inverse of wait_until
|
35
|
+
def wait_while(timeout=2)
|
36
|
+
if ENV['travis'] and timeout and timeout < 5
|
37
|
+
timeout = 5
|
38
|
+
end
|
39
|
+
|
40
|
+
time_to_stop = Time.now + timeout
|
41
|
+
while true
|
42
|
+
rval = yield
|
43
|
+
return rval unless rval
|
44
|
+
raise TimeoutError, "timeout of #{timeout}s exceeded" if Time.now > time_to_stop
|
45
|
+
Thread.pass
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def report_realtime(what)
|
50
|
+
return yield
|
51
|
+
t = Benchmark.realtime { yield }
|
52
|
+
$stderr.puts "#{what}: %0.3f" % [t.to_f]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ZK::Server::Cluster do
|
4
|
+
let(:tmpdir) { '/tmp/zookeeper' }
|
5
|
+
|
6
|
+
let(:num_members) { 5 }
|
7
|
+
|
8
|
+
subject do
|
9
|
+
described_class.new(num_members, :base_dir => tmpdir)
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
subject.shutdown
|
14
|
+
# subject.clobber!
|
15
|
+
# FileUtils.rm_rf(tmpdir)
|
16
|
+
end
|
17
|
+
|
18
|
+
it %[should spawn a ZK server, ping, and then shutdown properly] do
|
19
|
+
pending "cannot run this under JRuby" if defined?(::JRUBY_VERSION)
|
20
|
+
|
21
|
+
subject.run
|
22
|
+
|
23
|
+
wait_until { subject.all_running? }
|
24
|
+
|
25
|
+
subject.should be_ping_all
|
26
|
+
subject.should be_running
|
27
|
+
|
28
|
+
subject.shutdown
|
29
|
+
|
30
|
+
subject.should_not be_ping_all
|
31
|
+
subject.should_not be_running
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/zk-server.gemspec
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.add_runtime_dependency 'bundler', '~> 1.1.3'
|
12
12
|
s.add_runtime_dependency 'slyphon-log4j', '= 1.2.15'
|
13
13
|
s.add_runtime_dependency 'slyphon-zookeeper_jar', '~> 3.3.5'
|
14
|
+
s.add_runtime_dependency 'slop', '~> 3.2.0'
|
14
15
|
|
15
16
|
s.files = `git ls-files`.split($\)
|
16
17
|
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zk-server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
- 0
|
9
8
|
- 1
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jonathan D. Simms
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-05-
|
18
|
+
date: 2012-05-16 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: bundler
|
@@ -65,11 +65,27 @@ dependencies:
|
|
65
65
|
version: 3.3.5
|
66
66
|
type: :runtime
|
67
67
|
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: slop
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 15
|
77
|
+
segments:
|
78
|
+
- 3
|
79
|
+
- 2
|
80
|
+
- 0
|
81
|
+
version: 3.2.0
|
82
|
+
type: :runtime
|
83
|
+
version_requirements: *id004
|
68
84
|
description: runs a standalone zookeeper server
|
69
85
|
email:
|
70
86
|
- slyphon@gmail.com
|
71
|
-
executables:
|
72
|
-
|
87
|
+
executables:
|
88
|
+
- zk-server
|
73
89
|
extensions: []
|
74
90
|
|
75
91
|
extra_rdoc_files: []
|
@@ -83,8 +99,11 @@ files:
|
|
83
99
|
- MIT_LICENSE
|
84
100
|
- README.md
|
85
101
|
- Rakefile
|
102
|
+
- bin/zk-server
|
86
103
|
- lib/zk-server.rb
|
87
104
|
- lib/zk-server/base.rb
|
105
|
+
- lib/zk-server/cluster.rb
|
106
|
+
- lib/zk-server/command.rb
|
88
107
|
- lib/zk-server/config.rb
|
89
108
|
- lib/zk-server/java_embedded.rb
|
90
109
|
- lib/zk-server/log4j.properties
|
@@ -92,6 +111,8 @@ files:
|
|
92
111
|
- lib/zk-server/sub_process.rb
|
93
112
|
- lib/zk-server/version.rb
|
94
113
|
- spec/spec_helper.rb
|
114
|
+
- spec/support/wait_watchers.rb
|
115
|
+
- spec/zk-server/cluster_spec.rb
|
95
116
|
- spec/zk-server/java_embedded_spec.rb
|
96
117
|
- spec/zk-server/process_spec.rb
|
97
118
|
- zk-server.gemspec
|
@@ -124,11 +145,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
145
|
requirements: []
|
125
146
|
|
126
147
|
rubyforge_project:
|
127
|
-
rubygems_version: 1.8.
|
148
|
+
rubygems_version: 1.8.24
|
128
149
|
signing_key:
|
129
150
|
specification_version: 3
|
130
151
|
summary: runs a standalone zookeeper server
|
131
152
|
test_files:
|
132
153
|
- spec/spec_helper.rb
|
154
|
+
- spec/support/wait_watchers.rb
|
155
|
+
- spec/zk-server/cluster_spec.rb
|
133
156
|
- spec/zk-server/java_embedded_spec.rb
|
134
157
|
- spec/zk-server/process_spec.rb
|