polyphony 0.43.8 → 0.45.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/.rubocop.yml +7 -1
- data/CHANGELOG.md +38 -0
- data/Gemfile.lock +13 -11
- data/README.md +20 -5
- data/Rakefile +1 -1
- data/TODO.md +16 -14
- data/bin/stress.rb +28 -0
- data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
- data/docs/api-reference/thread.md +1 -1
- data/docs/getting-started/overview.md +14 -14
- data/docs/getting-started/tutorial.md +1 -1
- data/examples/adapters/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- data/examples/core/{xx-agent.rb → xx-backend.rb} +5 -5
- data/examples/core/xx-channels.rb +4 -2
- data/examples/core/xx-using-a-mutex.rb +2 -1
- data/examples/io/xx-pry.rb +18 -0
- data/examples/io/xx-rack_server.rb +71 -0
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/ext/polyphony/backend.h +41 -0
- data/ext/polyphony/event.c +86 -0
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/fiber.c +0 -5
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +234 -228
- data/ext/polyphony/polyphony.c +4 -0
- data/ext/polyphony/polyphony.h +16 -16
- data/ext/polyphony/polyphony_ext.c +4 -2
- data/ext/polyphony/queue.c +52 -12
- data/ext/polyphony/thread.c +55 -42
- data/lib/polyphony.rb +25 -39
- data/lib/polyphony/adapters/irb.rb +2 -17
- data/lib/polyphony/adapters/mysql2.rb +19 -0
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +2 -2
- data/lib/polyphony/adapters/readline.rb +17 -0
- data/lib/polyphony/adapters/sequel.rb +45 -0
- data/lib/polyphony/core/channel.rb +3 -34
- data/lib/polyphony/core/exceptions.rb +11 -0
- data/lib/polyphony/core/global_api.rb +11 -6
- data/lib/polyphony/core/resource_pool.rb +22 -71
- data/lib/polyphony/core/sync.rb +48 -9
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +37 -19
- data/lib/polyphony/extensions/fiber.rb +5 -1
- data/lib/polyphony/extensions/io.rb +7 -8
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +12 -22
- data/lib/polyphony/extensions/thread.rb +6 -5
- data/lib/polyphony/net.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +6 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_event.rb +1 -0
- data/test/test_fiber.rb +21 -5
- data/test/test_io.rb +1 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_queue.rb +20 -0
- data/test/test_resource_pool.rb +34 -43
- data/test/test_signal.rb +5 -29
- data/test/test_sync.rb +52 -0
- metadata +74 -30
- data/.gitbook.yaml +0 -4
- data/lib/polyphony/event.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9c8ab74213c6cc5e3852f73ee027ee496f2b04c6844e09856c9771ea5e7839a
|
4
|
+
data.tar.gz: 83d7c533024b6d6d633b9e18abc392911adfc07f728af826bd84cce35cafb20c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c42e49ebcd6fb10b384438cb7688801a387963bc2a6fb2c1d3de6d022a7731a7d88db138a34b364b35c9d71830b975a69cb1f14fb277166a9db1802e24e82dec
|
7
|
+
data.tar.gz: 6c578eacded00dd7a77a35124c69ebc966e4f9aa42264913df82af147f43955a2ecd86087630c085fcd4d6e964aa705202c5aa24bb4032648d3ba278d60d4f63
|
data/.rubocop.yml
CHANGED
@@ -81,6 +81,7 @@ Lint/SuppressedException:
|
|
81
81
|
- examples/**/*.rb
|
82
82
|
|
83
83
|
Metrics/MethodLength:
|
84
|
+
Max: 12
|
84
85
|
Exclude:
|
85
86
|
- lib/polyphony/http/server/rack.rb
|
86
87
|
- lib/polyphony/extensions/io.rb
|
@@ -111,6 +112,7 @@ Style/Documentation:
|
|
111
112
|
Exclude:
|
112
113
|
- test/**/*.rb
|
113
114
|
- examples/**/*.rb
|
115
|
+
- lib/polyphony/adapters/**/*.rb
|
114
116
|
|
115
117
|
Style/FormatString:
|
116
118
|
Exclude:
|
@@ -172,4 +174,8 @@ Style/RedundantRegexpEscape:
|
|
172
174
|
Enabled: true
|
173
175
|
|
174
176
|
Style/SlicingWithRange:
|
175
|
-
Enabled: true
|
177
|
+
Enabled: true
|
178
|
+
|
179
|
+
Style/RaiseArgs:
|
180
|
+
Exclude:
|
181
|
+
- lib/polyphony/extensions/fiber.rb
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,41 @@
|
|
1
|
+
## 0.45.0
|
2
|
+
|
3
|
+
* Cleanup code
|
4
|
+
* Rename `Agent` to `Backend`
|
5
|
+
* Implement `Polyphony::ConditionVariable`
|
6
|
+
* Fix Kernel.system
|
7
|
+
|
8
|
+
## 0.44.0 2020-07-25
|
9
|
+
|
10
|
+
* Fix reentrant `ResourcePool` (#38)
|
11
|
+
* Add `ResourcePool#discard!` (#35)
|
12
|
+
* Add `Mysql2::Client` and `Sequel::ConnectionPool` adapters (#35)
|
13
|
+
* Reimplement `Kernel.trap` using `Fiber#interject`
|
14
|
+
* Add `Fiber#interject` for running arbitrary code on arbitrary fibers (#39)
|
15
|
+
|
16
|
+
## 0.43.11 2020-07-24
|
17
|
+
|
18
|
+
* Dump uncaught exception info for forked process (#36)
|
19
|
+
* Add additional socket config options (#37)
|
20
|
+
- :reuse_port (`SO_REUSEPORT`)
|
21
|
+
- :backlog (listen backlog, default `SOMAXCONN`)
|
22
|
+
* Fix possible race condition in Queue#shift (#34)
|
23
|
+
|
24
|
+
## 0.43.10 2020-07-23
|
25
|
+
|
26
|
+
* Fix race condition when terminating fibers (#33)
|
27
|
+
* Fix lock release in `Mutex` (#32)
|
28
|
+
* Virtualize agent interface
|
29
|
+
* Implement `LibevAgent_connect`
|
30
|
+
|
31
|
+
## 0.43.9 2020-07-22
|
32
|
+
|
33
|
+
* Rewrite `Channel` using `Queue`
|
34
|
+
* Rewrite `Mutex` using `Queue`
|
35
|
+
* Reimplement `Event` in C to prevent cross-thread race condition
|
36
|
+
* Reimplement `ResourcePool` using `Queue`
|
37
|
+
* Implement `Queue#size`
|
38
|
+
|
1
39
|
## 0.43.8 2020-07-21
|
2
40
|
|
3
41
|
* Rename `LibevQueue` to `Queue`
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
polyphony (0.
|
4
|
+
polyphony (0.45.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -11,6 +11,7 @@ GEM
|
|
11
11
|
ansi (1.5.0)
|
12
12
|
ast (2.4.0)
|
13
13
|
builder (3.2.4)
|
14
|
+
coderay (1.1.3)
|
14
15
|
colorator (1.1.0)
|
15
16
|
concurrent-ruby (1.1.6)
|
16
17
|
docile (1.3.2)
|
@@ -22,9 +23,6 @@ GEM
|
|
22
23
|
forwardable-extended (2.6.0)
|
23
24
|
hiredis (0.6.3)
|
24
25
|
http_parser.rb (0.6.0)
|
25
|
-
httparty (0.17.0)
|
26
|
-
mime-types (~> 3.0)
|
27
|
-
multi_xml (>= 0.5.2)
|
28
26
|
i18n (0.9.5)
|
29
27
|
concurrent-ruby (~> 1.0)
|
30
28
|
jekyll (3.8.6)
|
@@ -60,25 +58,26 @@ GEM
|
|
60
58
|
listen (3.2.1)
|
61
59
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
62
60
|
rb-inotify (~> 0.9, >= 0.9.10)
|
63
|
-
localhost (1.1.4)
|
64
61
|
mercenary (0.3.6)
|
65
|
-
|
66
|
-
mime-types-data (~> 3.2015)
|
67
|
-
mime-types-data (3.2019.1009)
|
62
|
+
method_source (1.0.0)
|
68
63
|
minitest (5.13.0)
|
69
64
|
minitest-reporters (1.4.2)
|
70
65
|
ansi
|
71
66
|
builder
|
72
67
|
minitest (>= 5.0)
|
73
68
|
ruby-progressbar
|
74
|
-
|
69
|
+
mysql2 (0.5.3)
|
75
70
|
parallel (1.19.1)
|
76
71
|
parser (2.7.0.2)
|
77
72
|
ast (~> 2.4.0)
|
78
73
|
pathutil (0.16.2)
|
79
74
|
forwardable-extended (~> 2.6)
|
80
75
|
pg (1.1.4)
|
76
|
+
pry (0.13.1)
|
77
|
+
coderay (~> 1.1)
|
78
|
+
method_source (~> 1.0)
|
81
79
|
public_suffix (4.0.3)
|
80
|
+
rack (2.2.3)
|
82
81
|
rainbow (3.0.0)
|
83
82
|
rake (12.3.3)
|
84
83
|
rake-compiler (1.0.5)
|
@@ -109,6 +108,7 @@ GEM
|
|
109
108
|
sass-listen (4.0.0)
|
110
109
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
111
110
|
rb-inotify (~> 0.9, >= 0.9.7)
|
111
|
+
sequel (5.34.0)
|
112
112
|
simplecov (0.17.1)
|
113
113
|
docile (~> 1.1)
|
114
114
|
json (>= 1.8, < 3)
|
@@ -122,19 +122,21 @@ PLATFORMS
|
|
122
122
|
DEPENDENCIES
|
123
123
|
hiredis (= 0.6.3)
|
124
124
|
http_parser.rb (~> 0.6.0)
|
125
|
-
httparty (= 0.17.0)
|
126
125
|
jekyll (~> 3.8.6)
|
127
126
|
jekyll-remote-theme (~> 0.4.1)
|
128
127
|
jekyll-seo-tag (~> 2.6.1)
|
129
128
|
just-the-docs (~> 0.3.0)
|
130
|
-
localhost (= 1.1.4)
|
131
129
|
minitest (= 5.13.0)
|
132
130
|
minitest-reporters (= 1.4.2)
|
131
|
+
mysql2 (= 0.5.3)
|
133
132
|
pg (= 1.1.4)
|
134
133
|
polyphony!
|
134
|
+
pry (= 0.13.1)
|
135
|
+
rack (>= 2.0.8, < 2.3.0)
|
135
136
|
rake-compiler (= 1.0.5)
|
136
137
|
redis (= 4.1.0)
|
137
138
|
rubocop (= 0.85.1)
|
139
|
+
sequel (= 5.34.0)
|
138
140
|
simplecov (= 0.17.1)
|
139
141
|
|
140
142
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -1,10 +1,25 @@
|
|
1
|
-
<
|
1
|
+
<h1 align="center">
|
2
|
+
<a href="https://digital-fabric.github.io/polyphony/">
|
3
|
+
<img src="docs/polyphony-logo.png" alt="Polyphony">
|
4
|
+
</a>
|
5
|
+
<br>
|
6
|
+
Polyphony
|
7
|
+
<br>
|
8
|
+
</h1>
|
2
9
|
|
3
|
-
|
10
|
+
<h4 align="center">Fine-Grained Concurrency for Ruby</h4>
|
4
11
|
|
5
|
-
|
6
|
-
|
7
|
-
|
12
|
+
<p align="center">
|
13
|
+
<a href="http://rubygems.org/gems/polyphony">
|
14
|
+
<img src="https://badge.fury.io/rb/polyphony.svg" alt="Ruby gem">
|
15
|
+
</a>
|
16
|
+
<a href="https://github.com/digital-fabric/polyphony/actions?query=workflow%3ATests">
|
17
|
+
<img src="https://github.com/digital-fabric/polyphony/workflows/Tests/badge.svg" alt="Tests">
|
18
|
+
</a>
|
19
|
+
<a href="https://github.com/digital-fabric/polyphony/blob/master/LICENSE">
|
20
|
+
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
|
21
|
+
</a>
|
22
|
+
</p>
|
8
23
|
|
9
24
|
[DOCS](https://digital-fabric.github.io/polyphony/) |
|
10
25
|
[EXAMPLES](examples)
|
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ task :stress_test do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
task :docs do
|
23
|
-
exec 'RUBYOPT=-W0 jekyll serve -s docs -H ec2-
|
23
|
+
exec 'RUBYOPT=-W0 jekyll serve -s docs -H ec2-18-156-117-172.eu-central-1.compute.amazonaws.com'
|
24
24
|
end
|
25
25
|
|
26
26
|
CLEAN.include "**/*.o", "**/*.so", "**/*.bundle", "**/*.jar", "pkg", "tmp"
|
data/TODO.md
CHANGED
@@ -1,13 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
-
|
5
|
-
-
|
1
|
+
0.45
|
2
|
+
|
3
|
+
- Review all code
|
4
|
+
- Cleanup C code
|
5
|
+
- Cleanup and annotate examples (and remove all the examples used for
|
6
|
+
debugging). Focus on examples that serve as "how-to".
|
7
|
+
|
8
|
+
0.45.1
|
9
|
+
|
10
|
+
- Adapter for Pry and IRB (Which fixes #5 and #6)
|
11
|
+
|
12
|
+
0.46.0
|
6
13
|
|
7
14
|
- Debugging
|
8
15
|
- Eat your own dogfood: need a good tool to check what's going on when some
|
9
16
|
test fails
|
10
|
-
- Needs to work with Pry (can write perhaps an extension for pry)
|
11
17
|
- First impl in Ruby using `TracePoint` API
|
12
18
|
- Mode of operation:
|
13
19
|
- Two parts: tracer and controller
|
@@ -117,7 +123,7 @@
|
|
117
123
|
- discuss using `snooze` for ensuring responsiveness when executing CPU-bound work
|
118
124
|
|
119
125
|
|
120
|
-
## 0.
|
126
|
+
## 0.47
|
121
127
|
|
122
128
|
### Some more API work, more docs
|
123
129
|
|
@@ -130,13 +136,13 @@
|
|
130
136
|
- proceed from there
|
131
137
|
|
132
138
|
|
133
|
-
## 0.
|
139
|
+
## 0.48
|
134
140
|
|
135
141
|
### Sinatra / Sidekiq
|
136
142
|
|
137
143
|
- Pull out redis/postgres code, put into new `polyphony-xxx` gems
|
138
144
|
|
139
|
-
## 0.
|
145
|
+
## 0.49
|
140
146
|
|
141
147
|
### Testing && Docs
|
142
148
|
|
@@ -148,11 +154,7 @@
|
|
148
154
|
- `IO.foreach`
|
149
155
|
- `Process.waitpid`
|
150
156
|
|
151
|
-
## 0.
|
152
|
-
|
153
|
-
### Real IO#gets and IO#read
|
154
|
-
|
155
|
-
## 0.48 DNS
|
157
|
+
## 0.50 DNS
|
156
158
|
|
157
159
|
### DNS client
|
158
160
|
|
data/bin/stress.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
`rake recompile`
|
5
|
+
|
6
|
+
count = ARGV[0] ? ARGV[0].to_i : 100
|
7
|
+
|
8
|
+
TEST_CMD = 'ruby test/run.rb'
|
9
|
+
|
10
|
+
def run_test(count)
|
11
|
+
puts "#{count}: running tests..."
|
12
|
+
system(TEST_CMD)
|
13
|
+
return if $?.exitstatus == 0
|
14
|
+
|
15
|
+
puts "Failure after #{count} tests"
|
16
|
+
exit!
|
17
|
+
end
|
18
|
+
|
19
|
+
trap('INT') { exit! }
|
20
|
+
t0 = Time.now
|
21
|
+
count.times { |i| run_test(i + 1) }
|
22
|
+
elapsed = Time.now - t0
|
23
|
+
puts format(
|
24
|
+
"Successfully ran %d tests in %f seconds (%f per test)",
|
25
|
+
count,
|
26
|
+
elapsed,
|
27
|
+
elapsed / count
|
28
|
+
)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Polyphony 0.44.1
|
2
|
+
|
3
|
+
## More performance, more compatibility, more robustness
|
4
|
+
|
5
|
+
The last three weeks have been very busy for Polyphony. Since I first presented
|
6
|
+
Polyphony here and elsewhere, 17 issues were closed, 10 pull requests were
|
7
|
+
merged, and 144 commits were made by 4 different authors. I'm really
|
8
|
+
excited about Polyphony and the momentum it seems to be gathering. Your
|
9
|
+
reactions have been very positive so far (it even got [tweeted by
|
10
|
+
Matz!](https://twitter.com/yukihiro_matz/status/1279289318083715073))
|
11
|
+
|
12
|
+
I'm even more excited about the contributions Polyphony is starting to get from
|
13
|
+
other developers. Thank you [Will](https://github.com/wjordan),
|
14
|
+
[Máximo](https://github.com/ElMassimo) and [Trent](https://github.com/misfo) for
|
15
|
+
your valuable contributions! Also, the Polyphony project has now got a logo
|
16
|
+
designed by my friend [Gérald Morales](https://webocube.com/).
|
17
|
+
|
18
|
+
I'd like to encourage other developers to get in on the action and start
|
19
|
+
contributing by testing Polyphony, creating issues and writing code and
|
20
|
+
documentation. Together we can make Polyphony a game-changer for developing
|
21
|
+
concurrent apps in Ruby, and finally put to rest the notion that "Ruby is slow"!
|
22
|
+
|
23
|
+
Since the last public release of Polyphony, we have focused on fixing bugs,
|
24
|
+
improving performance and introducing new features that improve the Polyphony
|
25
|
+
developer experience. Polyphony 0.44 is up to 20% percent faster than the
|
26
|
+
previous release, due notably to a new ring-buffer implementation used by the
|
27
|
+
fiber run queue and the `Polyphony::Queue` class, a new `Backend#read_loop` API
|
28
|
+
for tighter server loops, and minimizing `fcntl` syscalls when doing I/O. These
|
29
|
+
and other minor improvements have resulted in Polyphony first crossing the
|
30
|
+
50,000 requests per second threshold for the first time in a minimal [rack
|
31
|
+
server
|
32
|
+
example](https://github.com/digital-fabric/polyphony/blob/master/examples/io/xx-rack_server.rb).
|
33
|
+
|
34
|
+
Notable new features include a MySQL adapter, a Sequel adapter, and a new
|
35
|
+
`Fiber#interject` API that allows executing arbitrary code on arbitrary fibers.
|
36
|
+
|
37
|
+
We have also fixed numerous bugs, among which an issue building Polyphony on
|
38
|
+
MacOS, problems issuing `Net::HTTP` requests with secure URLs, an issue with
|
39
|
+
`YAML.load` and much more...
|
40
|
+
|
41
|
+
For the full list of changes please consult the [change log](https://github.com/digital-fabric/polyphony/blob/master/CHANGELOG.md).
|
42
|
+
|
43
|
+
## What's next for Polyphony?
|
44
|
+
|
45
|
+
The next release of Polyphony will focus on full support IRB and Pry. Being able
|
46
|
+
to run operations in the background in IRB and Pry can be very beneficial, most
|
47
|
+
of all when developing and when debugging running processes using `binding.pry`
|
48
|
+
for example.
|
49
|
+
|
50
|
+
Subsequent releases will introduce a whole new full-featured debugger for
|
51
|
+
fiber-aware concurrent apps, and eventually full support for Sequel, Sinatra,
|
52
|
+
Hanami, Sidekiq and other major areas of the Ruby ecosystem.
|
53
|
+
|
54
|
+
## Tipi - a polyphonic web server for Ruby
|
55
|
+
|
56
|
+
[Tipi](https://github.com/digital-fabric/tipi) is a new web server for Ruby
|
57
|
+
apps. It is intended to be *the* go-to app server for Ruby apps looking for
|
58
|
+
robustness, scalability and performance. Tipi already supports HTTP/1, HTTP/2,
|
59
|
+
WebSockets and SSL termination. It can currently drive simple Rack apps. In the
|
60
|
+
future Tipi will be fully compliant with the Rack specification, and will also
|
61
|
+
offer a static file server, a rich configuration and automatic TLS certificates
|
62
|
+
(using Let's Encrypt) out of the box.
|
63
|
+
|
64
|
+
For those wondering about performance, here are some preliminary numbers (see
|
65
|
+
disclaimer below):
|
66
|
+
|
67
|
+
- HTTP, hello world, single process: ~50000 requests/second
|
68
|
+
- HTTP, Rack hello world app, single process: ~33000 requests/second
|
69
|
+
- HTTP, Rack hello world, 4 worker processes: ~95000 requests/second
|
70
|
+
- HTTPS, hello world, single process: ~20000 requests/second
|
71
|
+
- HTTPS, hello world, 4 worker processes: ~72000 requests/second
|
72
|
+
|
73
|
+
Disclaimer: these numbers should be taken with a grain of salt. They do not
|
74
|
+
follow any established benchmarking methodology, and may vary significantly. The
|
75
|
+
different configurtations were benchmarked using the command: `wrk -d10 -t1 -c10
|
76
|
+
"<http|https>://127.0.0.1:1234/"` on the same machine (an `m2.xlarge` instance)
|
77
|
+
as the server. In the future Tipi's performance might substantially change. YMMV.
|
@@ -12,7 +12,7 @@ Polyphony enhances the core `Thread` class with APIs for switching and
|
|
12
12
|
scheduling fibers, and reimplements some of its APIs such as `Thread#raise`
|
13
13
|
using fibers which, incidentally, make it safe.
|
14
14
|
|
15
|
-
Each thread has its own run queue and its own system
|
15
|
+
Each thread has its own run queue and its own system backend. While running
|
16
16
|
multiple threads does not result in true parallelism in MRI Ruby, sometimes
|
17
17
|
multithreading is inevitable, for instance when using third-party gems that
|
18
18
|
spawn threads, or when calling blocking APIs that are not fiber-aware.
|
@@ -341,25 +341,25 @@ move_on_after(10) { perform_query }
|
|
341
341
|
cancel_after(10) { perform_query }
|
342
342
|
```
|
343
343
|
|
344
|
-
## The
|
344
|
+
## The Polyphony Backend
|
345
345
|
|
346
346
|
In order to implement automatic fiber switching when performing blocking
|
347
|
-
operations, Polyphony introduces a concept called the *system
|
348
|
-
|
347
|
+
operations, Polyphony introduces a concept called the *system backend*. The system
|
348
|
+
backend is an object having a uniform interface, that performs all blocking
|
349
349
|
operations.
|
350
350
|
|
351
351
|
While a standard event loop-based solution would implement a blocking call
|
352
|
-
separately from the fiber scheduling, the system
|
352
|
+
separately from the fiber scheduling, the system backend integrates the two to
|
353
353
|
create a blocking call that is already knows how to switch and schedule fibers.
|
354
354
|
For example, in Polyphony all APIs having to do with reading from files or
|
355
|
-
sockets end up calling `Thread.current.
|
355
|
+
sockets end up calling `Thread.current.backend.read`, which does all the work.
|
356
356
|
|
357
357
|
This design offers some major advantages over other designs. It minimizes memory
|
358
358
|
allocations, of both Ruby objects and C structures. For example, instead of
|
359
359
|
having to allocate libev watchers on the heap and then pass them around, they
|
360
360
|
are allocated on the stack instead, which saves up on both memory and CPU cycles.
|
361
361
|
|
362
|
-
In addition, the
|
362
|
+
In addition, the backend interface includes two methods that allow maximizing
|
363
363
|
server performance by accepting connections and reading from sockets in a tight
|
364
364
|
loop. Here's a naive implementation of an HTTP/1 server:
|
365
365
|
|
@@ -372,7 +372,7 @@ def handle_client(socket)
|
|
372
372
|
reqs = []
|
373
373
|
parser.on_message_complete = proc { |env| reqs << { foo: :bar } }
|
374
374
|
|
375
|
-
Thread.current.
|
375
|
+
Thread.current.backend.read_loop(socket) do |data|
|
376
376
|
parser << data
|
377
377
|
reqs.each { |r| reply(socket, r) }
|
378
378
|
reqs.clear
|
@@ -388,20 +388,20 @@ end
|
|
388
388
|
server = TCPServer.open('0.0.0.0', 1234)
|
389
389
|
puts "listening on port 1234"
|
390
390
|
|
391
|
-
Thread.current.
|
391
|
+
Thread.current.backend.accept_loop(server) do |client|
|
392
392
|
spin { handle_client(client) }
|
393
393
|
end
|
394
394
|
```
|
395
395
|
|
396
|
-
The `#read_loop` and `#accept_loop`
|
396
|
+
The `#read_loop` and `#accept_loop` backend methods implement tight loops that
|
397
397
|
provide a significant boost to performance (up to +30% better throughput.)
|
398
398
|
|
399
|
-
Currently, Polyphony includes a single system
|
399
|
+
Currently, Polyphony includes a single system backend based on
|
400
400
|
[libev](http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod). In the future,
|
401
|
-
Polyphony will include other platform-specific system
|
402
|
-
|
401
|
+
Polyphony will include other platform-specific system backends, such as a Windows
|
402
|
+
backend using
|
403
403
|
[IOCP](https://docs.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports),
|
404
|
-
or an [io_uring](https://unixism.net/loti/what_is_io_uring.html)
|
404
|
+
or an [io_uring](https://unixism.net/loti/what_is_io_uring.html) backend,
|
405
405
|
which might be a game-changer for writing highly-concurrent Ruby-based web apps.
|
406
406
|
|
407
407
|
## Writing Web Apps with Polyphony
|
@@ -482,5 +482,5 @@ reach version 1.0. Here are some of the exciting directions we're working on.
|
|
482
482
|
|
483
483
|
- Support for more core and stdlib APIs
|
484
484
|
- More adapters for gems with C-extensions, such as `mysql`, `sqlite3` etc
|
485
|
-
- Use `io_uring`
|
485
|
+
- Use `io_uring` backend as alternative to the libev backend
|
486
486
|
- More concurrency constructs for building highly concurrent applications
|