rflow 1.0.0a2 → 1.0.0a3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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