zyre 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +2 -0
- data.tar.gz.sig +0 -0
- data/History.md +8 -0
- data/LICENSE.txt +20 -0
- data/README.md +104 -0
- data/ext/zyre_ext/event.c +479 -0
- data/ext/zyre_ext/extconf.rb +25 -0
- data/ext/zyre_ext/node.c +850 -0
- data/ext/zyre_ext/poller.c +272 -0
- data/ext/zyre_ext/zyre_ext.c +154 -0
- data/ext/zyre_ext/zyre_ext.h +99 -0
- data/lib/observability/instrumentation/zyre.rb +52 -0
- data/lib/zyre.rb +53 -0
- data/lib/zyre/event.rb +82 -0
- data/lib/zyre/event/enter.rb +19 -0
- data/lib/zyre/event/evasive.rb +17 -0
- data/lib/zyre/event/exit.rb +18 -0
- data/lib/zyre/event/join.rb +18 -0
- data/lib/zyre/event/leave.rb +18 -0
- data/lib/zyre/event/shout.rb +19 -0
- data/lib/zyre/event/silent.rb +18 -0
- data/lib/zyre/event/stop.rb +9 -0
- data/lib/zyre/event/whisper.rb +18 -0
- data/lib/zyre/node.rb +147 -0
- data/lib/zyre/poller.rb +16 -0
- data/lib/zyre/testing.rb +296 -0
- data/spec/observability/instrumentation/zyre_spec.rb +56 -0
- data/spec/spec_helper.rb +62 -0
- data/spec/zyre/event_spec.rb +141 -0
- data/spec/zyre/node_spec.rb +356 -0
- data/spec/zyre/poller_spec.rb +44 -0
- data/spec/zyre/testing_spec.rb +260 -0
- data/spec/zyre_spec.rb +55 -0
- metadata +224 -0
- metadata.gz.sig +3 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'observability'
|
5
|
+
require 'observability/instrumentation' unless defined?( Observability::Instrumentation )
|
6
|
+
|
7
|
+
|
8
|
+
# Instrumentation for the Zyre library
|
9
|
+
# Refs:
|
10
|
+
# - https://github.com/zeromq/zyre
|
11
|
+
# - https://gitlab.com/ravngroup/open-source/ruby-zyre
|
12
|
+
module Observability::Instrumentation::Zyre
|
13
|
+
extend Observability::Instrumentation
|
14
|
+
|
15
|
+
depends_on 'Zyre'
|
16
|
+
|
17
|
+
|
18
|
+
when_installed( 'Zyre::Node' ) do
|
19
|
+
Zyre::Node.extend( Observability )
|
20
|
+
Zyre::Node.observe_class_method( :new )
|
21
|
+
Zyre::Node.observe_method( :port= )
|
22
|
+
Zyre::Node.observe_method( :evasive_timeout= )
|
23
|
+
Zyre::Node.observe_method( :interface= )
|
24
|
+
Zyre::Node.observe_method( :endpoint= )
|
25
|
+
Zyre::Node.observe_method( :set_header )
|
26
|
+
Zyre::Node.observe_method( :start )
|
27
|
+
Zyre::Node.observe_method( :stop )
|
28
|
+
Zyre::Node.observe_method( :join )
|
29
|
+
Zyre::Node.observe_method( :leave )
|
30
|
+
Zyre::Node.observe_method( :recv )
|
31
|
+
Zyre::Node.observe_method( :whisper, &self.method(:observe_whisper) )
|
32
|
+
Zyre::Node.observe_method( :shout, &self.method(:observe_shout) )
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
###############
|
37
|
+
module_function
|
38
|
+
###############
|
39
|
+
|
40
|
+
### Observer callback for the #whisper method.
|
41
|
+
def observe_whisper( peer_uuid, msg )
|
42
|
+
Observability.observer.add( peer_uuid: peer_uuid, message: msg )
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
### Observer callback for the #shout method.
|
47
|
+
def observe_shout( group, msg )
|
48
|
+
Observability.observer.add( group: group, message: msg )
|
49
|
+
end
|
50
|
+
|
51
|
+
end # module Observability::Instrumentation::Zyre
|
52
|
+
|
data/lib/zyre.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'loggability'
|
5
|
+
|
6
|
+
require_relative 'zyre_ext'
|
7
|
+
|
8
|
+
|
9
|
+
# A binding to libzyre
|
10
|
+
module Zyre
|
11
|
+
extend Loggability
|
12
|
+
|
13
|
+
|
14
|
+
# Gem version (semver)
|
15
|
+
VERSION = '0.1.0'
|
16
|
+
|
17
|
+
|
18
|
+
# Set up a logger for Zyre classes
|
19
|
+
log_as :zyre
|
20
|
+
|
21
|
+
|
22
|
+
### Wait on one or more +nodes+ to become readable, returning the first one that does
|
23
|
+
### or +nil+ if the +timeout+ is zero or greater and at least that many seconds elapse.
|
24
|
+
### Specify a +timeout+ of -1 to wait indefinitely. The timeout is in floating-point
|
25
|
+
### seconds.
|
26
|
+
###
|
27
|
+
### Raises an Interrupt if the call is interrupted or the ZMQ context is destroyed.
|
28
|
+
def self::wait( *nodes, timeout: -1 )
|
29
|
+
nodes = nodes.flatten
|
30
|
+
return nil if nodes.empty?
|
31
|
+
return self.wait2( nodes, timeout )
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
### If the given +key+ is a Symbol, transform it into an RFC822-style header key. If
|
36
|
+
### it's not a Symbol, returns it unchanged.
|
37
|
+
def self::transform_header_key( key )
|
38
|
+
if key.is_a?( Symbol )
|
39
|
+
key = key.to_s.gsub( /_/, '-' ).capitalize
|
40
|
+
end
|
41
|
+
|
42
|
+
return key
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
### Transform the given +headers+ hash into a form that can be passed to Zyre.
|
47
|
+
def self::normalize_headers( headers )
|
48
|
+
return headers.
|
49
|
+
transform_keys {|k| Zyre.transform_header_key(k) }.
|
50
|
+
transform_values {|v| v.to_s.encode('us-ascii') }
|
51
|
+
end
|
52
|
+
|
53
|
+
end # module Zyre
|
data/lib/zyre/event.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'loggability'
|
5
|
+
|
6
|
+
require 'zyre' unless defined?( Zyre )
|
7
|
+
|
8
|
+
|
9
|
+
#--
|
10
|
+
# See also: ext/zyre_ext/event.c
|
11
|
+
class Zyre::Event
|
12
|
+
extend Loggability
|
13
|
+
|
14
|
+
|
15
|
+
# Send logging to the 'zyre' logger
|
16
|
+
log_to :zyre
|
17
|
+
|
18
|
+
# Don't allow direct instantiation
|
19
|
+
private_class_method :new
|
20
|
+
|
21
|
+
|
22
|
+
# Autoload concrete event types
|
23
|
+
autoload :Enter, 'zyre/event/enter'
|
24
|
+
autoload :Evasive, 'zyre/event/evasive'
|
25
|
+
autoload :Exit, 'zyre/event/exit'
|
26
|
+
autoload :Join, 'zyre/event/join'
|
27
|
+
autoload :Leave, 'zyre/event/leave'
|
28
|
+
autoload :Shout, 'zyre/event/shout'
|
29
|
+
autoload :Silent, 'zyre/event/silent'
|
30
|
+
autoload :Stop, 'zyre/event/stop'
|
31
|
+
autoload :Whisper, 'zyre/event/whisper'
|
32
|
+
|
33
|
+
|
34
|
+
### Given the +name+ of an event type, return the Zyre::Event subclass that
|
35
|
+
### corresponds to it.
|
36
|
+
def self::type_by_name( name )
|
37
|
+
capname = name.to_s.capitalize
|
38
|
+
classobj = self.const_get( capname )
|
39
|
+
rescue NameError => err
|
40
|
+
self.log.debug( err )
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
### Return the event type as Zyre refers to it.
|
46
|
+
def self::type_name
|
47
|
+
return self.name[ /.*::(\w+)/, 1 ].upcase
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# Some convenience aliases
|
52
|
+
alias_method :message, :msg
|
53
|
+
|
54
|
+
|
55
|
+
### Returns +true+ if the specified +criteria+ match attribute of the event.
|
56
|
+
def match( criteria )
|
57
|
+
return criteria.all? do |key, val|
|
58
|
+
self.respond_to?( key ) && self.public_send( key ) == val
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
### Return a string describing this event, suitable for debugging.
|
64
|
+
def inspect
|
65
|
+
details = self.inspect_details
|
66
|
+
details = ' ' + details unless details.start_with?( ' ' )
|
67
|
+
|
68
|
+
return "#<%p:%#016x%s>" % [
|
69
|
+
self.class,
|
70
|
+
self.object_id,
|
71
|
+
details,
|
72
|
+
]
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
### Provide the details of the inspect message. Defaults to an empty string.
|
77
|
+
def inspect_details
|
78
|
+
return ''
|
79
|
+
end
|
80
|
+
|
81
|
+
end # class Zyre::Event
|
82
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'zyre/event' unless defined?( Zyre::Event )
|
5
|
+
|
6
|
+
|
7
|
+
class Zyre::Event::Enter < Zyre::Event
|
8
|
+
|
9
|
+
### Provide the details of the inspect message.
|
10
|
+
def inspect_details
|
11
|
+
return "%s (%s at %s) has entered the network: %p" % [
|
12
|
+
self.peer_uuid,
|
13
|
+
self.peer_name,
|
14
|
+
self.peer_addr,
|
15
|
+
self.headers,
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
end # class Zyre::Event::Enter
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'zyre/event' unless defined?( Zyre::Event )
|
5
|
+
|
6
|
+
|
7
|
+
class Zyre::Event::Evasive < Zyre::Event
|
8
|
+
|
9
|
+
### Provide the details of the inspect message.
|
10
|
+
def inspect_details
|
11
|
+
return "%s (%s) is being evasive and will be pinged manually" % [
|
12
|
+
self.peer_uuid,
|
13
|
+
self.peer_name,
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
end # class Zyre::Event::Evasive
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'zyre/event' unless defined?( Zyre::Event )
|
5
|
+
|
6
|
+
|
7
|
+
class Zyre::Event::Exit < Zyre::Event
|
8
|
+
|
9
|
+
### Provide the details of the inspect message.
|
10
|
+
def inspect_details
|
11
|
+
return "%s (%s) has left the network" % [
|
12
|
+
self.peer_uuid,
|
13
|
+
self.peer_name,
|
14
|
+
self.headers,
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
end # class Zyre::Event::Exit
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'zyre/event' unless defined?( Zyre::Event )
|
5
|
+
|
6
|
+
|
7
|
+
class Zyre::Event::Join < Zyre::Event
|
8
|
+
|
9
|
+
### Provide the details of the inspect message.
|
10
|
+
def inspect_details
|
11
|
+
return "%s (%s) joined «%s»" % [
|
12
|
+
self.peer_uuid,
|
13
|
+
self.peer_name,
|
14
|
+
self.group
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
end # class Zyre::Event::Join
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'zyre/event' unless defined?( Zyre::Event )
|
5
|
+
|
6
|
+
|
7
|
+
class Zyre::Event::Leave < Zyre::Event
|
8
|
+
|
9
|
+
### Provide the details of the inspect message.
|
10
|
+
def inspect_details
|
11
|
+
return "%s (%s) left «%s»" % [
|
12
|
+
self.peer_uuid,
|
13
|
+
self.peer_name,
|
14
|
+
self.group
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
end # class Zyre::Event::Leave
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'zyre/event' unless defined?( Zyre::Event )
|
5
|
+
|
6
|
+
|
7
|
+
class Zyre::Event::Shout < Zyre::Event
|
8
|
+
|
9
|
+
### Provide the details of the inspect message.
|
10
|
+
def inspect_details
|
11
|
+
return "shout from %s (%s) on «%s»: %p" % [
|
12
|
+
self.peer_uuid,
|
13
|
+
self.peer_name,
|
14
|
+
self.group,
|
15
|
+
self.msg,
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
end # class Zyre::Event::Shout
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'zyre/event' unless defined?( Zyre::Event )
|
5
|
+
|
6
|
+
|
7
|
+
class Zyre::Event::Silent < Zyre::Event
|
8
|
+
|
9
|
+
### Provide the details of the inspect message.
|
10
|
+
def inspect_details
|
11
|
+
return "%s (%s) isn't responding to pings" % [
|
12
|
+
self.peer_uuid,
|
13
|
+
self.peer_name,
|
14
|
+
self.headers,
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
end # class Zyre::Event::Silent
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'zyre/event' unless defined?( Zyre::Event )
|
5
|
+
|
6
|
+
|
7
|
+
class Zyre::Event::Whisper < Zyre::Event
|
8
|
+
|
9
|
+
### Provide the details of the inspect message.
|
10
|
+
def inspect_details
|
11
|
+
return "whisper from %s (%s): %p" % [
|
12
|
+
self.peer_uuid,
|
13
|
+
self.peer_name,
|
14
|
+
self.msg,
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
end # class Zyre::Event::Whisper
|
data/lib/zyre/node.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'loggability'
|
5
|
+
|
6
|
+
require 'zyre' unless defined?( Zyre )
|
7
|
+
|
8
|
+
|
9
|
+
#--
|
10
|
+
# See also: ext/zyre_ext/node.c
|
11
|
+
class Zyre::Node
|
12
|
+
extend Loggability
|
13
|
+
|
14
|
+
|
15
|
+
# Use the Zyre logger
|
16
|
+
log_to :zyre
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
### Yield each incoming event to the block. If no block is given, returns an
|
21
|
+
### enumerator instead.
|
22
|
+
def each_event( &block )
|
23
|
+
iter = self.make_event_enum
|
24
|
+
return iter.each( &block ) if block
|
25
|
+
return iter
|
26
|
+
end
|
27
|
+
alias_method :each, :each_event
|
28
|
+
|
29
|
+
|
30
|
+
### Wait for an event of a given +event_type+ (e.g., :JOIN) and matching any
|
31
|
+
### optional +criteria+, returning the event if a matching one was seen. If a
|
32
|
+
### +timeout+ is given and the event hasn't been seen after the +timeout+
|
33
|
+
### seconds have elapsed, return +nil+. If a block is given, call the block
|
34
|
+
### for each (non-matching) event that arrives in the interim. Note that the
|
35
|
+
### execution time of the block is counted in the timeout.
|
36
|
+
def wait_for( event_type, timeout: nil, **criteria, &block )
|
37
|
+
expected_type = Zyre::Event.type_by_name( event_type ) or
|
38
|
+
raise ArgumentError, "no such event type %p" % [ event_type ]
|
39
|
+
|
40
|
+
if timeout
|
41
|
+
return self.wait_for_with_timeout( expected_type, timeout, **criteria, &block )
|
42
|
+
else
|
43
|
+
return self.wait_for_indefinitely( expected_type, **criteria, &block )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
### Set headers from the given +hash+. Convenience wrapper for #set_header. Symbol
|
49
|
+
### keys will have `_` characters converted to `-` and will be capitalized when
|
50
|
+
### converted into Strings. E.g.,
|
51
|
+
###
|
52
|
+
### headers = { content_type: 'application/json' }
|
53
|
+
###
|
54
|
+
### will call:
|
55
|
+
###
|
56
|
+
### .set_header( 'Content-type', 'application/json' )
|
57
|
+
###
|
58
|
+
def headers=( hash )
|
59
|
+
hash.each do |key, val|
|
60
|
+
key = Zyre.transform_header_key( key )
|
61
|
+
self.set_header( key.to_s, val.to_s )
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
### Return a string describing the node suitable for debugging.
|
67
|
+
def inspect
|
68
|
+
return "#<%p:%#016x %s[%s]>" % [
|
69
|
+
self.class,
|
70
|
+
self.object_id,
|
71
|
+
self.name,
|
72
|
+
self.uuid,
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
#########
|
79
|
+
protected
|
80
|
+
#########
|
81
|
+
|
82
|
+
### Create an Enumerator that yields each event as it comes in. If there is
|
83
|
+
### no event to read, block until there is one.
|
84
|
+
def make_event_enum
|
85
|
+
return Enumerator.new do |yielder|
|
86
|
+
while event = self.recv
|
87
|
+
yielder.yield( event )
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
### Wait for an event of the given +event_class+ and +criteria+, returning it when it
|
94
|
+
### arrives. Blocks indefinitely until it arrives or interrupted.
|
95
|
+
def wait_for_indefinitely( event_class, **criteria, &block )
|
96
|
+
poller = Zyre::Poller.new( self )
|
97
|
+
while poller.wait
|
98
|
+
event = self.recv
|
99
|
+
if event.kind_of?( event_class ) && event.match( criteria )
|
100
|
+
return event
|
101
|
+
else
|
102
|
+
block.call( event ) if block
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
### Wait for an event of the given +event_class+ and +criteria+, returning it if
|
109
|
+
### it arrives before +timeout+ seconds elapses. If the timeout elapses first,
|
110
|
+
### return +nil+.
|
111
|
+
def wait_for_with_timeout( event_class, timeout, **criteria, &block )
|
112
|
+
start_time = get_monotime()
|
113
|
+
timeout_at = start_time + timeout
|
114
|
+
|
115
|
+
poller = Zyre::Poller.new( self )
|
116
|
+
|
117
|
+
timeout = timeout_at - get_monotime()
|
118
|
+
while timeout > 0
|
119
|
+
if poller.wait( timeout )
|
120
|
+
event = self.recv
|
121
|
+
if event.kind_of?( event_class ) && event.match( criteria )
|
122
|
+
return event
|
123
|
+
else
|
124
|
+
block.call( event ) if block
|
125
|
+
end
|
126
|
+
else
|
127
|
+
break
|
128
|
+
end
|
129
|
+
|
130
|
+
timeout = timeout_at - get_monotime()
|
131
|
+
end
|
132
|
+
|
133
|
+
return nil
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
#######
|
139
|
+
private
|
140
|
+
#######
|
141
|
+
|
142
|
+
### Return the monotonic time.
|
143
|
+
def get_monotime
|
144
|
+
return Process.clock_gettime( Process::CLOCK_MONOTONIC )
|
145
|
+
end
|
146
|
+
|
147
|
+
end # class Zyre::Node
|