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.
@@ -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(receiver, port_name)
86
+ def initialize(target_port)
81
87
  super(RFlow::Configuration::NullConfiguration.new)
82
- @receiver = receiver
83
- @port_name = port_name.to_sym
88
+ @target_port = target_port
84
89
  end
85
90
 
86
91
  def send_message(message)
87
- @receiver.send(@port_name).send_message(message)
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(receiver, port_name, port_key)
101
+ def initialize(target_port)
97
102
  super(RFlow::Configuration::NullConfiguration.new)
98
- @receiver = receiver
99
- @port_name = port_name.to_sym
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(@receiver.send(@port_name), @port_key, self, message)
108
+ @receiver.process_message(@target_port, nil, self, message)
105
109
  end
106
110
  end
107
111
  end
@@ -1,6 +1,11 @@
1
- require 'em-zeromq'
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
- RFlow.logger.debug "Creating a new ZeroMQ context"
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; self.class.zmq_context; end
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
@@ -146,7 +146,7 @@ class RFlow
146
146
  end
147
147
 
148
148
  def signal_subprocesses(signal)
149
- subprocesses.each do |p|
149
+ subprocesses.reject {|p| p.pid.nil? }.each do |p|
150
150
  RFlow.logger.info "Signaling #{p.name} with #{signal}"
151
151
  Process.kill(signal, p.pid)
152
152
  end
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.connect!
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
@@ -1,3 +1,3 @@
1
1
  class RFlow
2
- VERSION = "1.0.0a2"
2
+ VERSION = "1.0.0a3"
3
3
  end
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", "~> 0.4.2"
29
+ s.add_dependency "em-zeromq", "0.5.0"
30
30
 
31
- s.add_development_dependency "bundler", "~> 1.5"
32
- s.add_development_dependency "rspec", "~> 2.99"
33
- s.add_development_dependency "rspec-collection_matchers", "~> 0.0.4"
34
- s.add_development_dependency "rake", ">= 0.8.7"
35
- s.add_development_dependency "yard", "~> 0.8.7"
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, :output_file
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
- output_file.puts message.data.data_object.inspect
26
- output_file.flush
27
- end
28
-
29
- def cleanup
30
- output_file.close
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.should_not be_connected
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
- config = double('Port Config')
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.should_receive(:connect_input!)
26
-
27
- config = double('Port Config')
28
- config.stub(:name).and_return('port')
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(config).tap do |port|
26
+ described_class.new(nil).tap do |port|
32
27
  port.add_connection(nil, connection)
33
- port.should_not be_connected
28
+ expect(port).not_to be_connected
34
29
  port.connect!
35
- port.should be_connected
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.should_receive(:connect_output!)
46
-
47
- port_config = double('Port Config')
48
- port_config.stub(:name).and_return('port')
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(port_config).tap do |port|
45
+ described_class.new(nil).tap do |port|
52
46
  port.add_connection(nil, connection)
53
- port.should_not be_connected
47
+ expect(port).not_to be_connected
54
48
  port.connect!
55
- port.should be_connected
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(config).tap do |c|
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.should == 'Clock'
30
- c.tick_interval.should == 1
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.should == 'testname'
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.should == 1.5
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.should == 1.5
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.should_receive(:new).with(1)
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.should be_empty
55
+ expect(messages).to be_empty
61
56
  c.tick
62
- messages.should have(1).message
57
+ expect(messages).to have(1).message
63
58
  messages.first.tap do |m|
64
- m.data_type_name.should == 'RFlow::Message::Clock::Tick'
65
- m.data.name.should == 'Clock'
66
- m.data.timestamp.should >= now
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.should have(0).shards
16
- Component.should have(0).components
17
- Port.should have(0).ports
18
- Connection.should have(0).connections
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.should have(1).shard
27
- Component.should have(1).component
28
- Port.should have(0).ports
29
- Connection.should have(0).connections
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.should == 'boom'
33
- c.specification.should == 'town'
34
- c.options.should == {'opt1' => 'OPT1', 'opt2' => 'OPT2'}
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.should have(1).shard
49
- Component.should have(2).components
50
- Port.should have(2).ports
51
- Connection.should have(4).connections
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.should == 'First'
55
- component.should have(0).input_ports
56
- component.should have(1).output_port
57
- component.output_ports.first.name.should == 'out'
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.should have(4).connections
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.should be_nil
62
- connections[0].output_port_key.should be_nil
63
- connections[1].input_port_key.should == 'inkey'
64
- connections[1].output_port_key.should be_nil
65
- connections[2].input_port_key.should be_nil
66
- connections[2].output_port_key.should == 'outkey'
67
- connections[3].input_port_key.should == 'inkey'
68
- connections[3].output_port_key.should == 'outkey'
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.should == 'Second'
74
- component.should have(1).input_port
75
- component.input_ports.first.name.should == 'in'
76
- component.should have(0).output_ports
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.should have(4).connections
79
- component.input_ports.first.connections.should == first_component.output_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.component 'fifth', 'Fifth'
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.should have(3).shards
110
- Component.should have(5).components
111
- Port.should have(8).ports
112
- Connection.should have(5).connections
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).should == ['DEFAULT', 's1', 's2']
116
- shards.first.components.all.map(&:name).should == ['first', 'fifth']
117
- shards.second.components.all.map(&:name).should == ['second']
118
- shards.third.components.all.map(&:name).should == ['third', 'fourth']
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).should == ['out', 'in', 'out', 'in', 'in2', 'out', 'in', 'in']
136
+ expect(Port.all.map(&:name)).to eq(['out', 'in', 'out', 'in', 'in2', 'out', 'in', 'in', 'in'])
122
137
 
123
- Connection.all.map(&:name).should ==
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