rflow 1.0.1 → 1.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 +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +3 -0
- data/Gemfile +2 -1
- data/Guardfile +1 -1
- data/README.md +9 -9
- data/Vagrantfile +9 -9
- data/bin/rflow +9 -9
- data/example/http_extensions.rb +7 -7
- data/lib/rflow.rb +24 -3
- data/lib/rflow/child_process.rb +2 -0
- data/lib/rflow/component.rb +2 -2
- data/lib/rflow/component/port.rb +51 -26
- data/lib/rflow/configuration.rb +4 -4
- data/lib/rflow/configuration/ruby_dsl.rb +4 -4
- data/lib/rflow/connection.rb +4 -4
- data/lib/rflow/connections/zmq_connection.rb +5 -7
- data/lib/rflow/daemon_process.rb +12 -2
- data/lib/rflow/logger.rb +1 -1
- data/lib/rflow/master.rb +2 -20
- data/lib/rflow/message.rb +28 -18
- data/lib/rflow/pid_file.rb +17 -5
- data/lib/rflow/shard.rb +6 -6
- data/lib/rflow/version.rb +1 -1
- data/rflow.gemspec +20 -20
- data/schema/message.avsc +6 -1
- data/spec/rflow/component/port_spec.rb +17 -17
- data/spec/rflow/components/clock_spec.rb +1 -1
- data/spec/rflow/configuration/ruby_dsl_spec.rb +47 -47
- data/spec/rflow/configuration_spec.rb +8 -8
- data/spec/rflow/forward_to_input_port_spec.rb +38 -9
- data/spec/rflow/forward_to_output_port_spec.rb +5 -4
- data/spec/rflow/logger_spec.rb +5 -5
- data/spec/rflow/message/data/raw_spec.rb +2 -2
- data/spec/rflow/message/data_spec.rb +8 -8
- data/spec/rflow/message_spec.rb +63 -29
- data/spec/rflow_spec.rb +18 -18
- metadata +14 -13
- data/NOTES +0 -187
data/schema/message.avsc
CHANGED
@@ -22,6 +22,12 @@
|
|
22
22
|
}
|
23
23
|
}
|
24
24
|
},
|
25
|
+
{"name": "properties",
|
26
|
+
"type": {
|
27
|
+
"type": "map",
|
28
|
+
"values": "string"
|
29
|
+
}
|
30
|
+
},
|
25
31
|
{"name": "data_serialization_type",
|
26
32
|
"type": {
|
27
33
|
"type": "enum",
|
@@ -33,4 +39,3 @@
|
|
33
39
|
{"name": "data", "type": "bytes"}
|
34
40
|
]
|
35
41
|
}
|
36
|
-
|
@@ -2,39 +2,39 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
class RFlow
|
4
4
|
class Component
|
5
|
-
context
|
5
|
+
context 'Input and output ports' do
|
6
6
|
let(:connection) { RFlow::Connection.new(RFlow::Configuration::NullConnectionConfiguration.new) }
|
7
7
|
let(:message) { RFlow::Message.new('RFlow::Message::Data::Raw') }
|
8
8
|
|
9
9
|
describe Port do
|
10
|
-
it
|
10
|
+
it 'should not be connected' do
|
11
11
|
expect(described_class.new(nil)).not_to be_connected
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
describe HashPort do
|
16
|
-
it
|
16
|
+
it 'should not be connected' do
|
17
17
|
expect(described_class.new(nil)).not_to be_connected
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
[InputPort, OutputPort].each do |c|
|
22
22
|
describe c do
|
23
|
-
context
|
24
|
-
it
|
23
|
+
context '#add_connection' do
|
24
|
+
it 'should add the connection' do
|
25
25
|
described_class.new(nil).tap do |port|
|
26
26
|
port.add_connection(nil, connection)
|
27
|
-
expect(port[nil]).to include connection
|
27
|
+
expect(port[nil].connections).to include connection
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
context
|
33
|
-
it
|
32
|
+
context '#remove_connection' do
|
33
|
+
it 'should remove the connection' do
|
34
34
|
described_class.new(nil).tap do |port|
|
35
35
|
port.add_connection(nil, connection)
|
36
36
|
port.remove_connection(nil, connection)
|
37
|
-
expect(port[nil]).not_to include connection
|
37
|
+
expect(port[nil].connections).not_to include connection
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -42,8 +42,8 @@ class RFlow
|
|
42
42
|
end
|
43
43
|
|
44
44
|
describe InputPort do
|
45
|
-
context
|
46
|
-
it
|
45
|
+
context '#connect!' do
|
46
|
+
it 'should be connected' do
|
47
47
|
expect(connection).to receive(:connect_input!)
|
48
48
|
|
49
49
|
described_class.new(nil).tap do |port|
|
@@ -57,8 +57,8 @@ class RFlow
|
|
57
57
|
end
|
58
58
|
|
59
59
|
describe OutputPort do
|
60
|
-
context
|
61
|
-
it
|
60
|
+
context '#connect!' do
|
61
|
+
it 'should be connected' do
|
62
62
|
expect(connection).to receive(:connect_output!)
|
63
63
|
|
64
64
|
described_class.new(nil).tap do |port|
|
@@ -70,8 +70,8 @@ class RFlow
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
context
|
74
|
-
it
|
73
|
+
context '#add_connection' do
|
74
|
+
it 'should deliver messages to the new connection' do
|
75
75
|
described_class.new(nil).tap do |port|
|
76
76
|
port.connect!
|
77
77
|
port.all_connections # trigger caching
|
@@ -85,8 +85,8 @@ class RFlow
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
context
|
89
|
-
it
|
88
|
+
context '#remove_connection' do
|
89
|
+
it 'should not deliver messages to the old connection' do
|
90
90
|
described_class.new(nil).tap do |port|
|
91
91
|
allow(connection).to receive(:connect_output!)
|
92
92
|
port.add_connection(nil, connection)
|
@@ -4,7 +4,7 @@ class RFlow
|
|
4
4
|
module Components
|
5
5
|
describe Clock do
|
6
6
|
before(:each) do
|
7
|
-
ActiveRecord::Base.establish_connection adapter:
|
7
|
+
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
|
8
8
|
Configuration.migrate_database
|
9
9
|
end
|
10
10
|
|
@@ -5,11 +5,11 @@ class RFlow
|
|
5
5
|
class Configuration
|
6
6
|
describe RubyDSL do
|
7
7
|
before(:each) do
|
8
|
-
ActiveRecord::Base.establish_connection adapter:
|
8
|
+
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
|
9
9
|
Configuration.migrate_database
|
10
10
|
end
|
11
11
|
|
12
|
-
it
|
12
|
+
it 'should correctly process an empty DSL' do
|
13
13
|
described_class.configure {}
|
14
14
|
|
15
15
|
expect(Shard).to have(0).shards
|
@@ -18,7 +18,7 @@ class RFlow
|
|
18
18
|
expect(Connection).to have(0).connections
|
19
19
|
end
|
20
20
|
|
21
|
-
it
|
21
|
+
it 'should correctly process a component declaration' do
|
22
22
|
described_class.configure do |c|
|
23
23
|
c.component 'boom', 'town', 'opt1' => 'OPT1', 'opt2' => 'OPT2'
|
24
24
|
end
|
@@ -35,7 +35,7 @@ class RFlow
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
it
|
38
|
+
it 'should correctly process a connect declaration' do
|
39
39
|
described_class.configure do |c|
|
40
40
|
c.component 'first', 'First'
|
41
41
|
c.component 'second', 'Second'
|
@@ -109,32 +109,32 @@ class RFlow
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
it
|
112
|
+
it 'should correctly process shard declarations' do
|
113
113
|
described_class.configure do |c|
|
114
114
|
c.component 'first', 'First', :opt1 => 'opt1'
|
115
115
|
|
116
|
-
c.shard
|
117
|
-
s.component 'second', 'Second', :opt1 => 'opt1',
|
116
|
+
c.shard 's1', :process => 2 do |s|
|
117
|
+
s.component 'second', 'Second', :opt1 => 'opt1', 'opt2' => 'opt2'
|
118
118
|
end
|
119
119
|
|
120
|
-
c.shard
|
120
|
+
c.shard 's2', :type => :process, :count => 10 do |s|
|
121
121
|
s.component 'third', 'Third'
|
122
122
|
s.component 'fourth', 'Fourth'
|
123
123
|
end
|
124
124
|
|
125
|
-
c.process
|
125
|
+
c.process 's3', :count => 10 do |s|
|
126
126
|
s.component 'fifth', 'Fifth'
|
127
127
|
end
|
128
128
|
|
129
|
-
c.shard
|
129
|
+
c.shard 's-ignored', :type => :process, :count => 10 do
|
130
130
|
# ignored because there are no components
|
131
131
|
end
|
132
132
|
|
133
|
-
c.thread
|
133
|
+
c.thread 's4', :count => 10 do |s|
|
134
134
|
s.component 'sixth', 'Sixth'
|
135
135
|
end
|
136
136
|
|
137
|
-
c.shard
|
137
|
+
c.shard 's5', :type => :thread, :count => 10 do |s|
|
138
138
|
s.component 'seventh', 'Seventh'
|
139
139
|
end
|
140
140
|
|
@@ -173,12 +173,12 @@ class RFlow
|
|
173
173
|
'third#out=>sixth#in'])
|
174
174
|
end
|
175
175
|
|
176
|
-
it
|
176
|
+
it 'should generate PUSH-PULL inproc ZeroMQ connections for in-shard connections' do
|
177
177
|
described_class.configure do |c|
|
178
178
|
|
179
|
-
c.shard
|
179
|
+
c.shard 's1', :process => 2 do |s|
|
180
180
|
s.component 'first', 'First', :opt1 => 'opt1'
|
181
|
-
s.component 'second', 'Second', :opt1 => 'opt1',
|
181
|
+
s.component 'second', 'Second', :opt1 => 'opt1', 'opt2' => 'opt2'
|
182
182
|
end
|
183
183
|
|
184
184
|
c.connect 'first#out' => 'second#in'
|
@@ -205,15 +205,15 @@ class RFlow
|
|
205
205
|
end
|
206
206
|
end
|
207
207
|
|
208
|
-
it
|
208
|
+
it 'should generate PUSH-PULL ipc ZeroMQ connections for one-to-one inter-shard connections' do
|
209
209
|
described_class.configure do |c|
|
210
210
|
|
211
|
-
c.shard
|
211
|
+
c.shard 's1', :process => 1 do |s|
|
212
212
|
s.component 'first', 'First', :opt1 => 'opt1'
|
213
213
|
end
|
214
214
|
|
215
|
-
c.shard
|
216
|
-
s.component 'second', 'Second', :opt1 => 'opt1',
|
215
|
+
c.shard 's2', :process => 1 do |s|
|
216
|
+
s.component 'second', 'Second', :opt1 => 'opt1', 'opt2' => 'opt2'
|
217
217
|
end
|
218
218
|
|
219
219
|
c.connect 'first#out' => 'second#in'
|
@@ -240,15 +240,15 @@ class RFlow
|
|
240
240
|
end
|
241
241
|
end
|
242
242
|
|
243
|
-
it
|
243
|
+
it 'should generate PUSH-PULL ipc ZeroMQ connections for one-to-many inter-shard connections' do
|
244
244
|
described_class.configure do |c|
|
245
245
|
|
246
|
-
c.shard
|
246
|
+
c.shard 's1', :process => 1 do |s|
|
247
247
|
s.component 'first', 'First', :opt1 => 'opt1'
|
248
248
|
end
|
249
249
|
|
250
|
-
c.shard
|
251
|
-
s.component 'second', 'Second', :opt1 => 'opt1',
|
250
|
+
c.shard 's2', :process => 3 do |s|
|
251
|
+
s.component 'second', 'Second', :opt1 => 'opt1', 'opt2' => 'opt2'
|
252
252
|
end
|
253
253
|
|
254
254
|
c.connect 'first#out' => 'second#in'
|
@@ -275,15 +275,15 @@ class RFlow
|
|
275
275
|
end
|
276
276
|
end
|
277
277
|
|
278
|
-
it
|
278
|
+
it 'should generate PUSH-PULL ipc ZeroMQ connections for many-to-one inter-shard connections' do
|
279
279
|
described_class.configure do |c|
|
280
280
|
|
281
|
-
c.shard
|
281
|
+
c.shard 's1', :process => 3 do |s|
|
282
282
|
s.component 'first', 'First', :opt1 => 'opt1'
|
283
283
|
end
|
284
284
|
|
285
|
-
c.shard
|
286
|
-
s.component 'second', 'Second', :opt1 => 'opt1',
|
285
|
+
c.shard 's2', :process => 1 do |s|
|
286
|
+
s.component 'second', 'Second', :opt1 => 'opt1', 'opt2' => 'opt2'
|
287
287
|
end
|
288
288
|
|
289
289
|
c.connect 'first#out' => 'second#in'
|
@@ -310,15 +310,15 @@ class RFlow
|
|
310
310
|
end
|
311
311
|
end
|
312
312
|
|
313
|
-
it
|
313
|
+
it 'should generate PUSH-PULL brokered ZeroMQ connections for many-to-many inter-shard connections' do
|
314
314
|
described_class.configure do |c|
|
315
315
|
|
316
|
-
c.shard
|
316
|
+
c.shard 's1', :process => 3 do |s|
|
317
317
|
s.component 'first', 'First', :opt1 => 'opt1'
|
318
318
|
end
|
319
319
|
|
320
|
-
c.shard
|
321
|
-
s.component 'second', 'Second', :opt1 => 'opt1',
|
320
|
+
c.shard 's2', :process => 3 do |s|
|
321
|
+
s.component 'second', 'Second', :opt1 => 'opt1', 'opt2' => 'opt2'
|
322
322
|
end
|
323
323
|
|
324
324
|
c.connect 'first#out' => 'second#in'
|
@@ -345,15 +345,15 @@ class RFlow
|
|
345
345
|
end
|
346
346
|
end
|
347
347
|
|
348
|
-
it
|
348
|
+
it 'should generate PUB-SUB ipc ZeroMQ connections for one-to-many broadcast connections' do
|
349
349
|
described_class.configure do |c|
|
350
350
|
|
351
|
-
c.shard
|
351
|
+
c.shard 's1', :process => 1 do |s|
|
352
352
|
s.component 'first', 'First', :opt1 => 'opt1'
|
353
353
|
end
|
354
354
|
|
355
|
-
c.shard
|
356
|
-
s.component 'second', 'Second', :opt1 => 'opt1',
|
355
|
+
c.shard 's2', :process => 3 do |s|
|
356
|
+
s.component 'second', 'Second', :opt1 => 'opt1', 'opt2' => 'opt2'
|
357
357
|
end
|
358
358
|
|
359
359
|
c.connect 'first#out' => 'second#in', :delivery => 'broadcast'
|
@@ -380,15 +380,15 @@ class RFlow
|
|
380
380
|
end
|
381
381
|
end
|
382
382
|
|
383
|
-
it
|
383
|
+
it 'should generate PUB-SUB brokered ZeroMQ connections for many-to-many broadcast connections' do
|
384
384
|
described_class.configure do |c|
|
385
385
|
|
386
|
-
c.shard
|
386
|
+
c.shard 's1', :process => 3 do |s|
|
387
387
|
s.component 'first', 'First', :opt1 => 'opt1'
|
388
388
|
end
|
389
389
|
|
390
|
-
c.shard
|
391
|
-
s.component 'second', 'Second', :opt1 => 'opt1',
|
390
|
+
c.shard 's2', :process => 3 do |s|
|
391
|
+
s.component 'second', 'Second', :opt1 => 'opt1', 'opt2' => 'opt2'
|
392
392
|
end
|
393
393
|
|
394
394
|
c.connect 'first#out' => 'second#in', :delivery => 'broadcast'
|
@@ -415,12 +415,12 @@ class RFlow
|
|
415
415
|
end
|
416
416
|
end
|
417
417
|
|
418
|
-
it
|
418
|
+
it 'should generate PUB-SUB brokered ZeroMQ connections for many-to-many in-shard broadcast connections' do
|
419
419
|
described_class.configure do |c|
|
420
420
|
|
421
|
-
c.shard
|
421
|
+
c.shard 's1', :process => 3 do |s|
|
422
422
|
s.component 'first', 'First', :opt1 => 'opt1'
|
423
|
-
s.component 'second', 'Second', :opt1 => 'opt1',
|
423
|
+
s.component 'second', 'Second', :opt1 => 'opt1', 'opt2' => 'opt2'
|
424
424
|
end
|
425
425
|
|
426
426
|
c.connect 'first#out' => 'second#in', :delivery => 'broadcast'
|
@@ -447,7 +447,7 @@ class RFlow
|
|
447
447
|
end
|
448
448
|
end
|
449
449
|
|
450
|
-
it
|
450
|
+
it 'should not allow two components with the same name' do
|
451
451
|
expect {
|
452
452
|
described_class.configure do |c|
|
453
453
|
c.component 'first', 'First'
|
@@ -456,13 +456,13 @@ class RFlow
|
|
456
456
|
}.to raise_error(ActiveRecord::RecordInvalid)
|
457
457
|
end
|
458
458
|
|
459
|
-
it
|
459
|
+
it 'should not allow two shards with the same name' do
|
460
460
|
expect {
|
461
461
|
described_class.configure do |c|
|
462
|
-
c.shard(
|
463
|
-
c.shard(
|
462
|
+
c.shard('s1', :process => 2) {|c| c.component 'x', 'y' }
|
463
|
+
c.shard('s1', :process => 2) {|c| c.component 'z', 'q' }
|
464
464
|
end
|
465
|
-
}.to raise_error
|
465
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
466
466
|
end
|
467
467
|
end
|
468
468
|
end
|
@@ -5,12 +5,12 @@ class RFlow
|
|
5
5
|
describe Configuration do
|
6
6
|
describe '.add_available_data_type' do
|
7
7
|
context 'if passed a data_serialization that is not avro' do
|
8
|
-
it
|
8
|
+
it 'should throw an exception' do
|
9
9
|
expect { Configuration.add_available_data_type('A', 'boom', 'schema') }.to raise_error(
|
10
10
|
ArgumentError, "Data serialization_type must be 'avro' for 'A'")
|
11
11
|
end
|
12
12
|
|
13
|
-
it
|
13
|
+
it 'should not update the available_data_types' do
|
14
14
|
expect {
|
15
15
|
Configuration.add_available_data_type('A', 'boom', 'schema') rescue nil
|
16
16
|
}.not_to change { Configuration.available_data_types.size }
|
@@ -18,18 +18,18 @@ class RFlow
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
describe
|
22
|
-
describe
|
21
|
+
describe 'Data Extensions' do
|
22
|
+
describe '.add_available_data_extension' do
|
23
23
|
context 'if passed a non-module data extension' do
|
24
|
-
it
|
24
|
+
it 'should throw an exception' do
|
25
25
|
expect {
|
26
26
|
Configuration.add_available_data_extension('data_type', 'NOTAMODULE')
|
27
|
-
}.to raise_error(ArgumentError,
|
27
|
+
}.to raise_error(ArgumentError, 'Invalid data extension NOTAMODULE for data_type. Only Ruby Modules allowed')
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
context
|
32
|
-
it
|
31
|
+
context 'if passed a valid Module as a data extension' do
|
32
|
+
it 'should update the available_data_extensions' do
|
33
33
|
expect {
|
34
34
|
Configuration.add_available_data_extension('data_type', Module.new)
|
35
35
|
}.to change { Configuration.available_data_extensions['data_type'].size }.by(1)
|
@@ -3,38 +3,67 @@ require 'spec_helper'
|
|
3
3
|
class RFlow
|
4
4
|
describe ForwardToInputPort do
|
5
5
|
before(:each) do
|
6
|
-
ActiveRecord::Base.establish_connection adapter:
|
6
|
+
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
|
7
7
|
Configuration.migrate_database
|
8
8
|
end
|
9
9
|
|
10
|
-
let(:
|
10
|
+
let(:accepted_messages) { [] }
|
11
11
|
let(:dropped_messages) { [] }
|
12
12
|
|
13
13
|
let(:generator) do
|
14
14
|
RFlow::Components::GenerateIntegerSequence.new.tap do |c|
|
15
15
|
c.configure!({})
|
16
|
-
c.out.direct_connect ruby_proc_filter.in
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
|
-
let(:
|
19
|
+
let(:accept_evens) do
|
21
20
|
RFlow::Components::RubyProcFilter.new.tap do |c|
|
22
21
|
c.configure! 'filter_proc_string' => 'message.data.data_object % 2 == 0'
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
25
|
it 'should forward generated integers to be filtered by the proc filter' do
|
27
|
-
|
28
|
-
|
26
|
+
generator.out.direct_connect accept_evens.in
|
27
|
+
|
28
|
+
accept_evens.filtered.collect_messages(nil, accepted_messages) do
|
29
|
+
accept_evens.dropped.collect_messages(nil, dropped_messages) do
|
30
|
+
5.times { generator.generate }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
expect(accepted_messages).to have(3).messages
|
35
|
+
expect(accepted_messages.map(&:data).map(&:data_object)).to eq([0, 2, 4])
|
36
|
+
expect(dropped_messages).to have(2).messages
|
37
|
+
expect(dropped_messages.map(&:data).map(&:data_object)).to eq([1, 3])
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should forward integers from the union of subports' do
|
41
|
+
generator.even_odd_out.direct_connect accept_evens.in
|
42
|
+
|
43
|
+
accept_evens.filtered.collect_messages(nil, accepted_messages) do
|
44
|
+
accept_evens.dropped.collect_messages(nil, dropped_messages) do
|
29
45
|
5.times { generator.generate }
|
30
46
|
end
|
31
47
|
end
|
32
48
|
|
33
|
-
expect(
|
34
|
-
expect(
|
49
|
+
expect(accepted_messages).to have(3).messages
|
50
|
+
expect(accepted_messages.map(&:data).map(&:data_object)).to eq([0, 2, 4])
|
35
51
|
expect(dropped_messages).to have(2).messages
|
36
52
|
expect(dropped_messages.map(&:data).map(&:data_object)).to eq([1, 3])
|
37
53
|
end
|
54
|
+
|
55
|
+
it 'should forward integers from a subport' do
|
56
|
+
generator.even_odd_out['even'].direct_connect accept_evens.in
|
57
|
+
|
58
|
+
accept_evens.filtered.collect_messages(nil, accepted_messages) do
|
59
|
+
accept_evens.dropped.collect_messages(nil, dropped_messages) do
|
60
|
+
5.times { generator.generate }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
expect(accepted_messages).to have(3).messages
|
65
|
+
expect(accepted_messages.map(&:data).map(&:data_object)).to eq([0, 2, 4])
|
66
|
+
expect(dropped_messages).to have(0).messages
|
67
|
+
end
|
38
68
|
end
|
39
69
|
end
|
40
|
-
|