zyre 0.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.
- 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
|