ruby_skynet 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3fd7fd4822334e7d782d4dea66adfebee81ed5e6
4
- data.tar.gz: df2655971d950f5672beadbbe324e866b24ef754
3
+ metadata.gz: db463c6efbc6b84b670681ac67fd39358dcdcfd1
4
+ data.tar.gz: 6f18c09efa035cc23435b5a29a8a16c75dcd7437
5
5
  SHA512:
6
- metadata.gz: c40532f7c7ba9710f1aa48ee5f25fa1a6c1e0a4a1e2fb42d265309abb959e02a0dc1d40c5eada990cb0907898a04806540a1a39d3cd02e1bb2e2065ef12a4325
7
- data.tar.gz: 1d877ccc14a6a71bd708161722e45aefbd5b529f9fc62ad6a20f4f7d393d231e4d9f808a849033b8cbfe7a79ecafe0065a65315921c34023c458beaf601a727c
6
+ metadata.gz: e763b084264b1073831234160c328f9e594d0d33c228240b7487bac01c5fcb9422fd20910a0e3beeca0a38d5cb5f7e1c8521bb49c7751bdbc790bc974c815cb8
7
+ data.tar.gz: 41976bc92888af6ba2d1e20d75b5babac0d1b5b03b0f38da10489d2e4e9121557bca532c1e9f1ca1f76e8c2adbcc05e6c1af331ae77edd5d0782304bde56bf5f
data/Gemfile.lock CHANGED
@@ -7,20 +7,18 @@ GEM
7
7
  multi_json (~> 1.3)
8
8
  thread_safe (~> 0.1)
9
9
  tzinfo (~> 0.3.37)
10
- atomic (1.1.10)
11
- atomic (1.1.10-java)
12
- bson (1.9.0)
13
- bson (1.9.0-java)
14
- bson_ext (1.9.0)
15
- bson (~> 1.9.0)
10
+ atomic (1.1.13)
11
+ bson (1.9.1)
12
+ bson_ext (1.9.1)
13
+ bson (~> 1.9.1)
16
14
  gene_pool (1.3.0)
17
- i18n (0.6.4)
15
+ i18n (0.6.5)
18
16
  minitest (4.7.5)
19
- multi_json (1.7.7)
17
+ multi_json (1.7.9)
20
18
  rake (10.1.0)
21
19
  resilient_socket (0.5.0)
22
20
  semantic_logger (>= 2.1)
23
- ruby_doozer (0.7.1)
21
+ ruby_doozer (0.8.1)
24
22
  gene_pool (>= 1.3.0)
25
23
  multi_json (>= 1.6.1)
26
24
  resilient_socket (>= 0.5.0)
@@ -28,28 +26,22 @@ GEM
28
26
  semantic_logger (>= 2.1)
29
27
  sync_attr (>= 1.0.0)
30
28
  ruby_protobuf (0.4.11)
31
- semantic_logger (2.1.0)
29
+ semantic_logger (2.2.0)
32
30
  sync_attr (>= 1.0)
33
31
  thread_safe (>= 0.1.0)
34
32
  shoulda (3.5.0)
35
33
  shoulda-context (~> 1.0, >= 1.0.1)
36
34
  shoulda-matchers (>= 1.4.1, < 3.0)
37
- shoulda-context (1.1.4)
38
- shoulda-matchers (2.2.0)
35
+ shoulda-context (1.1.5)
36
+ shoulda-matchers (2.3.0)
39
37
  activesupport (>= 3.0.0)
40
- slyphon-log4j (1.2.15)
41
- slyphon-zookeeper_jar (3.3.5-java)
42
38
  sync_attr (1.0.0)
43
- thread_safe (0.1.0)
39
+ thread_safe (0.1.2)
44
40
  atomic
45
41
  tzinfo (0.3.37)
46
42
  zookeeper (1.4.4)
47
- zookeeper (1.4.4-java)
48
- slyphon-log4j (= 1.2.15)
49
- slyphon-zookeeper_jar (= 3.3.5)
50
43
 
51
44
  PLATFORMS
52
- java
53
45
  ruby
54
46
 
55
47
  DEPENDENCIES
@@ -15,8 +15,8 @@ module RubySkynet #:nodoc:
15
15
  load "ruby_skynet/railties/ruby_skynet.rake"
16
16
  end
17
17
 
18
- # Load RubySkynet Configuration
19
- config.before_configuration do
18
+ # Load RubySkynet Configuration once rails has started
19
+ initializer 'ruby_skynet.initialize' do
20
20
  config_file = Rails.root.join("config", "ruby_skynet.yml")
21
21
  if config_file.file?
22
22
  ::RubySkynet.configure!(config_file, Rails.env)
@@ -20,14 +20,14 @@ namespace :ruby_skynet do
20
20
 
21
21
  RubySkynet::Server.load_services
22
22
 
23
- # Start the server
24
- RubySkynet::Server.start
25
-
26
- at_exit do
23
+ begin
24
+ # Start the server
25
+ RubySkynet::Server.start
26
+ RubySkynet::Server.wait_until_server_stops
27
+ ensure
27
28
  RubySkynet::Server.stop
28
29
  end
29
30
 
30
- RubySkynet::Server.wait_until_server_stops
31
31
  end
32
32
 
33
33
  end
@@ -98,10 +98,10 @@ module RubySkynet
98
98
 
99
99
  # Extract just the zookeeper or doozer configuration element
100
100
  RubySkynet.service_registry = ServiceRegistry.new(
101
- :registry => config[:registry]
101
+ :registry => config.delete(:registry)
102
102
  )
103
103
 
104
- config.each_pair {|k,v| RubySkynet::Server.logger.warn "Ignoring unknown RubySkynet config option #{k} => #{v}"}
104
+ config.each_pair {|k,v| warn "Ignoring unknown RubySkynet config option #{k} => #{v}"}
105
105
  end
106
106
 
107
107
  # Initialize internal class variable
@@ -1,3 +1,3 @@
1
1
  module RubySkynet #:nodoc
2
- VERSION = "1.1.1"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -37,6 +37,10 @@ module RubySkynet
37
37
  # :ephemeral [Boolean]
38
38
  # All set operations of non-nil values will result in ephemeral nodes.
39
39
  #
40
+ # :on_connect [Proc]
41
+ # Block to call after the connection to Zookeeper has been established
42
+ # and every time the connection is re-established
43
+ #
40
44
  # :registry [Hash|ZooKeeper]
41
45
  # ZooKeeper configuration information, or an existing
42
46
  # ZooKeeper ( ZooKeeper client) instance
@@ -72,19 +76,13 @@ module RubySkynet
72
76
  @root = '/' if @root == ''
73
77
 
74
78
  registry_config = params.delete(:registry) || {}
75
- if registry_config.is_a?(::Zookeeper::Client)
76
- @zookeeper = registry_config
77
- else
78
- servers = registry_config.delete(:servers) || ['127.0.0.1:2181']
79
- connect_timeout = (registry_config.delete(:connect_timeout) || 10).to_f
80
79
 
81
- # Generate warning log entries for any unknown configuration options
82
- registry_config.each_pair {|k,v| logger.warn "Ignoring unknown configuration option: zookeeper.#{k}"}
80
+ # server1:2181,server2:2181,server3:2181
81
+ @servers = (registry_config.delete(:servers) || ['127.0.0.1:2181']).join(',')
82
+ @connect_timeout = (registry_config.delete(:connect_timeout) || 10).to_f
83
83
 
84
- # Create Zookeeper connection
85
- # server1:2181,server2:2181,server3:2181
86
- @zookeeper = ::Zookeeper.new(servers.join(','), connect_timeout, watcher)
87
- end
84
+ # Generate warning log entries for any unknown configuration options
85
+ registry_config.each_pair {|k,v| logger.warn "Ignoring unknown configuration option: zookeeper.#{k}"}
88
86
 
89
87
  # Allow the serializer and deserializer implementations to be replaced
90
88
  @serializer = params.delete(:serializer) || RubySkynet::Zookeeper::Json::Serializer
@@ -93,18 +91,18 @@ module RubySkynet
93
91
  @ephemeral = params.delete(:ephemeral)
94
92
  @ephemeral = false if @ephemeral.nil?
95
93
 
94
+ @on_connect = params.delete(:on_connect)
95
+
96
96
  # Generate warning log entries for any unknown configuration options
97
97
  params.each_pair {|k,v| logger.warn "Ignoring unknown configuration option: #{k}"}
98
98
 
99
99
  # Hash with Array values containing the list of children for each node, if any
100
100
  @children = ThreadSafe::Hash.new
101
101
 
102
- # Start watching registry for any changes
103
- get_recursive(@root, watch=true, create_path=true, &block)
102
+ # Block is used in init
103
+ @block = block
104
104
 
105
- at_exit do
106
- close
107
- end
105
+ self.init
108
106
  end
109
107
 
110
108
  # Retrieve the latest value from a specific path from the registry
@@ -326,69 +324,83 @@ module RubySkynet
326
324
  end
327
325
  end
328
326
 
329
- # returns the watcher proc for this registry instances
327
+ # returns the watcher proc for this registry instance
330
328
  def watcher
331
329
  # Subscription block to call for watch events
332
330
  @watch_proc ||= Proc.new do |event_hash|
333
- path = event_hash[:path]
334
- case event_hash[:type]
335
- when ::Zookeeper::ZOO_CHANGED_EVENT
336
- logger.debug "Node '#{path}' Changed", event_hash
337
-
338
- # Fetch current value and re-subscribe
339
- result = @zookeeper.get(:path => path, :watcher => @watch_proc)
340
- check_rc(result)
341
- value = @deserializer.deserialize(result[:data])
342
- stat = result[:stat]
343
-
344
- # Invoke on_update callbacks
345
- node_updated(relative_key(path), value, stat.version)
346
-
347
- when ::Zookeeper::ZOO_DELETED_EVENT
348
- # A node has been deleted
349
- # TODO How to ignore child deleted when it is a directory, not a leaf
350
- logger.debug "Node '#{path}' Deleted", event_hash
351
- @children.delete(path)
352
- node_deleted(relative_key(path))
353
-
354
- when ::Zookeeper::ZOO_CHILD_EVENT
355
- # The list of nodes has changed - Does not say if it was added or removed
356
- logger.debug "Node '#{path}' Child changed", event_hash
357
- result = @zookeeper.get_children(:path => path, :watcher => @watch_proc)
358
-
359
- # This node could have been deleted already
360
- if result[:rc] == ::Zookeeper::ZOK
361
- current_children = result[:children]
362
- previous_children = @children[path]
363
-
364
- # Save children so that we can later identify new children
365
- @children[path] = current_children
366
-
367
- # New Child Nodes
368
- new_nodes = previous_children ? (current_children - previous_children) : current_children
369
- new_nodes.each do |child|
370
- get_recursive(File.join(path,child), true) do |key, value, version|
371
- node_created(key, value, version)
331
+ begin
332
+ path = event_hash[:path]
333
+ logger.trace "Event Received", event_hash
334
+ case event_hash[:type]
335
+ when ::Zookeeper::ZOO_CHANGED_EVENT
336
+ logger.debug "Node '#{path}' Changed", event_hash
337
+
338
+ # Fetch current value and re-subscribe
339
+ result = @zookeeper.get(:path => path, :watcher => @watch_proc)
340
+ check_rc(result)
341
+ value = @deserializer.deserialize(result[:data])
342
+ stat = result[:stat]
343
+
344
+ # Invoke on_update callbacks
345
+ node_updated(relative_key(path), value, stat.version)
346
+
347
+ when ::Zookeeper::ZOO_DELETED_EVENT
348
+ # A node has been deleted
349
+ # TODO How to ignore child deleted when it is a directory, not a leaf
350
+ logger.debug "Node '#{path}' Deleted", event_hash
351
+ @children.delete(path)
352
+ node_deleted(relative_key(path))
353
+
354
+ when ::Zookeeper::ZOO_CHILD_EVENT
355
+ # The list of nodes has changed - Does not say if it was added or removed
356
+ logger.debug "Node '#{path}' Child changed", event_hash
357
+ result = @zookeeper.get_children(:path => path, :watcher => @watch_proc)
358
+
359
+ # This node could have been deleted already
360
+ if result[:rc] == ::Zookeeper::ZOK
361
+ current_children = result[:children]
362
+ previous_children = @children[path]
363
+
364
+ # Save children so that we can later identify new children
365
+ @children[path] = current_children
366
+
367
+ # New Child Nodes
368
+ new_nodes = previous_children ? (current_children - previous_children) : current_children
369
+ new_nodes.each do |child|
370
+ get_recursive(File.join(path,child), true) do |key, value, version|
371
+ node_created(key, value, version)
372
+ end
372
373
  end
374
+ # Ignore Deleted Child Nodes since they will be handled by the Deleted Node event
373
375
  end
374
- # Ignore Deleted Child Nodes since they will be handled by the Deleted Node event
375
- end
376
376
 
377
- when ::Zookeeper::ZOO_CREATED_EVENT
378
- # Node created events are only created for paths that were deleted
379
- # and then created again
380
- # No op - This is covered by node_child created event
381
- logger.debug "Node '#{path}' Created - No op", event_hash
377
+ when ::Zookeeper::ZOO_CREATED_EVENT
378
+ # Node created events are only created for paths that were deleted
379
+ # and then created again
380
+ # No op - This is covered by node_child created event
381
+ logger.debug "Node '#{path}' Created - No op", event_hash
382
+
383
+ when ::Zookeeper::ZOO_SESSION_EVENT
384
+ logger.debug "Session Event: #{@zookeeper.state_by_value(event_hash[:state]) if @zookeeper}", event_hash
382
385
 
383
- when ::Zookeeper::ZOO_SESSION_EVENT
384
- logger.debug "Session Event: #{@zookeeper.state_by_value(event_hash[:state])}", event_hash
386
+ # Replace zookeeper connection since it is stale. Only react to global request
387
+ # since this event will be received for every node being watched.
388
+ # Do not close the current connection since this background watcher thread is running
389
+ # as part of the current zookeeper connection
390
+ # event_hash => {:req_id=>-1, :type=>-1, :state=>-112, :path=>"", :context=>nil}
391
+ Thread.new { self.init } if (event_hash[:req_id] == -1) && (event_hash[:state] == ::Zookeeper::ZOO_EXPIRED_SESSION_STATE)
385
392
 
386
- when ::Zookeeper::ZOO_NOTWATCHING_EVENT
387
- logger.debug "Ignoring ZOO_NOTWATCHING_EVENT", event_hash
393
+ when ::Zookeeper::ZOO_NOTWATCHING_EVENT
394
+ logger.debug "Ignoring ZOO_NOTWATCHING_EVENT", event_hash
388
395
 
389
- else
390
- # TODO Need to re-load registry when re-connected
391
- logger.warn "Ignoring unknown event", event_hash
396
+ else
397
+ # TODO Need to re-load registry when re-connected
398
+ logger.warn "Ignoring unknown event", event_hash
399
+ end
400
+ rescue ::Zookeeper::Exceptions::ZookeeperException => exc
401
+ logger.warn "Watching thread failed due to Zookeeper failure", exc
402
+ rescue Exception => exc
403
+ logger.error "Watching thread failed due to unhandled exception", exc
392
404
  end
393
405
  end
394
406
  end
@@ -511,6 +523,24 @@ module RubySkynet
511
523
  end
512
524
  end
513
525
 
526
+ # Create ZooKeeper connection and start watching the registry for any changes
527
+ def init
528
+ logger.benchmark_info "Connected to Zookeeper" do
529
+ @zookeeper.close if @zookeeper
530
+ # Create Zookeeper connection
531
+ @zookeeper = ::Zookeeper.new(@servers, @connect_timeout, watcher)
532
+ at_exit do
533
+ @zookeeper.close if @zookeeper
534
+ end
535
+
536
+ # Start watching registry for any changes
537
+ get_recursive(@root, watch=true, create_path=true, &@block)
538
+
539
+ # Call on_connect callback if supplied
540
+ @on_connect.call(self) if @on_connect
541
+ end
542
+ end
543
+
514
544
  end
515
545
  end
516
546
  end
@@ -23,9 +23,17 @@ module RubySkynet
23
23
  @cache = ThreadSafe::Hash.new
24
24
  @notifications_cache = ThreadSafe::Hash.new
25
25
 
26
+ # Keep a list of registered services so that they can be re-registered
27
+ # if the connection is lost
28
+ @services = ThreadSafe::Hash.new
29
+
26
30
  # Supply block to load the current keys from the Registry
27
31
  params[:root] = '/instances'
28
32
  params[:ephemeral] = true
33
+ params[:on_connect] = Proc.new do |registry|
34
+ # Re-Register services every time the connection to ZooKeeper is lost
35
+ @services.values.each {|v| register_service(*v)}
36
+ end
29
37
  @registry = Zookeeper::Registry.new(params) do |key, value|
30
38
  service_info_created(key, value)
31
39
  end
@@ -44,13 +52,14 @@ module RubySkynet
44
52
  # Returns the UUID for the service that was created
45
53
  def register_service(name, version, region, hostname, port)
46
54
  uuid = "#{hostname}:#{port}-#{$$}-#{name}-#{version}"
47
- # TODO Make sets ephemeral
48
55
  @registry[File.join(uuid,'addr')] = "#{hostname}:#{port}"
49
56
  @registry[File.join(uuid,'name')] = name
50
57
  @registry[File.join(uuid,'version')] = version
51
58
  @registry[File.join(uuid,'region')] = region
52
59
  @registry[File.join(uuid,'registered')] = true
53
- uuid
60
+ # Add to local services list
61
+ @services[uuid] = [name, version, region, hostname, port]
62
+ uuid
54
63
  end
55
64
 
56
65
  # Deregister the supplied service from the Registry
@@ -62,6 +71,9 @@ module RubySkynet
62
71
  @registry.delete(File.join(uuid,'region'), false)
63
72
  @registry.delete(File.join(uuid,'registered'), false)
64
73
  @registry.delete(uuid, false)
74
+ # Remove from local services list
75
+ @services.delete(uuid)
76
+ uuid
65
77
  end
66
78
 
67
79
  # Return a server that implements the specified service
@@ -105,21 +117,21 @@ module RubySkynet
105
117
  protected
106
118
 
107
119
  def service_info_deleted(path)
108
- logger.info("service_info_deleted: #{path}")
120
+ logger.trace("service_info_deleted: #{path}")
109
121
  # path: "uuid/key"
110
122
  uuid, key = path.split('/')
111
- if (key == 'registered')
112
- if server = @notifications_cache.delete(uuid)
113
- hostname, port = server['addr'].split(':')
114
- # Service has stopped and needs to be removed
115
- remove_server(File.join(server['name'], server['version'].to_s, server['region']), hostname, port, true)
116
- end
123
+ # If any child of the node is deleted, deregister the service,
124
+ # not just for (key == 'registered')
125
+ if server = @notifications_cache.delete(uuid)
126
+ hostname, port = server['addr'].split(':')
127
+ # Service has stopped and needs to be removed
128
+ remove_server(File.join(server['name'], server['version'].to_s, server['region']), hostname, port, true)
117
129
  end
118
130
  end
119
131
 
120
132
  # Service information created
121
133
  def service_info_created(path, value=nil)
122
- logger.info("service_info_created: #{path}", value)
134
+ logger.trace("service_info_created: #{path}", value)
123
135
  # path: "uuid/key"
124
136
  uuid, key = path.split('/')
125
137
 
@@ -135,7 +147,7 @@ module RubySkynet
135
147
 
136
148
  # Service information changed
137
149
  def service_info_updated(path, value=nil)
138
- logger.info("service_info_updated: #{path}", value)
150
+ logger.trace("service_info_updated: #{path}", value)
139
151
  # path: "uuid/key"
140
152
  uuid, key = path.split('/')
141
153
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_skynet
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-07 00:00:00.000000000 Z
11
+ date: 2013-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: semantic_logger