rflow 1.0.0a2 → 1.0.0a3
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 +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/README.VAGRANT +63 -0
- data/README.md +118 -33
- data/Vagrantfile +53 -0
- data/bin/rflow +6 -1
- data/example/basic_extensions.rb +7 -8
- data/example/http_extensions.rb +7 -8
- data/lib/rflow/broker.rb +18 -0
- data/lib/rflow/child_process.rb +3 -1
- data/lib/rflow/component.rb +51 -61
- data/lib/rflow/component/port.rb +24 -15
- data/lib/rflow/configuration.rb +1 -0
- data/lib/rflow/configuration/connection.rb +35 -17
- data/lib/rflow/configuration/ruby_dsl.rb +47 -9
- data/lib/rflow/connection.rb +13 -9
- data/lib/rflow/connections/zmq_connection.rb +46 -3
- data/lib/rflow/daemon_process.rb +1 -1
- data/lib/rflow/master.rb +8 -1
- data/lib/rflow/shard.rb +8 -2
- data/lib/rflow/version.rb +1 -1
- data/rflow.gemspec +6 -6
- data/spec/fixtures/extensions_ints.rb +7 -8
- data/spec/rflow/component/port_spec.rb +16 -22
- data/spec/rflow/components/clock_spec.rb +12 -17
- data/spec/rflow/configuration/ruby_dsl_spec.rb +234 -46
- data/spec/rflow/configuration_spec.rb +5 -5
- data/spec/rflow/forward_to_input_port_spec.rb +10 -18
- data/spec/rflow/forward_to_output_port_spec.rb +6 -13
- data/spec/rflow/logger_spec.rb +6 -6
- data/spec/rflow/message/data/raw_spec.rb +3 -3
- data/spec/rflow/message_spec.rb +16 -16
- data/spec/rflow_spec.rb +37 -37
- data/spec/spec_helper.rb +3 -5
- metadata +20 -17
data/lib/rflow/connection.rb
CHANGED
@@ -7,6 +7,8 @@ class RFlow
|
|
7
7
|
case config.type
|
8
8
|
when 'RFlow::Configuration::ZMQConnection'
|
9
9
|
RFlow::Connections::ZMQConnection.new(config)
|
10
|
+
when 'RFlow::Configuration::BrokeredZMQConnection'
|
11
|
+
RFlow::Connections::BrokeredZMQConnection.new(config)
|
10
12
|
else
|
11
13
|
raise ArgumentError, "Only ZMQConnections currently supported"
|
12
14
|
end
|
@@ -18,6 +20,7 @@ class RFlow
|
|
18
20
|
protected
|
19
21
|
attr_reader :recv_callback
|
20
22
|
|
23
|
+
public
|
21
24
|
def initialize(config)
|
22
25
|
@config = config
|
23
26
|
@uuid = config.uuid
|
@@ -56,6 +59,9 @@ class RFlow
|
|
56
59
|
def recv_callback
|
57
60
|
@recv_callback ||= Proc.new {|message|}
|
58
61
|
end
|
62
|
+
|
63
|
+
def input_port_key; config.input_port_key; end
|
64
|
+
def output_port_key; config.output_port_key; end
|
59
65
|
end
|
60
66
|
|
61
67
|
# Primarily for testing purposes. Captures whatever messages are sent on it.
|
@@ -77,14 +83,13 @@ class RFlow
|
|
77
83
|
# contain other components within it, shuttling messages in and out without
|
78
84
|
# making the internal component visible to the larger RFlow network.
|
79
85
|
class ForwardToOutputPort < Connection
|
80
|
-
def initialize(
|
86
|
+
def initialize(target_port)
|
81
87
|
super(RFlow::Configuration::NullConfiguration.new)
|
82
|
-
@
|
83
|
-
@port_name = port_name.to_sym
|
88
|
+
@target_port = target_port
|
84
89
|
end
|
85
90
|
|
86
91
|
def send_message(message)
|
87
|
-
@
|
92
|
+
@target_port.send_message(message)
|
88
93
|
end
|
89
94
|
end
|
90
95
|
|
@@ -93,15 +98,14 @@ class RFlow
|
|
93
98
|
# contain other components within it, shuttling messages in and out without
|
94
99
|
# making the internal component visible to the larger RFlow network.
|
95
100
|
class ForwardToInputPort < Connection
|
96
|
-
def initialize(
|
101
|
+
def initialize(target_port)
|
97
102
|
super(RFlow::Configuration::NullConfiguration.new)
|
98
|
-
@receiver =
|
99
|
-
@
|
100
|
-
@port_key = port_key
|
103
|
+
@receiver = target_port.component
|
104
|
+
@target_port = target_port
|
101
105
|
end
|
102
106
|
|
103
107
|
def send_message(message)
|
104
|
-
@receiver.process_message(@
|
108
|
+
@receiver.process_message(@target_port, nil, self, message)
|
105
109
|
end
|
106
110
|
end
|
107
111
|
end
|
@@ -1,6 +1,11 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'em-zeromq'
|
3
|
+
rescue Exception => e
|
4
|
+
raise LoadError, 'Error loading ZeroMQ; perhaps the wrong system library version is installed?'
|
5
|
+
end
|
2
6
|
require 'rflow/connection'
|
3
7
|
require 'rflow/message'
|
8
|
+
require 'rflow/broker'
|
4
9
|
|
5
10
|
class RFlow
|
6
11
|
module Connections
|
@@ -9,7 +14,8 @@ class RFlow
|
|
9
14
|
attr_accessor :zmq_context
|
10
15
|
|
11
16
|
def create_zmq_context
|
12
|
-
|
17
|
+
version = LibZMQ::version
|
18
|
+
RFlow.logger.debug { "Creating a new ZeroMQ context; ZeroMQ version is #{version[:major]}.#{version[:minor]}.#{version[:patch]}" }
|
13
19
|
if EM.reactor_running?
|
14
20
|
raise RuntimeError, "EventMachine reactor is running when attempting to create a ZeroMQ context"
|
15
21
|
end
|
@@ -22,7 +28,7 @@ class RFlow
|
|
22
28
|
end
|
23
29
|
end
|
24
30
|
|
25
|
-
def zmq_context;
|
31
|
+
def zmq_context; ZMQConnection.zmq_context; end
|
26
32
|
|
27
33
|
private
|
28
34
|
attr_accessor :input_socket, :output_socket
|
@@ -94,5 +100,42 @@ class RFlow
|
|
94
100
|
true
|
95
101
|
end
|
96
102
|
end
|
103
|
+
|
104
|
+
class BrokeredZMQConnection < ZMQConnection
|
105
|
+
end
|
106
|
+
|
107
|
+
# The broker process responsible for shuttling messages back and forth on a
|
108
|
+
# many-to-many pipeline link. (Solutions without a broker only allow a
|
109
|
+
# 1-to-many or many-to-1 connection.)
|
110
|
+
class ZMQStreamer < Broker
|
111
|
+
private
|
112
|
+
attr_reader :connection, :context, :back, :front
|
113
|
+
|
114
|
+
public
|
115
|
+
def initialize(config)
|
116
|
+
@connection = config.connection
|
117
|
+
super("broker-#{connection.name}", 'Broker')
|
118
|
+
end
|
119
|
+
|
120
|
+
def run_process
|
121
|
+
version = LibZMQ::version
|
122
|
+
RFlow.logger.debug { "Creating a new ZeroMQ context; ZeroMQ version is #{version[:major]}.#{version[:minor]}.#{version[:patch]}" }
|
123
|
+
@context = ZMQ::Context.new
|
124
|
+
RFlow.logger.debug { "Connecting message broker to route from #{connection.options['output_address']} to #{connection.options['input_address']}" }
|
125
|
+
@back = context.socket(ZMQ::PULL)
|
126
|
+
back.bind(connection.options['output_address'])
|
127
|
+
@front = context.socket(ZMQ::PUSH)
|
128
|
+
front.bind(connection.options['input_address'])
|
129
|
+
ZMQ::Proxy.new(back, front)
|
130
|
+
back.close
|
131
|
+
front.close
|
132
|
+
rescue Exception => e
|
133
|
+
RFlow.logger.error "Error running message broker: #{e.class}: #{e.message}, because: #{e.backtrace.inspect}"
|
134
|
+
ensure
|
135
|
+
back.close if back
|
136
|
+
front.close if front
|
137
|
+
context.terminate if context
|
138
|
+
end
|
139
|
+
end
|
97
140
|
end
|
98
141
|
end
|
data/lib/rflow/daemon_process.rb
CHANGED
data/lib/rflow/master.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
require 'rflow/daemon_process'
|
2
2
|
require 'rflow/pid_file'
|
3
3
|
require 'rflow/shard'
|
4
|
+
require 'rflow/broker'
|
4
5
|
|
5
6
|
class RFlow
|
6
7
|
class Master < DaemonProcess
|
7
8
|
attr_reader :shards
|
9
|
+
attr_reader :brokers
|
8
10
|
|
9
11
|
def initialize(config)
|
10
12
|
super(config['rflow.application_name'], 'Master')
|
11
13
|
@pid_file = PIDFile.new(config['rflow.pid_file_path'])
|
12
14
|
@shards = config.shards.map {|config| Shard.new(config) }
|
15
|
+
@brokers = config.connections.flat_map(&:brokers).map {|config| Broker.build(config) }
|
13
16
|
end
|
14
17
|
|
15
18
|
def run!
|
@@ -20,11 +23,15 @@ class RFlow
|
|
20
23
|
end
|
21
24
|
|
22
25
|
def spawn_subprocesses
|
26
|
+
RFlow.logger.debug "Running #{brokers.count} brokers" if brokers.count > 0
|
27
|
+
brokers.each(&:spawn!)
|
28
|
+
RFlow.logger.debug "#{brokers.count} brokers started: #{brokers.map { |w| "#{w.name} (#{w.pid})" }.join(", ")}" if brokers.count > 0
|
29
|
+
|
23
30
|
shards.each(&:run!)
|
24
31
|
end
|
25
32
|
|
26
33
|
def subprocesses
|
27
|
-
shards.flat_map(&:workers)
|
34
|
+
brokers + shards.flat_map(&:workers)
|
28
35
|
end
|
29
36
|
|
30
37
|
def run_process
|
data/lib/rflow/shard.rb
CHANGED
@@ -38,11 +38,17 @@ class RFlow
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
# Connect all inputs before all outputs, so connection types that require a 'server'
|
42
|
+
# to be established before a 'client' can connect can get themselves ready.
|
41
43
|
def connect_components!
|
42
44
|
RFlow.logger.debug "Connecting components"
|
43
45
|
@components.each do |component|
|
44
|
-
RFlow.logger.debug "Connecting component '#{component.name}' (#{component.uuid})"
|
45
|
-
component.
|
46
|
+
RFlow.logger.debug "Connecting inputs for component '#{component.name}' (#{component.uuid})"
|
47
|
+
component.connect_inputs!
|
48
|
+
end
|
49
|
+
@components.each do |component|
|
50
|
+
RFlow.logger.debug "Connecting outputs for component '#{component.name}' (#{component.uuid})"
|
51
|
+
component.connect_outputs!
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
data/lib/rflow/version.rb
CHANGED
data/rflow.gemspec
CHANGED
@@ -26,11 +26,11 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.add_dependency "activerecord", "~> 3.2"
|
27
27
|
|
28
28
|
s.add_dependency "avro", "~> 1.7.5"
|
29
|
-
s.add_dependency "em-zeromq", "
|
29
|
+
s.add_dependency "em-zeromq", "0.5.0"
|
30
30
|
|
31
|
-
s.add_development_dependency "bundler", "~> 1.
|
32
|
-
s.add_development_dependency "rspec", "~>
|
33
|
-
s.add_development_dependency "rspec-collection_matchers", "~>
|
34
|
-
s.add_development_dependency "rake", ">=
|
35
|
-
s.add_development_dependency "yard", "~> 0.8
|
31
|
+
s.add_development_dependency "bundler", "~> 1.6"
|
32
|
+
s.add_development_dependency "rspec", "~> 3.0"
|
33
|
+
s.add_development_dependency "rspec-collection_matchers", "~> 1.0"
|
34
|
+
s.add_development_dependency "rake", ">= 10.3"
|
35
|
+
s.add_development_dependency "yard", "~> 0.8"
|
36
36
|
end
|
@@ -13,21 +13,20 @@ end
|
|
13
13
|
RFlow::Configuration.add_available_data_extension('RFlow::Message::Data::Integer', SimpleDataExtension)
|
14
14
|
|
15
15
|
class RFlow::Components::FileOutput < RFlow::Component
|
16
|
-
attr_accessor :output_file_path
|
16
|
+
attr_accessor :output_file_path
|
17
17
|
input_port :in
|
18
18
|
|
19
19
|
def configure!(config)
|
20
20
|
self.output_file_path = config['output_file_path']
|
21
|
-
self.output_file = File.new output_file_path, 'w+'
|
22
21
|
end
|
23
22
|
|
24
23
|
def process_message(input_port, input_port_key, connection, message)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
File.open(output_file_path, 'a') do |f|
|
25
|
+
f.flock(File::LOCK_EX)
|
26
|
+
f.puts message.data.data_object.inspect
|
27
|
+
f.flush
|
28
|
+
f.flock(File::LOCK_UN)
|
29
|
+
end
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
@@ -4,17 +4,13 @@ class RFlow
|
|
4
4
|
class Component
|
5
5
|
describe Port do
|
6
6
|
it "should not be connected" do
|
7
|
-
described_class.new.
|
7
|
+
expect(described_class.new(nil)).not_to be_connected
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
describe HashPort do
|
12
12
|
it "should not be connected" do
|
13
|
-
|
14
|
-
config.stub(:name).and_return('port')
|
15
|
-
config.stub(:uuid).and_return('1')
|
16
|
-
|
17
|
-
described_class.new(config).should_not be_connected
|
13
|
+
expect(described_class.new(nil)).not_to be_connected
|
18
14
|
end
|
19
15
|
end
|
20
16
|
|
@@ -22,17 +18,16 @@ class RFlow
|
|
22
18
|
context "#connect!" do
|
23
19
|
it "should be connected" do
|
24
20
|
connection = double('connection')
|
25
|
-
connection.
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
config.stub(:uuid).and_return('1')
|
21
|
+
allow(connection).to receive(:name)
|
22
|
+
allow(connection).to receive(:uuid)
|
23
|
+
allow(connection).to receive(:input_port_key)
|
24
|
+
expect(connection).to receive(:connect_input!)
|
30
25
|
|
31
|
-
described_class.new(
|
26
|
+
described_class.new(nil).tap do |port|
|
32
27
|
port.add_connection(nil, connection)
|
33
|
-
port.
|
28
|
+
expect(port).not_to be_connected
|
34
29
|
port.connect!
|
35
|
-
port.
|
30
|
+
expect(port).to be_connected
|
36
31
|
end
|
37
32
|
end
|
38
33
|
end
|
@@ -42,17 +37,16 @@ class RFlow
|
|
42
37
|
context "#connect!" do
|
43
38
|
it "should be connected" do
|
44
39
|
connection = double('connection')
|
45
|
-
connection.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
port_config.stub(:uuid).and_return('1')
|
40
|
+
allow(connection).to receive(:name)
|
41
|
+
allow(connection).to receive(:uuid)
|
42
|
+
allow(connection).to receive(:input_port_key)
|
43
|
+
expect(connection).to receive(:connect_output!)
|
50
44
|
|
51
|
-
described_class.new(
|
45
|
+
described_class.new(nil).tap do |port|
|
52
46
|
port.add_connection(nil, connection)
|
53
|
-
port.
|
47
|
+
expect(port).not_to be_connected
|
54
48
|
port.connect!
|
55
|
-
port.
|
49
|
+
expect(port).to be_connected
|
56
50
|
end
|
57
51
|
end
|
58
52
|
end
|
@@ -7,15 +7,10 @@ class RFlow
|
|
7
7
|
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
8
8
|
Configuration.migrate_database
|
9
9
|
end
|
10
|
-
let(:config) do
|
11
|
-
RFlow::Configuration::Component.new.tap do |c|
|
12
|
-
c.output_ports << RFlow::Configuration::OutputPort.new(name: 'tick_port')
|
13
|
-
end
|
14
|
-
end
|
15
10
|
let(:message_connection) { RFlow::MessageCollectingConnection.new }
|
16
11
|
|
17
12
|
def clock(args = {})
|
18
|
-
Clock.new
|
13
|
+
Clock.new.tap do |c|
|
19
14
|
c.configure! args
|
20
15
|
c.tick_port.connect!
|
21
16
|
c.tick_port.add_connection nil, message_connection
|
@@ -26,44 +21,44 @@ class RFlow
|
|
26
21
|
|
27
22
|
it 'defaults configuration nicely' do
|
28
23
|
clock.tap do |c|
|
29
|
-
c.clock_name.
|
30
|
-
c.tick_interval.
|
24
|
+
expect(c.clock_name).to eq('Clock')
|
25
|
+
expect(c.tick_interval).to eq(1)
|
31
26
|
end
|
32
27
|
end
|
33
28
|
|
34
29
|
it 'supports name overrides' do
|
35
30
|
clock('name' => 'testname').tap do |c|
|
36
|
-
c.clock_name.
|
31
|
+
expect(c.clock_name).to eq('testname')
|
37
32
|
end
|
38
33
|
end
|
39
34
|
|
40
35
|
it 'supports interval overrides for floats' do
|
41
36
|
clock('tick_interval' => 1.5).tap do |c|
|
42
|
-
c.tick_interval.
|
37
|
+
expect(c.tick_interval).to eq(1.5)
|
43
38
|
end
|
44
39
|
end
|
45
40
|
|
46
41
|
it 'supports interval overrides for strings' do
|
47
42
|
clock('tick_interval' => '1.5').tap do |c|
|
48
|
-
c.tick_interval.
|
43
|
+
expect(c.tick_interval).to eq(1.5)
|
49
44
|
end
|
50
45
|
end
|
51
46
|
|
52
47
|
it 'should register a timer' do
|
53
|
-
EventMachine::PeriodicTimer.
|
48
|
+
expect(EventMachine::PeriodicTimer).to receive(:new).with(1)
|
54
49
|
clock.run!
|
55
50
|
end
|
56
51
|
|
57
52
|
it 'should generate a tick message when asked' do
|
58
53
|
clock.tap do |c|
|
59
54
|
now = Integer(Time.now.to_f * 1000)
|
60
|
-
messages.
|
55
|
+
expect(messages).to be_empty
|
61
56
|
c.tick
|
62
|
-
messages.
|
57
|
+
expect(messages).to have(1).message
|
63
58
|
messages.first.tap do |m|
|
64
|
-
m.data_type_name.
|
65
|
-
m.data.name.
|
66
|
-
m.data.timestamp.
|
59
|
+
expect(m.data_type_name).to eq('RFlow::Message::Clock::Tick')
|
60
|
+
expect(m.data.name).to eq('Clock')
|
61
|
+
expect(m.data.timestamp).to be >= now
|
67
62
|
end
|
68
63
|
end
|
69
64
|
end
|
@@ -12,10 +12,10 @@ class RFlow
|
|
12
12
|
it "should correctly process an empty DSL" do
|
13
13
|
described_class.configure {}
|
14
14
|
|
15
|
-
Shard.
|
16
|
-
Component.
|
17
|
-
Port.
|
18
|
-
Connection.
|
15
|
+
expect(Shard).to have(0).shards
|
16
|
+
expect(Component).to have(0).components
|
17
|
+
expect(Port).to have(0).ports
|
18
|
+
expect(Connection).to have(0).connections
|
19
19
|
end
|
20
20
|
|
21
21
|
it "should correctly process a component declaration" do
|
@@ -23,15 +23,15 @@ class RFlow
|
|
23
23
|
c.component 'boom', 'town', 'opt1' => 'OPT1', 'opt2' => 'OPT2'
|
24
24
|
end
|
25
25
|
|
26
|
-
Shard.
|
27
|
-
Component.
|
28
|
-
Port.
|
29
|
-
Connection.
|
26
|
+
expect(Shard).to have(1).shard
|
27
|
+
expect(Component).to have(1).component
|
28
|
+
expect(Port).to have(0).ports
|
29
|
+
expect(Connection).to have(0).connections
|
30
30
|
|
31
31
|
Component.first.tap do |c|
|
32
|
-
c.name.
|
33
|
-
c.specification.
|
34
|
-
c.options.
|
32
|
+
expect(c.name).to eq('boom')
|
33
|
+
expect(c.specification).to eq('town')
|
34
|
+
expect(c.options).to eq('opt1' => 'OPT1', 'opt2' => 'OPT2')
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -45,38 +45,38 @@ class RFlow
|
|
45
45
|
c.connect 'first#out[outkey]' => 'second#in[inkey]'
|
46
46
|
end
|
47
47
|
|
48
|
-
Shard.
|
49
|
-
Component.
|
50
|
-
Port.
|
51
|
-
Connection.
|
48
|
+
expect(Shard).to have(1).shard
|
49
|
+
expect(Component).to have(2).components
|
50
|
+
expect(Port).to have(2).ports
|
51
|
+
expect(Connection).to have(4).connections
|
52
52
|
|
53
53
|
first_component = Component.where(name: 'first').first.tap do |component|
|
54
|
-
component.specification.
|
55
|
-
component.
|
56
|
-
component.
|
57
|
-
component.output_ports.first.name.
|
54
|
+
expect(component.specification).to eq('First')
|
55
|
+
expect(component).to have(0).input_ports
|
56
|
+
expect(component).to have(1).output_port
|
57
|
+
expect(component.output_ports.first.name).to eq('out')
|
58
58
|
|
59
|
-
component.output_ports.first.
|
59
|
+
expect(component.output_ports.first).to have(4).connections
|
60
60
|
component.output_ports.first.connections.tap do |connections|
|
61
|
-
connections[0].input_port_key.
|
62
|
-
connections[0].output_port_key.
|
63
|
-
connections[1].input_port_key.
|
64
|
-
connections[1].output_port_key.
|
65
|
-
connections[2].input_port_key.
|
66
|
-
connections[2].output_port_key.
|
67
|
-
connections[3].input_port_key.
|
68
|
-
connections[3].output_port_key.
|
61
|
+
expect(connections[0].input_port_key).to be_nil
|
62
|
+
expect(connections[0].output_port_key).to be_nil
|
63
|
+
expect(connections[1].input_port_key).to eq('inkey')
|
64
|
+
expect(connections[1].output_port_key).to be_nil
|
65
|
+
expect(connections[2].input_port_key).to be_nil
|
66
|
+
expect(connections[2].output_port_key).to eq('outkey')
|
67
|
+
expect(connections[3].input_port_key).to eq('inkey')
|
68
|
+
expect(connections[3].output_port_key).to eq('outkey')
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
72
|
Component.where(name: 'second').first.tap do |component|
|
73
|
-
component.specification.
|
74
|
-
component.
|
75
|
-
component.input_ports.first.name.
|
76
|
-
component.
|
73
|
+
expect(component.specification).to eq('Second')
|
74
|
+
expect(component).to have(1).input_port
|
75
|
+
expect(component.input_ports.first.name).to eq('in')
|
76
|
+
expect(component).to have(0).output_ports
|
77
77
|
|
78
|
-
component.input_ports.first.
|
79
|
-
component.input_ports.first.connections.
|
78
|
+
expect(component.input_ports.first).to have(4).connections
|
79
|
+
expect(component.input_ports.first.connections).to eq(first_component.output_ports.first.connections)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
@@ -93,39 +93,227 @@ class RFlow
|
|
93
93
|
s.component 'fourth', 'Fourth'
|
94
94
|
end
|
95
95
|
|
96
|
+
c.process "s3", :count => 10 do |s|
|
97
|
+
s.component 'fifth', 'Fifth'
|
98
|
+
end
|
99
|
+
|
96
100
|
c.shard "s-ignored", :type => :process, :count => 10 do
|
97
101
|
# ignored because there are no components
|
98
102
|
end
|
99
103
|
|
100
|
-
c.
|
104
|
+
c.thread "s4", :count => 10 do |s|
|
105
|
+
s.component 'sixth', 'Sixth'
|
106
|
+
end
|
107
|
+
|
108
|
+
c.shard "s5", :type => :thread, :count => 10 do |s|
|
109
|
+
s.component 'seventh', 'Seventh'
|
110
|
+
end
|
111
|
+
|
112
|
+
c.component 'eighth', 'Eighth'
|
101
113
|
|
102
114
|
c.connect 'first#out' => 'second#in'
|
103
115
|
c.connect 'second#out[outkey]' => 'third#in[inkey]'
|
104
116
|
c.connect 'second#out' => 'third#in2'
|
105
117
|
c.connect 'third#out' => 'fourth#in'
|
106
118
|
c.connect 'third#out' => 'fifth#in'
|
119
|
+
c.connect 'third#out' => 'sixth#in'
|
107
120
|
end
|
108
121
|
|
109
|
-
Shard.
|
110
|
-
Component.
|
111
|
-
Port.
|
112
|
-
Connection.
|
122
|
+
expect(Shard).to have(6).shards
|
123
|
+
expect(Component).to have(8).components
|
124
|
+
expect(Port).to have(9).ports
|
125
|
+
expect(Connection).to have(6).connections
|
113
126
|
|
114
127
|
Shard.all.tap do |shards|
|
115
|
-
shards.map(&:name).
|
116
|
-
shards.
|
117
|
-
shards.
|
118
|
-
shards.
|
128
|
+
expect(shards.map(&:name)).to eq(['DEFAULT', 's1', 's2', 's3', 's4', 's5'])
|
129
|
+
expect(shards.map(&:type)).to eq((['RFlow::Configuration::ProcessShard'] * 4) + (['RFlow::Configuration::ThreadShard'] * 2))
|
130
|
+
expect(shards.first.components.all.map(&:name)).to eq(['first', 'eighth'])
|
131
|
+
expect(shards.second.components.all.map(&:name)).to eq(['second'])
|
132
|
+
expect(shards.third.components.all.map(&:name)).to eq(['third', 'fourth'])
|
133
|
+
expect(shards.fourth.components.all.map(&:name)).to eq(['fifth'])
|
119
134
|
end
|
120
135
|
|
121
|
-
Port.all.map(&:name).
|
136
|
+
expect(Port.all.map(&:name)).to eq(['out', 'in', 'out', 'in', 'in2', 'out', 'in', 'in', 'in'])
|
122
137
|
|
123
|
-
Connection.all.map(&:name).
|
138
|
+
expect(Connection.all.map(&:name)).to eq(
|
124
139
|
['first#out=>second#in',
|
125
140
|
'second#out[outkey]=>third#in[inkey]',
|
126
141
|
'second#out=>third#in2',
|
127
142
|
'third#out=>fourth#in',
|
128
|
-
'third#out=>fifth#in'
|
143
|
+
'third#out=>fifth#in',
|
144
|
+
'third#out=>sixth#in'])
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should generate PUSH-PULL inproc ZeroMQ connections for in-shard connections" do
|
148
|
+
described_class.configure do |c|
|
149
|
+
|
150
|
+
c.shard "s1", :process => 1 do |s|
|
151
|
+
s.component 'first', 'First', :opt1 => 'opt1'
|
152
|
+
s.component 'second', 'Second', :opt1 => 'opt1', "opt2" => "opt2"
|
153
|
+
end
|
154
|
+
|
155
|
+
c.connect 'first#out' => 'second#in'
|
156
|
+
end
|
157
|
+
|
158
|
+
expect(Shard).to have(1).shards
|
159
|
+
expect(Component).to have(2).components
|
160
|
+
expect(Port).to have(2).ports
|
161
|
+
expect(Connection).to have(1).connections
|
162
|
+
|
163
|
+
Connection.first.tap do |conn|
|
164
|
+
expect(conn.type).to eq('RFlow::Configuration::ZMQConnection')
|
165
|
+
expect(conn.name).to eq('first#out=>second#in')
|
166
|
+
expect(conn.output_port_key).to be_nil
|
167
|
+
expect(conn.input_port_key).to be_nil
|
168
|
+
conn.options.tap do |opts|
|
169
|
+
expect(opts['output_socket_type']).to eq('PUSH')
|
170
|
+
expect(opts['output_address']).to eq("inproc://rflow.#{conn.uuid}")
|
171
|
+
expect(opts['output_responsibility']).to eq('connect')
|
172
|
+
expect(opts['input_socket_type']).to eq('PULL')
|
173
|
+
expect(opts['input_address']).to eq("inproc://rflow.#{conn.uuid}")
|
174
|
+
expect(opts['input_responsibility']).to eq('bind')
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should generate PUSH-PULL ipc ZeroMQ connections for one-to-one inter-shard connections" do
|
180
|
+
described_class.configure do |c|
|
181
|
+
|
182
|
+
c.shard "s1", :process => 1 do |s|
|
183
|
+
s.component 'first', 'First', :opt1 => 'opt1'
|
184
|
+
end
|
185
|
+
|
186
|
+
c.shard "s2", :process => 1 do |s|
|
187
|
+
s.component 'second', 'Second', :opt1 => 'opt1', "opt2" => "opt2"
|
188
|
+
end
|
189
|
+
|
190
|
+
c.connect 'first#out' => 'second#in'
|
191
|
+
end
|
192
|
+
|
193
|
+
expect(Shard).to have(2).shards
|
194
|
+
expect(Component).to have(2).components
|
195
|
+
expect(Port).to have(2).ports
|
196
|
+
expect(Connection).to have(1).connections
|
197
|
+
|
198
|
+
Connection.first.tap do |conn|
|
199
|
+
expect(conn.type).to eq('RFlow::Configuration::ZMQConnection')
|
200
|
+
expect(conn.name).to eq('first#out=>second#in')
|
201
|
+
expect(conn.output_port_key).to be_nil
|
202
|
+
expect(conn.input_port_key).to be_nil
|
203
|
+
conn.options.tap do |opts|
|
204
|
+
expect(opts['output_socket_type']).to eq('PUSH')
|
205
|
+
expect(opts['output_address']).to eq("ipc://rflow.#{conn.uuid}")
|
206
|
+
expect(opts['output_responsibility']).to eq('connect')
|
207
|
+
expect(opts['input_socket_type']).to eq('PULL')
|
208
|
+
expect(opts['input_address']).to eq("ipc://rflow.#{conn.uuid}")
|
209
|
+
expect(opts['input_responsibility']).to eq('bind')
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should generate PUSH-PULL ipc ZeroMQ connections for one-to-many inter-shard connections" do
|
215
|
+
described_class.configure do |c|
|
216
|
+
|
217
|
+
c.shard "s1", :process => 1 do |s|
|
218
|
+
s.component 'first', 'First', :opt1 => 'opt1'
|
219
|
+
end
|
220
|
+
|
221
|
+
c.shard "s2", :process => 3 do |s|
|
222
|
+
s.component 'second', 'Second', :opt1 => 'opt1', "opt2" => "opt2"
|
223
|
+
end
|
224
|
+
|
225
|
+
c.connect 'first#out' => 'second#in'
|
226
|
+
end
|
227
|
+
|
228
|
+
expect(Shard).to have(2).shards
|
229
|
+
expect(Component).to have(2).components
|
230
|
+
expect(Port).to have(2).ports
|
231
|
+
expect(Connection).to have(1).connections
|
232
|
+
|
233
|
+
Connection.first.tap do |conn|
|
234
|
+
expect(conn.type).to eq('RFlow::Configuration::ZMQConnection')
|
235
|
+
expect(conn.name).to eq('first#out=>second#in')
|
236
|
+
expect(conn.output_port_key).to be_nil
|
237
|
+
expect(conn.input_port_key).to be_nil
|
238
|
+
conn.options.tap do |opts|
|
239
|
+
expect(opts['output_socket_type']).to eq('PUSH')
|
240
|
+
expect(opts['output_address']).to eq("ipc://rflow.#{conn.uuid}")
|
241
|
+
expect(opts['output_responsibility']).to eq('bind')
|
242
|
+
expect(opts['input_socket_type']).to eq('PULL')
|
243
|
+
expect(opts['input_address']).to eq("ipc://rflow.#{conn.uuid}")
|
244
|
+
expect(opts['input_responsibility']).to eq('connect')
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should generate PUSH-PULL ipc ZeroMQ connections for many-to-one inter-shard connections" do
|
250
|
+
described_class.configure do |c|
|
251
|
+
|
252
|
+
c.shard "s1", :process => 3 do |s|
|
253
|
+
s.component 'first', 'First', :opt1 => 'opt1'
|
254
|
+
end
|
255
|
+
|
256
|
+
c.shard "s2", :process => 1 do |s|
|
257
|
+
s.component 'second', 'Second', :opt1 => 'opt1', "opt2" => "opt2"
|
258
|
+
end
|
259
|
+
|
260
|
+
c.connect 'first#out' => 'second#in'
|
261
|
+
end
|
262
|
+
|
263
|
+
expect(Shard).to have(2).shards
|
264
|
+
expect(Component).to have(2).components
|
265
|
+
expect(Port).to have(2).ports
|
266
|
+
expect(Connection).to have(1).connections
|
267
|
+
|
268
|
+
Connection.first.tap do |conn|
|
269
|
+
expect(conn.type).to eq('RFlow::Configuration::ZMQConnection')
|
270
|
+
expect(conn.name).to eq('first#out=>second#in')
|
271
|
+
expect(conn.output_port_key).to be_nil
|
272
|
+
expect(conn.input_port_key).to be_nil
|
273
|
+
conn.options.tap do |opts|
|
274
|
+
expect(opts['output_socket_type']).to eq('PUSH')
|
275
|
+
expect(opts['output_address']).to eq("ipc://rflow.#{conn.uuid}")
|
276
|
+
expect(opts['output_responsibility']).to eq('connect')
|
277
|
+
expect(opts['input_socket_type']).to eq('PULL')
|
278
|
+
expect(opts['input_address']).to eq("ipc://rflow.#{conn.uuid}")
|
279
|
+
expect(opts['input_responsibility']).to eq('bind')
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should generate PUSH-PULL brokered ZeroMQ connections for many-to-many inter-shard connections" do
|
285
|
+
described_class.configure do |c|
|
286
|
+
|
287
|
+
c.shard "s1", :process => 3 do |s|
|
288
|
+
s.component 'first', 'First', :opt1 => 'opt1'
|
289
|
+
end
|
290
|
+
|
291
|
+
c.shard "s2", :process => 3 do |s|
|
292
|
+
s.component 'second', 'Second', :opt1 => 'opt1', "opt2" => "opt2"
|
293
|
+
end
|
294
|
+
|
295
|
+
c.connect 'first#out' => 'second#in'
|
296
|
+
end
|
297
|
+
|
298
|
+
expect(Shard).to have(2).shards
|
299
|
+
expect(Component).to have(2).components
|
300
|
+
expect(Port).to have(2).ports
|
301
|
+
expect(Connection).to have(1).connections
|
302
|
+
|
303
|
+
Connection.first.tap do |conn|
|
304
|
+
expect(conn.type).to eq('RFlow::Configuration::BrokeredZMQConnection')
|
305
|
+
expect(conn.name).to eq('first#out=>second#in')
|
306
|
+
expect(conn.output_port_key).to be_nil
|
307
|
+
expect(conn.input_port_key).to be_nil
|
308
|
+
conn.options.tap do |opts|
|
309
|
+
expect(opts['output_socket_type']).to eq('PUSH')
|
310
|
+
expect(opts['output_address']).to eq("ipc://rflow.#{conn.uuid}.in")
|
311
|
+
expect(opts['output_responsibility']).to eq('connect')
|
312
|
+
expect(opts['input_socket_type']).to eq('PULL')
|
313
|
+
expect(opts['input_address']).to eq("ipc://rflow.#{conn.uuid}.out")
|
314
|
+
expect(opts['input_responsibility']).to eq('connect')
|
315
|
+
end
|
316
|
+
end
|
129
317
|
end
|
130
318
|
|
131
319
|
it "should not allow two components with the same name" do
|