zoology 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +18 -0
- data/Rakefile +2 -0
- data/lib/zoology.rb +4 -0
- data/lib/zoology/callbacks.rb +17 -0
- data/lib/zoology/client.rb +128 -0
- data/lib/zoology/logging.rb +26 -0
- data/lib/zoology/path_cache.rb +57 -0
- data/lib/zoology/path_cache/watcher.rb +67 -0
- data/zoology.gemspec +17 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7fb8d471eda9b3d0294c62df3da33a0475be0971
|
4
|
+
data.tar.gz: 976e309a76a19d24f178663de8e1b6fb421652a5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 943704e42385bdd91a5ec07f32fda23f2e81fc2815e48255b09c5e31e2d3312b0ecd302770871b94797f306244b4fef8cd1a0f6666ee7d5bceda09db535b3289
|
7
|
+
data.tar.gz: 43e63331b7a033963531bd64d3581b4dc0fd32187d252dedade6f11dbbdb360fbd86af6c00c9cdf7523e1e83d2dc4eea30148848d01d557b52d21e3b8b1a9aaa
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.swp
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/Rakefile
ADDED
data/lib/zoology.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Zoology
|
2
|
+
module Callbacks
|
3
|
+
def call(callback, *args)
|
4
|
+
cb = @callbacks[callback]
|
5
|
+
cb.call(*args) if cb
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def callbacks(*callbacks)
|
11
|
+
@callbacks ||= {}
|
12
|
+
callbacks.each do |cb|
|
13
|
+
self.class.send(:define_method, cb) { |&blk| @callbacks[cb] = blk }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Zoology
|
2
|
+
class Client
|
3
|
+
require 'zookeeper'
|
4
|
+
|
5
|
+
include Logging
|
6
|
+
include Callbacks
|
7
|
+
|
8
|
+
def initialize(servers = 'localhost:2181', opts = {}, klass = Zookeeper)
|
9
|
+
@servers = servers
|
10
|
+
@heartbeat = opts[:heartbeat] || 2000
|
11
|
+
@klass = klass
|
12
|
+
|
13
|
+
callbacks :on_connected, :on_expired, :on_disconnected
|
14
|
+
end
|
15
|
+
|
16
|
+
def connect
|
17
|
+
logger.debug 'Connect to ZK'
|
18
|
+
@zookeeper = @klass.new(@servers, @heartbeat, method(:watcher))
|
19
|
+
end
|
20
|
+
|
21
|
+
def reopen
|
22
|
+
logger.debug 'reopen it'
|
23
|
+
@zookeeper.reopen
|
24
|
+
end
|
25
|
+
|
26
|
+
def connected?
|
27
|
+
if @zookeeper
|
28
|
+
@zookeeper.connected?
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def when_path(path, &blk)
|
35
|
+
req = @zookeeper.get(:path => path)
|
36
|
+
if req[:rc] == ::Zookeeper::ZOK
|
37
|
+
blk.call
|
38
|
+
else
|
39
|
+
wait_for_path(path, path, [], &blk)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def method_missing(sym, *args, &blk)
|
44
|
+
logger.debug "Call to zookeeper #{sym}: #{args.map(&:to_s).join(',')}"
|
45
|
+
@zookeeper.send(sym, *args, &blk)
|
46
|
+
end
|
47
|
+
|
48
|
+
def watcher_callback(&blk)
|
49
|
+
Zookeeper::Callbacks::WatcherCallback.create(&blk)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def watcher(event)
|
55
|
+
case event[:state]
|
56
|
+
when ::Zookeeper::ZOO_CONNECTED_STATE
|
57
|
+
logger.debug 'Received connected state'
|
58
|
+
call(:on_connected)
|
59
|
+
when ::Zookeeper::ZOO_CONNECTING_STATE
|
60
|
+
logger.debug 'Received connecting state'
|
61
|
+
call(:on_disconnected)
|
62
|
+
when ::Zookeeper::ZOO_EXPIRED_SESSION_STATE
|
63
|
+
logger.debug 'Received expired state'
|
64
|
+
call(:on_expired)
|
65
|
+
reopen
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def wait_for_path(complete_path, wait_path, rest = [], &blk)
|
70
|
+
cb = watcher_callback do |event|
|
71
|
+
if event.type != ::Zookeeper::ZOO_SESSION_EVENT
|
72
|
+
next_path = join(wait_path, rest.first)
|
73
|
+
if @zookeeper.get(:path => next_path)[:rc] == ::Zookeeper::ZOK
|
74
|
+
if next_path == complete_path
|
75
|
+
logger.debug "#{complete_path} now exists, watching it"
|
76
|
+
blk.call
|
77
|
+
else
|
78
|
+
logger.debug "#{next_path} exists, moving on to next"
|
79
|
+
rest.shift
|
80
|
+
wait_for_path(complete_path, next_path, rest, &blk)
|
81
|
+
end
|
82
|
+
else
|
83
|
+
wait_for_path(complete_path, wait_path, rest, &blk)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
req = @zookeeper.get_children(:path => wait_path, :watcher => cb)
|
89
|
+
case req[:rc]
|
90
|
+
when ::Zookeeper::ZOK
|
91
|
+
logger.debug "Now watching #{wait_path}"
|
92
|
+
when ::Zookeeper::ZNONODE
|
93
|
+
logger.debug 'Re-resolving paths'
|
94
|
+
wait_path, rest = find_wait_path(complete_path)
|
95
|
+
wait_for_path(complete_path, wait_path, rest, &blk)
|
96
|
+
else
|
97
|
+
logger.warn "wait_for_path #{wait_path} failed: #{req[:rc]}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def find_wait_path(complete_path)
|
102
|
+
parts = split(complete_path)
|
103
|
+
rest = [parts.pop]
|
104
|
+
until parts.empty?
|
105
|
+
path = join(*parts)
|
106
|
+
if @zookeeper.get(:path => path)[:rc] == ::Zookeeper::ZOK
|
107
|
+
break
|
108
|
+
end
|
109
|
+
rest.unshift parts.pop
|
110
|
+
end
|
111
|
+
[path, rest]
|
112
|
+
end
|
113
|
+
|
114
|
+
def join(*path)
|
115
|
+
p = ::File.join(*path)
|
116
|
+
if p == ''
|
117
|
+
'/'
|
118
|
+
else
|
119
|
+
p
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def split(path)
|
124
|
+
head, *tail = path.split('/')
|
125
|
+
[head] + tail.reject(&:empty?)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Zoology
|
2
|
+
module Logging
|
3
|
+
require 'logger'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
def logger
|
7
|
+
@logger ||= Logging.logger(self.class)
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :level
|
12
|
+
|
13
|
+
def disable!
|
14
|
+
@disable = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def logger(name)
|
18
|
+
sink = @disable ? StringIO.new : STDOUT
|
19
|
+
logger = Logger.new(sink)
|
20
|
+
logger.level = @level || Logger::INFO
|
21
|
+
logger.progname = name
|
22
|
+
logger
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Zoology
|
2
|
+
class PathCache
|
3
|
+
require 'zoology/path_cache/watcher'
|
4
|
+
|
5
|
+
include Logging
|
6
|
+
|
7
|
+
def initialize(client, path, &callback)
|
8
|
+
@client = client
|
9
|
+
@watches = {}
|
10
|
+
@watcher = nil
|
11
|
+
@path = path
|
12
|
+
@callback = callback
|
13
|
+
@client.on_connected do |_event|
|
14
|
+
if @sleep
|
15
|
+
logger.debug "Sleeping for #{@sleep}s to avoid thundering herd"
|
16
|
+
sleep(@sleep) if @sleep
|
17
|
+
end
|
18
|
+
watch_paths
|
19
|
+
end
|
20
|
+
@client.on_expired do |_event|
|
21
|
+
@watches.each { |_path, watcher| watcher.shutdown }
|
22
|
+
@watches = {}
|
23
|
+
@sleep = rand(10)
|
24
|
+
end
|
25
|
+
if @client.connected?
|
26
|
+
watch_paths
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def watch_paths
|
33
|
+
watcher = lambda do |_wpath, type, req|
|
34
|
+
if type == :update
|
35
|
+
update_watches(req[:children]) if req[:rc] == Zookeeper::ZOK
|
36
|
+
else
|
37
|
+
@client.when_path(@path) { watch_paths }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@watcher = Watcher.new(@client, @path, :get_children, &watcher)
|
42
|
+
@watcher.watch
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_watches(children)
|
46
|
+
paths = children.map { |child| File.join(@path, child) }
|
47
|
+
(paths - @watches.keys).each do |path|
|
48
|
+
@watches[path] = Watcher.new(@client, path, &@callback)
|
49
|
+
end
|
50
|
+
(@watches.keys - paths).each do |path|
|
51
|
+
@watches.delete(path).shutdown
|
52
|
+
end
|
53
|
+
@watches.each { |_name, watcher| watcher.watch }
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Zoology; class PathCache
|
2
|
+
class Watcher
|
3
|
+
require 'state_machine'
|
4
|
+
|
5
|
+
include Logging
|
6
|
+
|
7
|
+
state_machine :state, :initial => :not_watching do
|
8
|
+
after_transition :not_watching => :watching, :do => :set_watch
|
9
|
+
before_transition any => :shutdown, :do => :deleted
|
10
|
+
|
11
|
+
event :watch do
|
12
|
+
transition :not_watching => :watching
|
13
|
+
end
|
14
|
+
|
15
|
+
event :not_watching do
|
16
|
+
transition :watching => :not_watching
|
17
|
+
end
|
18
|
+
|
19
|
+
event :shutdown do
|
20
|
+
transition all => :shutdown
|
21
|
+
end
|
22
|
+
|
23
|
+
state :shutdown do
|
24
|
+
def call(type, data = nil)
|
25
|
+
logger.debug "Received call request, but in shutdown so not doing anything: #{@path}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
state all - [:shutdown] do
|
30
|
+
def call(type, data = nil)
|
31
|
+
@callback.call @path, type, data
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(client, path, zk_call = :get, &callback)
|
37
|
+
@client = client
|
38
|
+
@path = path
|
39
|
+
@zk_call = zk_call
|
40
|
+
@callback = callback
|
41
|
+
|
42
|
+
super()
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_watch
|
46
|
+
watcher = @client.watcher_callback do |event|
|
47
|
+
set_watch if event.type != Zookeeper::ZOO_SESSION_EVENT
|
48
|
+
end
|
49
|
+
begin
|
50
|
+
req = @client.send(@zk_call, :path => @path, :watcher => watcher)
|
51
|
+
if req[:rc] == Zookeeper::ZOK
|
52
|
+
call(:update, req)
|
53
|
+
elsif req[:rc] == Zookeeper::ZNONODE
|
54
|
+
shutdown
|
55
|
+
else
|
56
|
+
not_watching
|
57
|
+
end
|
58
|
+
rescue Zookeeper::Exceptions::ContinuationTimeoutError
|
59
|
+
not_watching
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def deleted
|
64
|
+
call(:deleted)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end; end
|
data/zoology.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = 'zoology'
|
3
|
+
gem.version = '0.1'
|
4
|
+
gem.authors = ['Torbjörn Norinder']
|
5
|
+
gem.email = ['torbjorn@genunix.se']
|
6
|
+
gem.description = "Zookeeper utilities and recipes"
|
7
|
+
gem.summary = gem.description
|
8
|
+
gem.homepage = 'https://github.com/campanja/zoology'
|
9
|
+
gem.platform = Gem::Platform::RUBY
|
10
|
+
|
11
|
+
gem.add_dependency 'zookeeper'
|
12
|
+
gem.add_dependency 'state_machine'
|
13
|
+
gem.files = `git ls-files`.split($/)
|
14
|
+
gem.executables = []
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.require_paths = ['lib']
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zoology
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Torbjörn Norinder
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: zookeeper
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: state_machine
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Zookeeper utilities and recipes
|
42
|
+
email:
|
43
|
+
- torbjorn@genunix.se
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- Gemfile
|
50
|
+
- Gemfile.lock
|
51
|
+
- Rakefile
|
52
|
+
- lib/zoology.rb
|
53
|
+
- lib/zoology/callbacks.rb
|
54
|
+
- lib/zoology/client.rb
|
55
|
+
- lib/zoology/logging.rb
|
56
|
+
- lib/zoology/path_cache.rb
|
57
|
+
- lib/zoology/path_cache/watcher.rb
|
58
|
+
- zoology.gemspec
|
59
|
+
homepage: https://github.com/campanja/zoology
|
60
|
+
licenses: []
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 2.2.2
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: Zookeeper utilities and recipes
|
82
|
+
test_files: []
|