polyphony 0.43.11 → 0.45.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -1
- data/CHANGELOG.md +40 -0
- data/Gemfile.lock +18 -8
- data/Rakefile +1 -1
- data/TODO.md +22 -9
- 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/redis_client.rb +3 -1
- data/examples/adapters/redis_pubsub_perf.rb +11 -8
- data/examples/adapters/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- data/examples/adapters/sequel_pg.rb +24 -0
- data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
- data/examples/core/{xx-channels.rb → channels.rb} +0 -0
- data/examples/core/deferring-an-operation.rb +16 -0
- data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
- data/examples/core/{xx-forking.rb → forking.rb} +1 -1
- data/examples/core/handling-signals.rb +11 -0
- data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
- data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
- data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
- data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
- data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
- data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
- data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
- data/examples/core/supervisor.rb +20 -0
- data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
- data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
- data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
- data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
- data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
- data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
- data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
- data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
- data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
- data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
- data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
- data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
- data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
- data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
- data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
- data/examples/io/{xx-irb.rb → irb.rb} +0 -0
- data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
- data/examples/io/{xx-open.rb → open.rb} +0 -0
- data/examples/io/pry.rb +18 -0
- data/examples/io/rack_server.rb +71 -0
- data/examples/io/raw.rb +14 -0
- data/examples/io/reline.rb +18 -0
- data/examples/io/{xx-system.rb → system.rb} +1 -1
- data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
- data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
- data/examples/io/tunnel.rb +6 -1
- data/examples/io/{xx-zip.rb → zip.rb} +0 -0
- data/examples/performance/fiber_transfer.rb +2 -1
- data/examples/performance/fs_read.rb +5 -6
- data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
- data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
- data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
- data/examples/performance/thread_pool_perf.rb +6 -7
- data/ext/polyphony/backend.h +40 -0
- data/ext/polyphony/event.c +3 -3
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/fiber.c +66 -6
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +237 -238
- data/ext/polyphony/polyphony.c +3 -3
- data/ext/polyphony/polyphony.h +15 -20
- data/ext/polyphony/polyphony_ext.c +3 -4
- data/ext/polyphony/queue.c +5 -6
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/thread.c +36 -33
- data/lib/polyphony.rb +26 -39
- data/lib/polyphony/adapters/fs.rb +1 -1
- 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 -5
- data/lib/polyphony/adapters/readline.rb +17 -0
- data/lib/polyphony/adapters/redis.rb +1 -1
- data/lib/polyphony/adapters/sequel.rb +45 -0
- data/lib/polyphony/core/exceptions.rb +11 -0
- data/lib/polyphony/core/global_api.rb +17 -12
- data/lib/polyphony/core/resource_pool.rb +20 -7
- data/lib/polyphony/core/sync.rb +46 -8
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +30 -30
- data/lib/polyphony/extensions/fiber.rb +30 -49
- data/lib/polyphony/extensions/io.rb +60 -16
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +14 -15
- data/lib/polyphony/extensions/thread.rb +6 -5
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +7 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_fiber.rb +29 -12
- data/test/test_io.rb +59 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_resource_pool.rb +29 -4
- data/test/test_signal.rb +16 -37
- data/test/test_socket.rb +17 -0
- data/test/test_sync.rb +52 -0
- metadata +127 -97
- data/.gitbook.yaml +0 -4
- data/examples/adapters/concurrent-ruby.rb +0 -9
- data/examples/core/04-handling-signals.rb +0 -19
- data/examples/core/xx-agent.rb +0 -102
- data/examples/core/xx-at_exit.rb +0 -29
- data/examples/core/xx-caller.rb +0 -12
- data/examples/core/xx-daemon.rb +0 -14
- data/examples/core/xx-deadlock.rb +0 -8
- data/examples/core/xx-deferring-an-operation.rb +0 -14
- data/examples/core/xx-exception-backtrace.rb +0 -40
- data/examples/core/xx-fork-cleanup.rb +0 -22
- data/examples/core/xx-fork-spin.rb +0 -42
- data/examples/core/xx-fork-terminate.rb +0 -27
- data/examples/core/xx-move_on.rb +0 -23
- data/examples/core/xx-queue-async.rb +0 -120
- data/examples/core/xx-readpartial.rb +0 -18
- data/examples/core/xx-signals.rb +0 -16
- data/examples/core/xx-sleep-forever.rb +0 -9
- data/examples/core/xx-sleeping.rb +0 -25
- data/examples/core/xx-snooze-starve.rb +0 -16
- data/examples/core/xx-spin-fork.rb +0 -49
- data/examples/core/xx-state-machine.rb +0 -51
- data/examples/core/xx-stop.rb +0 -20
- data/examples/core/xx-supervisors.rb +0 -21
- data/examples/core/xx-thread-selector-sleep.rb +0 -51
- data/examples/core/xx-thread-selector-snooze.rb +0 -46
- data/examples/core/xx-thread-snooze.rb +0 -34
- data/examples/core/xx-timer-gc.rb +0 -17
- data/examples/core/xx-trace.rb +0 -79
- data/examples/performance/xx-array.rb +0 -11
- data/examples/performance/xx-fiber-switch.rb +0 -9
- data/examples/performance/xx-snooze.rb +0 -15
- data/examples/xx-spin.rb +0 -32
- data/ext/polyphony/agent.h +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52f8ebf1104d921c9e3b0afa0b4605ee8f912eabe8b6264898f23905d2cb6c6f
|
4
|
+
data.tar.gz: 38f0f7cd62997ba5681185c3c4859f57d86de875d8292faf67fffa53c7160181
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2429259a2e79757ec879c4c00db8fd8b87302b9e0a61c6ae2ddd5fdb6e1a3a06ac63d551ed33a4c77480e156eb1247a0fab4a263179a60fd2e9a2d3173831a58
|
7
|
+
data.tar.gz: 04f58dafb5faf1e21b08fd85d508ceb6e9869b0a69d63c133661a834f169eb750b599639ffdbff474abf7f92bc6bfee3a43b9442c2885a6dd88328657b0e1d36
|
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
|
@@ -95,6 +96,7 @@ Metrics/ModuleLength:
|
|
95
96
|
Metrics/ClassLength:
|
96
97
|
Exclude:
|
97
98
|
- lib/polyphony/http/server/http1.rb
|
99
|
+
- lib/polyphony/extensions/io.rb
|
98
100
|
- test/**/*.rb
|
99
101
|
- examples/**/*.rb
|
100
102
|
|
@@ -111,6 +113,7 @@ Style/Documentation:
|
|
111
113
|
Exclude:
|
112
114
|
- test/**/*.rb
|
113
115
|
- examples/**/*.rb
|
116
|
+
- lib/polyphony/adapters/**/*.rb
|
114
117
|
|
115
118
|
Style/FormatString:
|
116
119
|
Exclude:
|
@@ -172,4 +175,8 @@ Style/RedundantRegexpEscape:
|
|
172
175
|
Enabled: true
|
173
176
|
|
174
177
|
Style/SlicingWithRange:
|
175
|
-
Enabled: true
|
178
|
+
Enabled: true
|
179
|
+
|
180
|
+
Style/RaiseArgs:
|
181
|
+
Exclude:
|
182
|
+
- lib/polyphony/extensions/fiber.rb
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,43 @@
|
|
1
|
+
## 0.45.4
|
2
|
+
|
3
|
+
* Improve signal trapping mechanism
|
4
|
+
|
5
|
+
## 0.45.3
|
6
|
+
|
7
|
+
* Don't swallow error in `Process#kill_and_await`
|
8
|
+
* Add `Fiber#mailbox` attribute reader
|
9
|
+
* Fix bug in `Fiber.await`
|
10
|
+
* Implement `IO#getc`, `IO#getbyte`
|
11
|
+
|
12
|
+
## 0.45.2
|
13
|
+
|
14
|
+
* Rewrite `Fiber#<<`, `Fiber#await`, `Fiber#receive` in C
|
15
|
+
|
16
|
+
## 0.45.1
|
17
|
+
|
18
|
+
* Fix Net::HTTP compatibility
|
19
|
+
* Fix fs adapter
|
20
|
+
* Improve performance of IO#puts
|
21
|
+
* Mutex#synchronize
|
22
|
+
* Fix Socket#connect
|
23
|
+
* Cleanup code
|
24
|
+
* Improve support for Ruby 3 keyword args
|
25
|
+
|
26
|
+
## 0.45.0
|
27
|
+
|
28
|
+
* Cleanup code
|
29
|
+
* Rename `Agent` to `Backend`
|
30
|
+
* Implement `Polyphony::ConditionVariable`
|
31
|
+
* Fix Kernel.system
|
32
|
+
|
33
|
+
## 0.44.0 2020-07-25
|
34
|
+
|
35
|
+
* Fix reentrant `ResourcePool` (#38)
|
36
|
+
* Add `ResourcePool#discard!` (#35)
|
37
|
+
* Add `Mysql2::Client` and `Sequel::ConnectionPool` adapters (#35)
|
38
|
+
* Reimplement `Kernel.trap` using `Fiber#interject`
|
39
|
+
* Add `Fiber#interject` for running arbitrary code on arbitrary fibers (#39)
|
40
|
+
|
1
41
|
## 0.43.11 2020-07-24
|
2
42
|
|
3
43
|
* Dump uncaught exception info for forked process (#36)
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
polyphony (0.
|
4
|
+
polyphony (0.45.4)
|
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,7 +23,7 @@ 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.
|
26
|
+
httparty (0.17.1)
|
26
27
|
mime-types (~> 3.0)
|
27
28
|
multi_xml (>= 0.5.2)
|
28
29
|
i18n (0.9.5)
|
@@ -60,11 +61,11 @@ GEM
|
|
60
61
|
listen (3.2.1)
|
61
62
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
62
63
|
rb-inotify (~> 0.9, >= 0.9.10)
|
63
|
-
localhost (1.1.4)
|
64
64
|
mercenary (0.3.6)
|
65
|
+
method_source (1.0.0)
|
65
66
|
mime-types (3.3.1)
|
66
67
|
mime-types-data (~> 3.2015)
|
67
|
-
mime-types-data (3.
|
68
|
+
mime-types-data (3.2020.0512)
|
68
69
|
minitest (5.13.0)
|
69
70
|
minitest-reporters (1.4.2)
|
70
71
|
ansi
|
@@ -72,16 +73,21 @@ GEM
|
|
72
73
|
minitest (>= 5.0)
|
73
74
|
ruby-progressbar
|
74
75
|
multi_xml (0.6.0)
|
76
|
+
mysql2 (0.5.3)
|
75
77
|
parallel (1.19.1)
|
76
78
|
parser (2.7.0.2)
|
77
79
|
ast (~> 2.4.0)
|
78
80
|
pathutil (0.16.2)
|
79
81
|
forwardable-extended (~> 2.6)
|
80
82
|
pg (1.1.4)
|
83
|
+
pry (0.13.1)
|
84
|
+
coderay (~> 1.1)
|
85
|
+
method_source (~> 1.0)
|
81
86
|
public_suffix (4.0.3)
|
87
|
+
rack (2.2.3)
|
82
88
|
rainbow (3.0.0)
|
83
89
|
rake (12.3.3)
|
84
|
-
rake-compiler (1.
|
90
|
+
rake-compiler (1.1.1)
|
85
91
|
rake
|
86
92
|
rb-fsevent (0.10.3)
|
87
93
|
rb-inotify (0.10.1)
|
@@ -109,6 +115,7 @@ GEM
|
|
109
115
|
sass-listen (4.0.0)
|
110
116
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
111
117
|
rb-inotify (~> 0.9, >= 0.9.7)
|
118
|
+
sequel (5.34.0)
|
112
119
|
simplecov (0.17.1)
|
113
120
|
docile (~> 1.1)
|
114
121
|
json (>= 1.8, < 3)
|
@@ -122,19 +129,22 @@ PLATFORMS
|
|
122
129
|
DEPENDENCIES
|
123
130
|
hiredis (= 0.6.3)
|
124
131
|
http_parser.rb (~> 0.6.0)
|
125
|
-
httparty (= 0.17.
|
132
|
+
httparty (= 0.17.1)
|
126
133
|
jekyll (~> 3.8.6)
|
127
134
|
jekyll-remote-theme (~> 0.4.1)
|
128
135
|
jekyll-seo-tag (~> 2.6.1)
|
129
136
|
just-the-docs (~> 0.3.0)
|
130
|
-
localhost (= 1.1.4)
|
131
137
|
minitest (= 5.13.0)
|
132
138
|
minitest-reporters (= 1.4.2)
|
139
|
+
mysql2 (= 0.5.3)
|
133
140
|
pg (= 1.1.4)
|
134
141
|
polyphony!
|
135
|
-
|
142
|
+
pry (= 0.13.1)
|
143
|
+
rack (>= 2.0.8, < 2.3.0)
|
144
|
+
rake-compiler (= 1.1.1)
|
136
145
|
redis (= 4.1.0)
|
137
146
|
rubocop (= 0.85.1)
|
147
|
+
sequel (= 5.34.0)
|
138
148
|
simplecov (= 0.17.1)
|
139
149
|
|
140
150
|
BUNDLED WITH
|
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,7 +1,24 @@
|
|
1
|
+
(
|
2
|
+
io_uring: some work has been done on an io_uring based scheduler here:
|
3
|
+
https://github.com/dsh0416/evt
|
4
|
+
|
5
|
+
This can serve as a starting point for doing stuff with io_uring
|
6
|
+
)
|
7
|
+
|
8
|
+
0.45.4
|
9
|
+
|
10
|
+
- Adapter for io/console (what does `IO#raw` do?)
|
11
|
+
- Adapter for Pry and IRB (Which fixes #5 and #6)
|
12
|
+
- Improve `#supervise`. It does not work as advertised, and seems to exhibit an
|
13
|
+
inconsistent behaviour (see supervisor example).
|
14
|
+
- Fix backtrace for `Timeout.timeout` API (see timeout example).
|
15
|
+
- Check why worker-thread example doesn't work.
|
16
|
+
|
17
|
+
0.46.0
|
18
|
+
|
1
19
|
- Debugging
|
2
20
|
- Eat your own dogfood: need a good tool to check what's going on when some
|
3
21
|
test fails
|
4
|
-
- Needs to work with Pry (can write perhaps an extension for pry)
|
5
22
|
- First impl in Ruby using `TracePoint` API
|
6
23
|
- Mode of operation:
|
7
24
|
- Two parts: tracer and controller
|
@@ -111,7 +128,7 @@
|
|
111
128
|
- discuss using `snooze` for ensuring responsiveness when executing CPU-bound work
|
112
129
|
|
113
130
|
|
114
|
-
## 0.
|
131
|
+
## 0.47
|
115
132
|
|
116
133
|
### Some more API work, more docs
|
117
134
|
|
@@ -124,13 +141,13 @@
|
|
124
141
|
- proceed from there
|
125
142
|
|
126
143
|
|
127
|
-
## 0.
|
144
|
+
## 0.48
|
128
145
|
|
129
146
|
### Sinatra / Sidekiq
|
130
147
|
|
131
148
|
- Pull out redis/postgres code, put into new `polyphony-xxx` gems
|
132
149
|
|
133
|
-
## 0.
|
150
|
+
## 0.49
|
134
151
|
|
135
152
|
### Testing && Docs
|
136
153
|
|
@@ -142,11 +159,7 @@
|
|
142
159
|
- `IO.foreach`
|
143
160
|
- `Process.waitpid`
|
144
161
|
|
145
|
-
## 0.
|
146
|
-
|
147
|
-
### Real IO#gets and IO#read
|
148
|
-
|
149
|
-
## 0.48 DNS
|
162
|
+
## 0.50 DNS
|
150
163
|
|
151
164
|
### DNS client
|
152
165
|
|
@@ -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
|
@@ -123,7 +123,7 @@ suspend # The main fiber suspends, waiting for all other work to finish
|
|
123
123
|
sleep 1 # The sleeper fiber goes to sleep
|
124
124
|
Gyro::Timer.new(1, 0).await # A timer event watcher is setup and yields
|
125
125
|
Thread.current.switch_fiber # Polyphony looks for other runnable fibers
|
126
|
-
Thread.current.
|
126
|
+
Thread.current.backend.poll # With no work left, the event loop is ran
|
127
127
|
fiber.schedule # The timer event fires, scheduling the sleeper fiber
|
128
128
|
# <= The sleep method returns
|
129
129
|
puts "Woke up"
|
@@ -6,7 +6,7 @@ require 'json'
|
|
6
6
|
|
7
7
|
X_SESSIONS = 1000
|
8
8
|
X_NODES = 10_000
|
9
|
-
X_SUBSCRIPTIONS_PER_SESSION =
|
9
|
+
X_SUBSCRIPTIONS_PER_SESSION = 1000
|
10
10
|
|
11
11
|
$sessions = []
|
12
12
|
X_SESSIONS.times do
|
@@ -17,8 +17,11 @@ X_SESSIONS.times do
|
|
17
17
|
}
|
18
18
|
end
|
19
19
|
|
20
|
+
REDIS_HOST = ENV['REDIS_HOST'] || 'localhost'
|
21
|
+
p redis_host: REDIS_HOST
|
22
|
+
|
20
23
|
spin do
|
21
|
-
redis = Redis.new
|
24
|
+
redis = Redis.new(host: REDIS_HOST)
|
22
25
|
redis.subscribe('events') do |on|
|
23
26
|
on.message do |_, message|
|
24
27
|
distribute_event(JSON.parse(message, symbolize_names: true))
|
@@ -30,18 +33,18 @@ $update_count = 0
|
|
30
33
|
|
31
34
|
def distribute_event(event)
|
32
35
|
$update_count += 1
|
33
|
-
|
36
|
+
t0 = Time.now
|
34
37
|
count = 0
|
35
38
|
$sessions.each do |s|
|
36
39
|
count += 1 if s[:subscriptions].include?(event[:path])
|
37
40
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
+
elapsed = Time.now - t0
|
42
|
+
rate = X_SESSIONS / elapsed
|
43
|
+
puts "elapsed: #{elapsed} (#{rate}/s)" if $update_count % 100 == 0
|
41
44
|
end
|
42
45
|
|
43
46
|
spin do
|
44
|
-
redis = Redis.new
|
47
|
+
redis = Redis.new(host: REDIS_HOST)
|
45
48
|
throttled_loop(1000) do
|
46
49
|
redis.publish('events', { path: "node#{rand(X_NODES)}" }.to_json)
|
47
50
|
end
|
@@ -60,7 +63,7 @@ spin do
|
|
60
63
|
end
|
61
64
|
end
|
62
65
|
|
63
|
-
trap(
|
66
|
+
trap('SIGINT') do
|
64
67
|
puts 'bye...'
|
65
68
|
exit!
|
66
69
|
end
|