polyphony 0.43.11 → 0.45.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -1
  3. data/CHANGELOG.md +40 -0
  4. data/Gemfile.lock +18 -8
  5. data/Rakefile +1 -1
  6. data/TODO.md +22 -9
  7. data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
  8. data/docs/api-reference/thread.md +1 -1
  9. data/docs/getting-started/overview.md +14 -14
  10. data/docs/getting-started/tutorial.md +1 -1
  11. data/examples/adapters/redis_client.rb +3 -1
  12. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  13. data/examples/adapters/sequel_mysql.rb +23 -0
  14. data/examples/adapters/sequel_mysql_pool.rb +33 -0
  15. data/examples/adapters/sequel_pg.rb +24 -0
  16. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  17. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  18. data/examples/core/deferring-an-operation.rb +16 -0
  19. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  20. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  21. data/examples/core/handling-signals.rb +11 -0
  22. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  23. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  24. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  25. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  26. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  27. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  28. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  29. data/examples/core/supervisor.rb +20 -0
  30. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  31. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  32. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  33. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  34. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  35. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  36. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  37. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  38. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  39. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  40. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  41. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  42. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  43. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  44. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  45. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  46. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  47. data/examples/io/{xx-open.rb → open.rb} +0 -0
  48. data/examples/io/pry.rb +18 -0
  49. data/examples/io/rack_server.rb +71 -0
  50. data/examples/io/raw.rb +14 -0
  51. data/examples/io/reline.rb +18 -0
  52. data/examples/io/{xx-system.rb → system.rb} +1 -1
  53. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  54. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  55. data/examples/io/tunnel.rb +6 -1
  56. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  57. data/examples/performance/fiber_transfer.rb +2 -1
  58. data/examples/performance/fs_read.rb +5 -6
  59. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  60. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  61. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  62. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  63. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
  64. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
  65. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  66. data/examples/performance/thread_pool_perf.rb +6 -7
  67. data/ext/polyphony/backend.h +40 -0
  68. data/ext/polyphony/event.c +3 -3
  69. data/ext/polyphony/extconf.rb +1 -1
  70. data/ext/polyphony/fiber.c +66 -6
  71. data/ext/polyphony/{libev_agent.c → libev_backend.c} +237 -238
  72. data/ext/polyphony/polyphony.c +3 -3
  73. data/ext/polyphony/polyphony.h +15 -20
  74. data/ext/polyphony/polyphony_ext.c +3 -4
  75. data/ext/polyphony/queue.c +5 -6
  76. data/ext/polyphony/ring_buffer.c +0 -1
  77. data/ext/polyphony/thread.c +36 -33
  78. data/lib/polyphony.rb +26 -39
  79. data/lib/polyphony/adapters/fs.rb +1 -1
  80. data/lib/polyphony/adapters/irb.rb +2 -17
  81. data/lib/polyphony/adapters/mysql2.rb +19 -0
  82. data/lib/polyphony/adapters/postgres.rb +5 -5
  83. data/lib/polyphony/adapters/process.rb +2 -5
  84. data/lib/polyphony/adapters/readline.rb +17 -0
  85. data/lib/polyphony/adapters/redis.rb +1 -1
  86. data/lib/polyphony/adapters/sequel.rb +45 -0
  87. data/lib/polyphony/core/exceptions.rb +11 -0
  88. data/lib/polyphony/core/global_api.rb +17 -12
  89. data/lib/polyphony/core/resource_pool.rb +20 -7
  90. data/lib/polyphony/core/sync.rb +46 -8
  91. data/lib/polyphony/core/throttler.rb +1 -1
  92. data/lib/polyphony/extensions/core.rb +30 -30
  93. data/lib/polyphony/extensions/fiber.rb +30 -49
  94. data/lib/polyphony/extensions/io.rb +60 -16
  95. data/lib/polyphony/extensions/openssl.rb +6 -6
  96. data/lib/polyphony/extensions/socket.rb +14 -15
  97. data/lib/polyphony/extensions/thread.rb +6 -5
  98. data/lib/polyphony/version.rb +1 -1
  99. data/polyphony.gemspec +7 -3
  100. data/test/helper.rb +1 -1
  101. data/test/{test_agent.rb → test_backend.rb} +22 -22
  102. data/test/test_fiber.rb +29 -12
  103. data/test/test_io.rb +59 -1
  104. data/test/test_kernel.rb +5 -0
  105. data/test/test_resource_pool.rb +29 -4
  106. data/test/test_signal.rb +16 -37
  107. data/test/test_socket.rb +17 -0
  108. data/test/test_sync.rb +52 -0
  109. metadata +127 -97
  110. data/.gitbook.yaml +0 -4
  111. data/examples/adapters/concurrent-ruby.rb +0 -9
  112. data/examples/core/04-handling-signals.rb +0 -19
  113. data/examples/core/xx-agent.rb +0 -102
  114. data/examples/core/xx-at_exit.rb +0 -29
  115. data/examples/core/xx-caller.rb +0 -12
  116. data/examples/core/xx-daemon.rb +0 -14
  117. data/examples/core/xx-deadlock.rb +0 -8
  118. data/examples/core/xx-deferring-an-operation.rb +0 -14
  119. data/examples/core/xx-exception-backtrace.rb +0 -40
  120. data/examples/core/xx-fork-cleanup.rb +0 -22
  121. data/examples/core/xx-fork-spin.rb +0 -42
  122. data/examples/core/xx-fork-terminate.rb +0 -27
  123. data/examples/core/xx-move_on.rb +0 -23
  124. data/examples/core/xx-queue-async.rb +0 -120
  125. data/examples/core/xx-readpartial.rb +0 -18
  126. data/examples/core/xx-signals.rb +0 -16
  127. data/examples/core/xx-sleep-forever.rb +0 -9
  128. data/examples/core/xx-sleeping.rb +0 -25
  129. data/examples/core/xx-snooze-starve.rb +0 -16
  130. data/examples/core/xx-spin-fork.rb +0 -49
  131. data/examples/core/xx-state-machine.rb +0 -51
  132. data/examples/core/xx-stop.rb +0 -20
  133. data/examples/core/xx-supervisors.rb +0 -21
  134. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  135. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  136. data/examples/core/xx-thread-snooze.rb +0 -34
  137. data/examples/core/xx-timer-gc.rb +0 -17
  138. data/examples/core/xx-trace.rb +0 -79
  139. data/examples/performance/xx-array.rb +0 -11
  140. data/examples/performance/xx-fiber-switch.rb +0 -9
  141. data/examples/performance/xx-snooze.rb +0 -15
  142. data/examples/xx-spin.rb +0 -32
  143. data/ext/polyphony/agent.h +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d1eb9e2b1eef7f180107b3952fc6974e9c816268ebd8d2368ddfaac276d2086
4
- data.tar.gz: dafb382dc7606b92b97fb98125b57abc72e1b1d6b07bb877abff608a41ee51a1
3
+ metadata.gz: 52f8ebf1104d921c9e3b0afa0b4605ee8f912eabe8b6264898f23905d2cb6c6f
4
+ data.tar.gz: 38f0f7cd62997ba5681185c3c4859f57d86de875d8292faf67fffa53c7160181
5
5
  SHA512:
6
- metadata.gz: 9f91b787b3eec2c19f6a02fd8a77469c8ae5d7a9302799c3e2a5957376b9f4b0135d2cd958ec7dcf326701e8ba81d85a28201a140ce5806ccc057cedafe907d5
7
- data.tar.gz: df865277e2f37c0d7fde0ffd38f52fc466bf28033fb968bd012af7455df9e7805989152467e1e8e84719194f173c99c6290496987ad8a2b48fe26076e62c85bd
6
+ metadata.gz: 2429259a2e79757ec879c4c00db8fd8b87302b9e0a61c6ae2ddd5fdb6e1a3a06ac63d551ed33a4c77480e156eb1247a0fab4a263179a60fd2e9a2d3173831a58
7
+ data.tar.gz: 04f58dafb5faf1e21b08fd85d508ceb6e9869b0a69d63c133661a834f169eb750b599639ffdbff474abf7f92bc6bfee3a43b9442c2885a6dd88328657b0e1d36
@@ -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
@@ -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)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.43.11)
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.0)
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.2019.1009)
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.0.5)
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.0)
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
- rake-compiler (= 1.0.5)
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-35-158-110-38.eu-central-1.compute.amazonaws.com'
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.44
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.45
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.46
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.47
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 agent. While running
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 System Agent
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 agent*. The system
348
- agent is an object having a uniform interface, that performs all blocking
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 agent integrates the two to
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.agent.read`, which does all the work.
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 agent interface includes two methods that allow maximizing
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.agent.read_loop(socket) do |data|
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.agent.accept_loop(server) do |client|
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` agent methods implement tight loops that
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 agent based on
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 agents, such as a Windows
402
- agent using
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) agent,
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` agent as alternative to the libev agent
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.agent.poll # With no work left, the event loop is ran
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"
@@ -3,7 +3,9 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony/adapters/redis'
5
5
 
6
- redis = Redis.new
6
+ ::Exception.__disable_sanitized_backtrace__ = true
7
+
8
+ redis = Redis.new(host: ENV['REDIS_HOST'] || 'localhost')
7
9
 
8
10
  X = 10
9
11
 
@@ -6,7 +6,7 @@ require 'json'
6
6
 
7
7
  X_SESSIONS = 1000
8
8
  X_NODES = 10_000
9
- X_SUBSCRIPTIONS_PER_SESSION = 100
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
- # t0 = Time.now
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
- # elapsed = Time.now - t0
39
- # rate = X_SESSIONS / elapsed
40
- # puts "elapsed: #{elapsed} (#{rate}/s)" if $update_count % 100 == 0
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(:int) do
66
+ trap('SIGINT') do
64
67
  puts 'bye...'
65
68
  exit!
66
69
  end