funl 0.5 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f896217f8f6185a6894823174f381b98be0af3d
4
- data.tar.gz: 497d37bb1d3180d02e12eeebcbd47d643ed836cd
3
+ metadata.gz: 0b5ff91baeb01956c2e26427666b5ce60086a777
4
+ data.tar.gz: 49f96709858f97aa437782f3059dcda83d69cc39
5
5
  SHA512:
6
- metadata.gz: dded12ff33c1585f69b04305b1942850734bd49017c01d277785907278f2fff0ea09cc664792df414187c86a75fbf96b84784ecd2abd157b469ecfd3687a707d
7
- data.tar.gz: aa9fb5d8155ee50e9366327e2457e88bdfd0da12b92de899b8597855bbc9626f82bd69035858fb3999d0c84c143685d70e215519f245d1d97a0da6a7a537864e
6
+ metadata.gz: 022c1cfec28b024802f721039fbfe7aa5a4adff0f11b2f3bac58507dbdf6142a0507f340a85c9ce04d106424d617bd960911cb47fedb38431ed5735897e30217
7
+ data.tar.gz: fc5c636c27a20f99600f1c8fa208794cade737d11a225d6f9d054ead7fa033c42c0c24e7130a69ce69e9f3fee481349f7a1c5c7560eb59cea4d08e97f21b1571
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
1
  funl
2
2
  ====
3
3
 
4
- Sequences messages.
4
+ Sequences messages.
5
+
6
+ Funl is an [atomic broadcast protocol] (https://en.wikipedia.org/wiki/Atomic_broadcast).
7
+
8
+ Funl is primarily developed for [tupelo] (https://github.com/vjoel/tupelo).
@@ -0,0 +1,132 @@
1
+ require 'funl/message-sequencer-select'
2
+ require 'funl/message-sequencer-nio'
3
+ require 'socket'
4
+ require 'tmpdir'
5
+
6
+ include Funl
7
+
8
+ class BenchmarkTask
9
+ attr_reader :name, :params
10
+
11
+ def initialize name, **params
12
+ @name = name
13
+ @params = params
14
+ end
15
+
16
+ def inspect
17
+ ps = params.map{|k,v| "#{k}: #{v}" }
18
+ pstr = ps.empty? ? "" : " " + ps.join(', ')
19
+ "<#{name}#{pstr}>"
20
+ end
21
+ end
22
+
23
+ class WarmupTask < BenchmarkTask
24
+ def run make_stream
25
+ make_stream["1"]
26
+ end
27
+ end
28
+
29
+ # All clients run in the same process; mseq server is in a child process.
30
+ class BenchmarkEnv
31
+ attr_reader :name, :dir, :path, :log, :stream_type, :tasks, :mseq_class
32
+
33
+ def initialize name, stream_type: ObjectStream::MSGPACK_TYPE,
34
+ mseq_class: MessageSequencer
35
+ @name = name
36
+ @log = Logger.new($stderr)
37
+ log.level = Logger::WARN
38
+ @stream_type = stream_type
39
+ @tasks = []
40
+ @mseq_class = mseq_class
41
+ end
42
+
43
+ def add_task task
44
+ @tasks << task
45
+ end
46
+ alias << add_task
47
+
48
+ def run
49
+ @dir = Dir.mktmpdir "funl-benchmark-#{name}-"
50
+ @path = File.join(dir, "sock")
51
+ svr = UNIXServer.new(path)
52
+ s0, s1 = UNIXSocket.pair
53
+ @pid = fork do
54
+ run_server svr, s1
55
+ end
56
+
57
+ log.progname = "client"
58
+
59
+ @mseq_ctrl = ObjectStreamWrapper.new(s0, type: stream_type)
60
+
61
+ puts mseq_class
62
+ printf "%6s %6s | %s\n", "client", "server", "task"
63
+ puts "-"*60
64
+ tasks.each do |task|
65
+ run_task task
66
+ end
67
+ ensure
68
+ close
69
+ end
70
+
71
+ def run_server svr, s
72
+ log.progname = "#{name}-mseq"
73
+ mseq = mseq_class.new svr, log: log, stream_type: stream_type
74
+ mseq.start
75
+
76
+ run_control_loop(s)
77
+ rescue => ex
78
+ log.error ex
79
+ end
80
+
81
+ def run_control_loop s
82
+ t0 = Process.times
83
+ stream = ObjectStreamWrapper.new(s, type: stream_type)
84
+ loop do
85
+ stream.read do |msg|
86
+ case msg
87
+ when "dt"
88
+ t1 = Process.times
89
+ dt = t1.utime + t1.stime - (t0.utime + t0.stime)
90
+ t0 = t1
91
+ stream << dt
92
+ ## when GC start|enable|disable
93
+ else
94
+ raise "unknown control message: #{msg.inspect}"
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def run_task task
101
+ t0 = Process.times
102
+ @mseq_ctrl << "dt"
103
+ task.run method(:make_stream)
104
+ t1 = Process.times
105
+ @mseq_ctrl << "dt"
106
+
107
+ @mseq_ctrl.read
108
+ dt = @mseq_ctrl.read
109
+
110
+ time = t1.utime + t1.stime - (t0.utime + t0.stime)
111
+ printf "%6.3f %6.3f | %p\n", time, dt, task
112
+ rescue => ex
113
+ log.error "#{task.inspect}: #{ex}"
114
+ end
115
+
116
+ def make_stream client_id = nil ## subscriptions?
117
+ conn = UNIXSocket.new(path)
118
+ stream = ObjectStreamWrapper.new(conn, type: stream_type)
119
+ stream.write_to_outbox({"client_id" => client_id})
120
+ stream.write(Message.control(SUBSCRIBE_ALL))
121
+ global_tick = stream.read["tick"]
122
+ stream.expect Message
123
+ ack = stream.read
124
+ stream
125
+ end
126
+
127
+ def close
128
+ Process.kill "TERM", @pid if @pid
129
+ Process.waitpid @pid
130
+ FileUtils.remove_entry dir if dir
131
+ end
132
+ end
@@ -0,0 +1,56 @@
1
+ require_relative 'funl-bench'
2
+
3
+ # Measure the message rate through the sequencer.
4
+ class MessageRateBenchmarkTask < BenchmarkTask
5
+ def run make_stream
6
+ n_msg = params[:n_msg] || 10
7
+ n_cli = params[:n_cli] || 2
8
+ cycle_sender = params[:cycle_sender] || false
9
+
10
+ streams = n_cli.times.map {|i| make_stream[i]}
11
+ threads = n_cli.times.map do |i|
12
+ Thread.new do
13
+ n_msg.times do
14
+ streams[i].read
15
+ end
16
+ end
17
+ end
18
+
19
+ n_msg.times do |i|
20
+ stream = cycle_sender ? streams[i % n_cli] : streams[0]
21
+ stream <<
22
+ Message[client: "1", local: i, global: nil, delta: nil,
23
+ tags: nil, blob: nil]
24
+ end
25
+
26
+ threads.each {|th| th.join}
27
+ end
28
+ end
29
+
30
+ if __FILE__ == $0
31
+ b_sel = BenchmarkEnv.new("msg-rate", mseq_class: MessageSequencerSelect)
32
+
33
+ b_sel << WarmupTask.new("warmup")
34
+ b_sel << MessageRateBenchmarkTask.new("msg rate", n_msg: 100, n_cli: 2)
35
+ b_sel << MessageRateBenchmarkTask.new("msg rate", n_msg: 1000, n_cli: 2)
36
+ b_sel << MessageRateBenchmarkTask.new("msg rate", n_msg: 1000, n_cli: 10)
37
+ # b_sel << MessageRateBenchmarkTask.new("msg rate", n_msg: 1000, n_cli: 10,
38
+ # cycle_sender: false)
39
+ # b_sel << MessageRateBenchmarkTask.new("msg rate", n_msg: 1000, n_cli: 100)
40
+
41
+ b_sel.run
42
+
43
+ puts
44
+
45
+ b_nio = BenchmarkEnv.new("msg-rate", mseq_class: MessageSequencerNio)
46
+
47
+ b_nio << WarmupTask.new("warmup")
48
+ b_nio << MessageRateBenchmarkTask.new("msg rate", n_msg: 100, n_cli: 2)
49
+ b_nio << MessageRateBenchmarkTask.new("msg rate", n_msg: 1000, n_cli: 2)
50
+ b_nio << MessageRateBenchmarkTask.new("msg rate", n_msg: 1000, n_cli: 10)
51
+ # b_nio << MessageRateBenchmarkTask.new("msg rate", n_msg: 1000, n_cli: 10,
52
+ # cycle_sender: false)
53
+ # b_nio << MessageRateBenchmarkTask.new("msg rate", n_msg: 1000, n_cli: 100)
54
+
55
+ b_nio.run
56
+ end
@@ -0,0 +1,37 @@
1
+ require 'nio'
2
+ require 'funl/message-sequencer'
3
+
4
+ module Funl
5
+ class MessageSequencerNio < MessageSequencer
6
+ private
7
+
8
+ attr_reader :selector
9
+
10
+ def init_selector
11
+ @selector = NIO::Selector.new
12
+ if server
13
+ monitor = selector.register server, :r
14
+ monitor.value = proc {accept_conn}
15
+ end
16
+ end
17
+
18
+ def register_stream stream
19
+ monitor = selector.register stream, :r
20
+ monitor.value = proc {read_conn stream}
21
+ end
22
+
23
+ def deregister_stream stream
24
+ selector.deregister stream
25
+ end
26
+
27
+ def registered_stream? stream
28
+ selector.registered? stream
29
+ end
30
+
31
+ def select_streams
32
+ selector.select do |monitor|
33
+ monitor.value.call(monitor)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ require 'funl/message-sequencer'
2
+
3
+ module Funl
4
+ class MessageSequencerSelect < MessageSequencer
5
+ private
6
+
7
+ attr_reader :streams
8
+
9
+ def init_selector
10
+ @streams = []
11
+ end
12
+
13
+ def register_stream stream
14
+ streams << stream
15
+ end
16
+
17
+ def deregister_stream stream
18
+ streams.delete stream
19
+ end
20
+
21
+ def registered_stream? stream
22
+ streams.include? stream
23
+ end
24
+
25
+ def select_streams
26
+ readables, _ = select [server, *streams]
27
+
28
+ readables.each do |readable|
29
+ case readable
30
+ when server
31
+ accept_conn
32
+ else
33
+ read_conn readable
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -11,7 +11,6 @@ module Funl
11
11
 
12
12
  attr_reader :server
13
13
  attr_reader :server_thread
14
- attr_reader :streams
15
14
  attr_reader :tick
16
15
  attr_reader :log
17
16
  attr_reader :stream_type
@@ -19,6 +18,15 @@ module Funl
19
18
  attr_reader :blob_type
20
19
  attr_reader :greeting
21
20
  attr_reader :subscribers
21
+
22
+ def self.new *a
23
+ if self == MessageSequencer
24
+ require 'funl/message-sequencer-select'
25
+ MessageSequencerSelect.new *a
26
+ else
27
+ super
28
+ end
29
+ end
22
30
 
23
31
  def initialize server, *conns, log: Logger.new($stderr),
24
32
  stream_type: ObjectStream::MSGPACK_TYPE,
@@ -34,7 +42,8 @@ module Funl
34
42
  @greeting = default_greeting
35
43
  @tick = tick
36
44
 
37
- @streams = []
45
+ init_selector
46
+
38
47
  conns.each do |conn|
39
48
  try_conn conn
40
49
  end
@@ -50,16 +59,6 @@ module Funl
50
59
  }.freeze # can't change after initial conns read it
51
60
  end
52
61
 
53
- def try_conn conn
54
- stream = message_server_stream_for(conn)
55
- current_greeting = greeting.merge({"tick" => tick})
56
- if write_succeeds?(current_greeting, stream)
57
- log.debug {"connected #{stream.inspect}"}
58
- streams << stream
59
- end
60
- end
61
- private :try_conn
62
-
63
62
  def start
64
63
  @server_thread = Thread.new do
65
64
  run
@@ -73,52 +72,58 @@ module Funl
73
72
  def wait
74
73
  server_thread.join
75
74
  end
76
-
75
+
77
76
  def run
78
77
  loop do
79
- readables, _ = select [server, *streams]
80
-
81
- readables.each do |readable|
82
- case readable
83
- when server
84
- begin
85
- conn, addr = readable.accept_nonblock
86
- log.debug {"accepted #{conn.inspect} from #{addr.inspect}"}
87
- try_conn conn
88
- rescue IO::WaitReadable
89
- next
90
- end
91
-
92
- else
93
- log.debug {"readable = #{readable}"}
94
- begin
95
- msgs = []
96
- readable.read do |msg|
97
- msgs << msg
98
- end
99
- rescue IOError, SystemCallError => ex
100
- log.debug {"closing #{readable}: #{ex}"}
101
- reject_stream readable
102
- else
103
- log.debug {
104
- "read #{msgs.size} messages from #{readable.peer_name}"}
105
- end
106
-
107
- msgs.each do |msg|
108
- if msg.control?
109
- handle_control readable, *msg.control_op
110
- else
111
- handle_message msg, readable
112
- end
113
- end
114
- end
115
- end
78
+ select_streams
116
79
  end
117
80
  rescue => ex
118
81
  log.error ex
119
82
  raise
120
83
  end
121
84
 
85
+ private
86
+
87
+ def accept_conn
88
+ conn, addr = server.accept_nonblock
89
+ log.debug {"accepted #{conn.inspect} from #{addr.inspect}"}
90
+ try_conn conn
91
+ rescue IO::WaitReadable
92
+ end
93
+
94
+ def try_conn conn
95
+ stream = message_server_stream_for(conn)
96
+ current_greeting = greeting.merge({"tick" => tick})
97
+ if write_succeeds?(current_greeting, stream)
98
+ log.debug {"connected #{stream.inspect}"}
99
+ register_stream stream
100
+ end
101
+ end
102
+
103
+ def read_conn readable
104
+ log.debug {"readable = #{readable}"}
105
+ begin
106
+ msgs = []
107
+ readable.read do |msg|
108
+ msgs << msg
109
+ end
110
+ rescue IOError, SystemCallError => ex
111
+ log.debug {"closing #{readable}: #{ex}"}
112
+ reject_stream readable
113
+ else
114
+ log.debug {
115
+ "read #{msgs.size} messages from #{readable.peer_name}"}
116
+ end
117
+
118
+ msgs.each do |msg|
119
+ if msg.control?
120
+ handle_control readable, *msg.control_op
121
+ else
122
+ handle_message msg, readable
123
+ end
124
+ end
125
+ end
126
+
122
127
  def handle_control stream, op_type, tags = nil
123
128
  log.debug {"#{stream.peer_name} #{op_type} #{tags}"}
124
129
 
@@ -181,7 +186,6 @@ module Funl
181
186
  write_succeeds? msg, stream
182
187
  end
183
188
  end
184
- private :handle_message
185
189
 
186
190
  def write_succeeds? data, stream
187
191
  stream << data
@@ -191,12 +195,11 @@ module Funl
191
195
  reject_stream stream
192
196
  false
193
197
  end
194
- private :write_succeeds?
195
198
 
196
199
  def reject_stream stream
197
200
  stream.close unless stream.closed?
198
- if streams.include? stream
199
- streams.delete stream
201
+ if registered_stream? stream
202
+ deregister_stream stream
200
203
  @subscribers_to_all.delete stream
201
204
  tags = @tags.delete stream
202
205
  if tags
@@ -1,3 +1,3 @@
1
1
  module Funl
2
- VERSION = "0.5"
2
+ VERSION = "0.6"
3
3
  end
@@ -1,4 +1,13 @@
1
1
  require 'funl/message-sequencer'
2
+
3
+ have_nio = begin
4
+ require 'funl/message-sequencer-nio'
5
+ rescue LoadError
6
+ false
7
+ else
8
+ true
9
+ end
10
+
2
11
  require 'socket'
3
12
  require 'tmpdir'
4
13
 
@@ -8,6 +17,8 @@ require 'minitest/autorun'
8
17
 
9
18
  class TestMessageSequencer < Minitest::Test
10
19
  attr_reader :log
20
+
21
+ def mseq_class; MessageSequencer; end
11
22
 
12
23
  def setup
13
24
  @dir = Dir.mktmpdir "funl-test-mseq-"
@@ -24,7 +35,7 @@ class TestMessageSequencer < Minitest::Test
24
35
  def test_initial_conns
25
36
  as = []; bs = []
26
37
  @n_clients.times {a, b = UNIXSocket.pair; as << a; bs << b}
27
- mseq = MessageSequencer.new nil, *as, log: log
38
+ mseq = mseq_class.new nil, *as, log: log
28
39
  bs.each_with_index do |b, i|
29
40
  stream = ObjectStreamWrapper.new(b, type: mseq.stream_type)
30
41
  stream.write_to_outbox({"client_id" => "test_initial_conns #{i}"})
@@ -38,7 +49,7 @@ class TestMessageSequencer < Minitest::Test
38
49
  svr = UNIXServer.new(@path)
39
50
  pid = fork do
40
51
  log.progname = "mseq"
41
- mseq = MessageSequencer.new svr, log: log, stream_type: stream_type
52
+ mseq = mseq_class.new svr, log: log, stream_type: stream_type
42
53
  mseq.start
43
54
  sleep
44
55
  end
@@ -103,8 +114,7 @@ class TestMessageSequencer < Minitest::Test
103
114
 
104
115
  path = "#{@path}-#{i}"
105
116
  svr = UNIXServer.new(path)
106
- mseq = Funl::MessageSequencer.new svr, log: log,
107
- tick: saved_tick
117
+ mseq = mseq_class.new svr, log: log, tick: saved_tick
108
118
  mseq.start
109
119
 
110
120
  conn = UNIXSocket.new(path)
@@ -132,3 +142,9 @@ class TestMessageSequencer < Minitest::Test
132
142
  mseq.stop rescue nil
133
143
  end
134
144
  end
145
+
146
+ if have_nio
147
+ class TestMessageSequencerNio < TestMessageSequencer
148
+ def mseq_class; MessageSequencerNio; end
149
+ end
150
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: funl
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.5'
4
+ version: '0.6'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel VanderWerf
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-03 00:00:00.000000000 Z
11
+ date: 2013-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: object-stream
@@ -35,23 +35,27 @@ files:
35
35
  - README.md
36
36
  - COPYING
37
37
  - Rakefile
38
- - lib/funl/client-sequencer.rb
39
- - lib/funl/message-sequencer.rb
40
- - lib/funl/history-worker.rb
41
38
  - lib/funl/stream.rb
42
- - lib/funl/client.rb
43
- - lib/funl/history-client.rb
39
+ - lib/funl/blobber.rb
44
40
  - lib/funl/subscription-tracker.rb
41
+ - lib/funl/history-client.rb
42
+ - lib/funl/history-worker.rb
43
+ - lib/funl/client.rb
44
+ - lib/funl/message-sequencer.rb
45
+ - lib/funl/message-sequencer-nio.rb
46
+ - lib/funl/client-sequencer.rb
45
47
  - lib/funl/message.rb
46
- - lib/funl/blobber.rb
47
48
  - lib/funl/version.rb
48
- - test/test-stream.rb
49
- - test/test-message.rb
50
- - test/test-client.rb
49
+ - lib/funl/message-sequencer-select.rb
50
+ - bench/msg-rate.rb
51
+ - bench/funl-bench.rb
52
+ - test/test-message-sequencer.rb
51
53
  - test/test-client-sequencer.rb
52
- - test/test-subscribe.rb
53
54
  - test/test-reflect.rb
54
- - test/test-message-sequencer.rb
55
+ - test/test-message.rb
56
+ - test/test-stream.rb
57
+ - test/test-subscribe.rb
58
+ - test/test-client.rb
55
59
  homepage: https://github.com/vjoel/funl
56
60
  licenses:
57
61
  - BSD
@@ -79,16 +83,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
83
  version: '0'
80
84
  requirements: []
81
85
  rubyforge_project:
82
- rubygems_version: 2.1.10
86
+ rubygems_version: 2.1.11
83
87
  signing_key:
84
88
  specification_version: 4
85
89
  summary: Sequences messages
86
90
  test_files:
87
- - test/test-stream.rb
88
- - test/test-message.rb
89
- - test/test-client.rb
91
+ - test/test-message-sequencer.rb
90
92
  - test/test-client-sequencer.rb
91
- - test/test-subscribe.rb
92
93
  - test/test-reflect.rb
93
- - test/test-message-sequencer.rb
94
+ - test/test-message.rb
95
+ - test/test-stream.rb
96
+ - test/test-subscribe.rb
97
+ - test/test-client.rb
94
98
  has_rdoc: