right_speed 0.1.0 → 0.2.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/README.md +27 -2
- data/bin/right_speed +1 -3
- data/lib/right_speed/connection_closer.rb +8 -0
- data/lib/right_speed/listener.rb +13 -3
- data/lib/right_speed/processor.rb +43 -13
- data/lib/right_speed/server.rb +3 -10
- data/lib/right_speed/version.rb +1 -1
- data/lib/right_speed/worker/accepter.rb +13 -13
- data/lib/right_speed/worker/base.rb +2 -4
- data/lib/right_speed/worker/fair.rb +35 -0
- data/lib/right_speed/worker/{reader.rb → roundrobin.rb} +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a0fe25cf32e28c2aac8ee6c2621bd3d4dafef2823fffa4063e7ef6a3215f8f8
|
4
|
+
data.tar.gz: 9f77da3078ba68ac534e4124f858920d24d2b78b8b5077e4ebd5d7d874613acf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ff327d684d9ac4e0404640512e5c40250c5a33bc89018f624257da6239d787527382e232b0b09845bbf5e96d466e12bd9968f2a07724c9f5ebcbc10556e5500
|
7
|
+
data.tar.gz: efa47cb44fd9fbf9b370cc451fde661aca0a7b2ea9bbb896780fcf860c6957dce9e20c87b39f1c027df823202fad926194dcdf680fdc948639dfbdf3edd843ca
|
data/README.md
CHANGED
@@ -12,8 +12,17 @@ Currently, RightSpeed supports the very limited set of Rack protocol specificati
|
|
12
12
|
* Handling multipart contents flexisbly (using `rack.multipart.buffer_size` nor `rack.multipart.tempfile_factory`)
|
13
13
|
* [Hijacking](https://github.com/rack/rack/blob/master/SPEC.rdoc#label-Hijacking)
|
14
14
|
|
15
|
+
### Is Ractor-based server faster than prefork processes?
|
16
|
+
|
17
|
+
It can be. In our opinion, it may not be a tremendous difference, but could be a little improvement because:
|
18
|
+
* Accepted connection delivery inter-Ractor should be faster than bringing those over IPC
|
19
|
+
* JIT compilation can be just once using multiple Ractor
|
20
|
+
* ... and?
|
21
|
+
|
15
22
|
## Changelog
|
16
23
|
|
24
|
+
* v0.2.0:
|
25
|
+
* Add worker-type "fair" and "accept" in addition to "roundrobin"
|
17
26
|
* v0.1.0:
|
18
27
|
* The first release just before RubyKaigi Takeout 2021
|
19
28
|
|
@@ -27,7 +36,7 @@ Install `right_speed` by `gem` command (`gem i right_speed`), then use it direct
|
|
27
36
|
$ right_speed -c config.ru -p 8080 --workers 8
|
28
37
|
|
29
38
|
# See right_speed --help for full options:
|
30
|
-
$ right_speed --help
|
39
|
+
$ bundle exec ruby bin/right_speed --help
|
31
40
|
Usage: right_speed [options]
|
32
41
|
|
33
42
|
OPTIONS
|
@@ -35,7 +44,7 @@ OPTIONS
|
|
35
44
|
--port, -p PORT The port number to listen (default: 8080)
|
36
45
|
--backlog NUM The number of backlog
|
37
46
|
--workers NUM The number of Ractors (default: CPU cores)
|
38
|
-
--worker-type TYPE The type of workers
|
47
|
+
--worker-type TYPE The type of workers (available: roundrobin/fair/accept, default: roundrobin)
|
39
48
|
--help Show this message
|
40
49
|
```
|
41
50
|
|
@@ -47,6 +56,22 @@ $ rackup config.ru -s right_speed -p 8080 -O Workers=8
|
|
47
56
|
|
48
57
|
The default number of worker Ractors is the number of CPU cores.
|
49
58
|
|
59
|
+
### Worker Types
|
60
|
+
|
61
|
+
The `--worker-type` option is to try some patterns of use of Ractors.
|
62
|
+
|
63
|
+
* `roundrobin`
|
64
|
+
* Listener Ractor will accept connections, then send those to Worker Ractors in round-robin
|
65
|
+
* Worker Ractors will consume their input connections one-by-one
|
66
|
+
* `fair`
|
67
|
+
* Listener Ractor will accept connections, and yield those to consumers (workers)
|
68
|
+
* Worker Ractors will take connections from Listener as soon as they become available
|
69
|
+
* `accept`
|
70
|
+
* Listener does nothing
|
71
|
+
* Worker Ractors will accept connections, process requests individually
|
72
|
+
|
73
|
+
Currently, any of above workers cannot work well. We observed SEGV or Ruby runtime busy after traffic in seconds.
|
74
|
+
|
50
75
|
## Contributing
|
51
76
|
|
52
77
|
Bug reports and pull requests are welcome on GitHub at https://github.com/tagomoris/right_speed.
|
data/bin/right_speed
CHANGED
@@ -26,10 +26,8 @@ module RightSpeed
|
|
26
26
|
DEFAULT_PORT = Server::DEFAULT_PORT
|
27
27
|
DEFAULT_WORKERS = Env.processors
|
28
28
|
DEFAULT_WORKER_TYPE = Server::DEFAULT_WORKER_TYPE
|
29
|
-
DEFAULT_LISTENER_TYPE = Server::DEFAULT_LISTENER_TYPE
|
30
29
|
|
31
|
-
AVAILABLE_WORKER_TYPES = Server::AVAILABLE_WORKER_TYPES.map(
|
32
|
-
AVAILABLE_LISTENER_TYPES = Server::AVAILABLE_LISTENER_TYPES.map(:to_s).join('/')
|
30
|
+
AVAILABLE_WORKER_TYPES = Server::AVAILABLE_WORKER_TYPES.map(&:to_s).join('/')
|
33
31
|
|
34
32
|
def self.show_help(error: false, error_message: nil)
|
35
33
|
STDERR.puts(error_message, "\n") if error_message
|
@@ -4,6 +4,10 @@ require_relative "logger"
|
|
4
4
|
|
5
5
|
module RightSpeed
|
6
6
|
class ConnectionCloser
|
7
|
+
# This class was introduced to serialize closing connections
|
8
|
+
# (instead of closing those in each Ractor) to try to avoid SEGV.
|
9
|
+
# But SEGV is still happening, so this class may not be valueable.
|
10
|
+
|
7
11
|
def run(workers)
|
8
12
|
@ractor = Ractor.new(workers) do |workers|
|
9
13
|
logger = RightSpeed.logger
|
@@ -23,5 +27,9 @@ module RightSpeed
|
|
23
27
|
logger.error { "Unexpected error, #{e.class}:#{e.message}" }
|
24
28
|
end
|
25
29
|
end
|
30
|
+
|
31
|
+
def wait
|
32
|
+
@ractor.take
|
33
|
+
end
|
26
34
|
end
|
27
35
|
end
|
data/lib/right_speed/listener.rb
CHANGED
@@ -4,10 +4,12 @@ require_relative "logger"
|
|
4
4
|
|
5
5
|
module RightSpeed
|
6
6
|
module Listener
|
7
|
-
def self.setup(
|
8
|
-
case
|
7
|
+
def self.setup(worker_type:, host:, port:, backlog: nil)
|
8
|
+
case worker_type
|
9
9
|
when :roundrobin
|
10
10
|
RoundRobinListener.new(host, port, backlog)
|
11
|
+
when :fair
|
12
|
+
FairListener.new(host, port, backlog)
|
11
13
|
else
|
12
14
|
SimpleListener.new(host, port, backlog)
|
13
15
|
end
|
@@ -23,7 +25,7 @@ module RightSpeed
|
|
23
25
|
@sock = nil
|
24
26
|
end
|
25
27
|
|
26
|
-
def run
|
28
|
+
def run
|
27
29
|
@running = true
|
28
30
|
@sock = TCPServer.open(@host, @port)
|
29
31
|
@sock.listen(@backlog) if @backlog
|
@@ -43,6 +45,8 @@ module RightSpeed
|
|
43
45
|
end
|
44
46
|
|
45
47
|
class RoundRobinListener < SimpleListener
|
48
|
+
attr_reader :ractor
|
49
|
+
|
46
50
|
def run(processor)
|
47
51
|
@running = true
|
48
52
|
@ractor = Ractor.new(@host, @port, @backlog, processor) do |host, port, backlog, processor|
|
@@ -65,5 +69,11 @@ module RightSpeed
|
|
65
69
|
@ractor = nil # TODO: terminate the Ractor if possible
|
66
70
|
end
|
67
71
|
end
|
72
|
+
|
73
|
+
class FairListener < RoundRobinListener
|
74
|
+
def wait
|
75
|
+
# nothing to wait - @ractor.take consumes accepted connections unexpectedly
|
76
|
+
end
|
77
|
+
end
|
68
78
|
end
|
69
79
|
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
require 'rack/builder'
|
4
4
|
|
5
5
|
require_relative 'worker/accepter'
|
6
|
-
require_relative 'worker/
|
6
|
+
require_relative 'worker/fair'
|
7
|
+
require_relative 'worker/roundrobin'
|
7
8
|
require_relative 'connection_closer'
|
8
9
|
|
9
10
|
module RightSpeed
|
@@ -18,8 +19,10 @@ module RightSpeed
|
|
18
19
|
end
|
19
20
|
handler = Ractor.make_shareable(Handler.new(app))
|
20
21
|
case worker_type
|
21
|
-
when :
|
22
|
-
|
22
|
+
when :roundrobin
|
23
|
+
RoundRobinProcessor.new(workers, handler)
|
24
|
+
when :fair
|
25
|
+
FairProcessor.new(workers, handler)
|
23
26
|
when :accept
|
24
27
|
AcceptProcessor.new(workers, handler)
|
25
28
|
else
|
@@ -63,11 +66,11 @@ module RightSpeed
|
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
66
|
-
class
|
69
|
+
class RoundRobinProcessor < Base
|
67
70
|
def initialize(workers, handler)
|
68
71
|
@worker_num = workers
|
69
72
|
@handler = handler
|
70
|
-
@workers = workers.times.map{|i| Worker::
|
73
|
+
@workers = workers.times.map{|i| Worker::RoundRobin.new(id: i, handler: @handler)}
|
71
74
|
@closer = ConnectionCloser.new
|
72
75
|
@counter = 0
|
73
76
|
end
|
@@ -89,6 +92,35 @@ module RightSpeed
|
|
89
92
|
|
90
93
|
def wait
|
91
94
|
@workers.each{|w| w.wait}
|
95
|
+
@closer.wait
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class FairProcessor < Base
|
100
|
+
def initialize(workers, handler)
|
101
|
+
@worker_num = workers
|
102
|
+
@handler = handler
|
103
|
+
@workers = workers.times.map{|i| Worker::Fair.new(id: i, handler: @handler)}
|
104
|
+
@closer = ConnectionCloser.new
|
105
|
+
end
|
106
|
+
|
107
|
+
def configure(listener:)
|
108
|
+
@listener = listener
|
109
|
+
end
|
110
|
+
|
111
|
+
def run
|
112
|
+
@listener.run(self)
|
113
|
+
@workers.each{|w| w.run(@listener.ractor)}
|
114
|
+
@closer.run(@workers.map{|w| w.ractor})
|
115
|
+
end
|
116
|
+
|
117
|
+
def process(conn)
|
118
|
+
Ractor.yield(conn, move: true)
|
119
|
+
end
|
120
|
+
|
121
|
+
def wait
|
122
|
+
# listener, workers are using those outgoing to pass connections
|
123
|
+
@closer.wait
|
92
124
|
end
|
93
125
|
end
|
94
126
|
|
@@ -97,24 +129,22 @@ module RightSpeed
|
|
97
129
|
@worker_num = workers
|
98
130
|
@handler = handler
|
99
131
|
@workers = workers.times.map{|i| Worker::Accepter.new(id: i, handler: @handler) }
|
132
|
+
@closer = ConnectionCloser.new
|
100
133
|
end
|
101
134
|
|
102
135
|
def configure(listener:)
|
103
136
|
@listener = listener
|
104
|
-
@workers.each do |w|
|
105
|
-
w.configure(listener.sock)
|
106
|
-
end
|
107
137
|
end
|
108
138
|
|
109
139
|
def run
|
110
|
-
@
|
111
|
-
|
112
|
-
|
113
|
-
# TODO: connection closer
|
140
|
+
@listener.run
|
141
|
+
@workers.each{|w| w.run(@listener.sock)}
|
142
|
+
@closer.run(@workers.map{|w| w.ractor})
|
114
143
|
end
|
115
144
|
|
116
145
|
def wait
|
117
|
-
|
146
|
+
# workers are using those outgoing to pass connections
|
147
|
+
@closer.wait
|
118
148
|
end
|
119
149
|
end
|
120
150
|
end
|
data/lib/right_speed/server.rb
CHANGED
@@ -15,12 +15,10 @@ module RightSpeed
|
|
15
15
|
class Server
|
16
16
|
DEFAULT_HOST = "127.0.0.1"
|
17
17
|
DEFAULT_PORT = 8080
|
18
|
-
DEFAULT_WORKER_TYPE = :
|
18
|
+
DEFAULT_WORKER_TYPE = :roundrobin
|
19
19
|
DEFAULT_WORKERS = Env.processors
|
20
|
-
DEFAULT_SCHEDULER_TYPE = :roundrobin
|
21
20
|
|
22
|
-
AVAILABLE_WORKER_TYPES = [:
|
23
|
-
AVAILABLE_LISTENER_TYPES = [:roundrobin, :fair]
|
21
|
+
AVAILABLE_WORKER_TYPES = [:roundrobin, :fair, :accept]
|
24
22
|
|
25
23
|
attr_reader :config_hooks
|
26
24
|
|
@@ -30,7 +28,6 @@ module RightSpeed
|
|
30
28
|
port: DEFAULT_PORT,
|
31
29
|
workers: DEFAULT_WORKERS,
|
32
30
|
worker_type: DEFAULT_WORKER_TYPE,
|
33
|
-
scheduler_type: DEFAULT_SCHEDULER_TYPE,
|
34
31
|
backlog: nil
|
35
32
|
)
|
36
33
|
@host = host
|
@@ -38,10 +35,6 @@ module RightSpeed
|
|
38
35
|
@app = app
|
39
36
|
@workers = workers
|
40
37
|
@worker_type = worker_type
|
41
|
-
@listener_type = case @worker_type
|
42
|
-
when :read then scheduler_type
|
43
|
-
else :listen
|
44
|
-
end
|
45
38
|
@backlog = backlog
|
46
39
|
@config_hooks = []
|
47
40
|
@logger = nil
|
@@ -63,7 +56,7 @@ module RightSpeed
|
|
63
56
|
|
64
57
|
begin
|
65
58
|
processor = Processor.setup(app: @app, worker_type: @worker_type, workers: @workers)
|
66
|
-
listener = Listener.setup(
|
59
|
+
listener = Listener.setup(worker_type: @worker_type, host: @host, port: @port, backlog: nil)
|
67
60
|
processor.configure(listener: listener)
|
68
61
|
processor.run
|
69
62
|
listener.wait
|
data/lib/right_speed/version.rb
CHANGED
@@ -3,27 +3,27 @@ require_relative 'base'
|
|
3
3
|
module RightSpeed
|
4
4
|
module Worker
|
5
5
|
class Accepter < Base
|
6
|
-
def
|
7
|
-
@
|
8
|
-
|
9
|
-
|
10
|
-
def run
|
11
|
-
@ractor = Ractor.new(@id, @sock) do |id, sock|
|
6
|
+
def run(sock)
|
7
|
+
@ractor = Ractor.new(@id, sock, @handler) do |id, sock, handler|
|
8
|
+
logger = RightSpeed.logger
|
12
9
|
while conn = sock.accept
|
13
10
|
begin
|
14
|
-
|
15
|
-
# TODO:
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
handler.session(conn).process
|
12
|
+
# TODO: keep-alive?
|
13
|
+
Ractor.yield(conn, move: true) # to yield closing connections to ConnectionCloser
|
14
|
+
rescue => e
|
15
|
+
logger.error { "Unexpected error: #{e.message}\n" + e.backtrace.map{"\t#{_1}\n"}.join }
|
16
|
+
# TODO: print backtrace in better way
|
20
17
|
end
|
21
18
|
end
|
19
|
+
logger.info { "Worker#{id}: Finishing the Ractor" }
|
20
|
+
Ractor.yield(:closing) # to tell the outgoing path will be closed when stopping
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
25
24
|
def wait
|
26
|
-
@ractor.take
|
25
|
+
# nothing to wait - @ractor.take consumes closed connections unexpectedly
|
26
|
+
# @ractor.wait ?
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -4,16 +4,14 @@ require_relative "../handler"
|
|
4
4
|
module RightSpeed
|
5
5
|
module Worker
|
6
6
|
class Base
|
7
|
+
attr_reader :ractor
|
8
|
+
|
7
9
|
def initialize(id:, handler:)
|
8
10
|
@id = id
|
9
11
|
@handler = handler
|
10
12
|
@ractor = nil
|
11
13
|
end
|
12
14
|
|
13
|
-
def ractor
|
14
|
-
@ractor
|
15
|
-
end
|
16
|
-
|
17
15
|
def stop
|
18
16
|
@ractor # TODO: terminate if possible
|
19
17
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
require_relative "../logger"
|
3
|
+
|
4
|
+
module RightSpeed
|
5
|
+
module Worker
|
6
|
+
class Fair < Base
|
7
|
+
def run(listener_ractor)
|
8
|
+
@ractor = Ractor.new(@id, @handler, listener_ractor) do |id, handler, listener|
|
9
|
+
logger = RightSpeed.logger
|
10
|
+
while conn = listener.take
|
11
|
+
begin
|
12
|
+
handler.session(conn).process
|
13
|
+
# TODO: keep-alive?
|
14
|
+
Ractor.yield(conn, move: true) # to yield closing connections to ConnectionCloser
|
15
|
+
rescue => e
|
16
|
+
logger.error { "Unexpected error: #{e.message}\n" + e.backtrace.map{"\t#{_1}\n"}.join }
|
17
|
+
# TODO: print backtrace in better way
|
18
|
+
end
|
19
|
+
end
|
20
|
+
logger.info { "Worker#{id}: Finishing the Ractor" }
|
21
|
+
Ractor.yield(:closing) # to tell the outgoing path will be closed when stopping
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def process(conn)
|
26
|
+
raise "BUG: Worker::Fair#process should never be called"
|
27
|
+
end
|
28
|
+
|
29
|
+
def wait
|
30
|
+
# nothing to wait - @ractor.take consumes closed connections unexpectedly
|
31
|
+
# @ractor.wait ?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: right_speed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Satoshi Moris Tagomori
|
@@ -109,7 +109,8 @@ files:
|
|
109
109
|
- lib/right_speed/version.rb
|
110
110
|
- lib/right_speed/worker/accepter.rb
|
111
111
|
- lib/right_speed/worker/base.rb
|
112
|
-
- lib/right_speed/worker/
|
112
|
+
- lib/right_speed/worker/fair.rb
|
113
|
+
- lib/right_speed/worker/roundrobin.rb
|
113
114
|
- right_speed.gemspec
|
114
115
|
homepage: https://github.com/tagomoris/right_speed
|
115
116
|
licenses:
|