funl 0.5 → 0.6

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 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: