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