A high performance Rack server for Opal Ruby and Matz Ruby, Tech Demo
| Response type | env.to_s | env.to_s | "hello_world" | "hello_world" |
|---------------|--------------|------------|---------------|---------------|
| | requests/sec | latency ms | requests/sec | latency ms |
| Falcon | 42350.34 | 2.95 | 68092.27 | 1.83 |
| Iodine | 69212.39 | 1.80 | 219665.14 | 0.57 |
| Itsi | +2+ 75158.16 | 1.66 | 109310.00 | 1.14 |
| Puma | 12397.22 | 10.08 | 55328.63 | 2.26 |
| Up! ruby | +1+ 77760.68 | 1.60 | +1+ 268618.38 | 0.46 |
| Up! node | 23153.89 | 5.39 | 80492.32 | 1.55 |
| Up! uWS | 30891.58 | 4.04 | +2+ 247979.55 | 0.50 |
+1+ denotes the fastest for the response type
+2+ denotes the second fastest for the response type
running on/with:
Linux, Kernel 6.16.3
ruby 3.5.0-preview1, YJit enabled
Falcon 0.52.3, falcon --hybrid --forks 4 --threads 4 -b http://localhost:3000
Iodine 0.7.58, iodine -w 4 -t 1 -p 3000
Itsy 0.2.20, itsi -w 4
Puma 7.0.3, puma -w 4 -t 4 -p 3000
Up! node/ruby/uWS master, 4 workers, up -w 4
Opal 2.0dev PR#2746 'raise_platform_foundation'
Node v24.8.0
running the example_rack_app from this repo, benchmarked with:
bombardier http://localhost:3000/
and taking the Avg
on a AMD(R) Ryzen(TM) 5 4500U CPU @ 2.3~4.0 GHz
This is currently mainly a technical demonstration, demonstrating the speed of the Opal Ruby implementation employing Node and uWebSocketJs as runtime.
Its not yet a generic, all purpose Rack server, but good for further experimentation, research and open for improvement. The included ruby version allows for verification of code correctness and performance. If it works with bundle exec up_ruby it should work equally well with the various Opal versions, at least thats the future goal.
Its a intention of this project, to serve as a tool for enhancing Opal Ruby and porting Rack apps from Matz to Opal Ruby.
There are 3 implementations of the rack server in this repo:
- up_ruby - using a uWebScokets based native extension for Matz Ruby
- up_node - using standard node for Opal Ruby
- up - using node with uWebSockets.js for Opal Ruby
To start experimenting with the Opal version:
- clone this repo
- cd into it, bundle install
- cd example_rack_app
- bundle install
- bundle exec up
To start experimenting with the Ruby version:
- clone this repo
- cd into it, bundle install
- rake compile
- cd example_rack_app
- bundle install
- bundle exec up_ruby
Available with bundle exec within the example apps or if this gem is included in your Gemfile:
up- starts a cluster of workers using Opal running in Node with uWebSocketsup_node- starts a cluster of workers using Opal running in Node with ws websocket supportup_ruby- starts a cluster of workers using Ruby with uWebSockets in a native extension, does not support the --secure options/TLS
Usage: up [options]
-h, --help Show this message
-p, --port PORT Port number the server will listen to. Default: 3000
-b, --bind ADDRESS Address the server will listen to. Default: localhost
-s, --secure Use secure sockets.
When using secure sockets, the -a, -c and -k options must be provided
-a, --ca-file FILE File with CA certs
-c, --cert-file FILE File with the servers certificate
-k, --key-file FILE File with the servers certificate
-l, --log-file FILE Log file
-P, --pid-file FILE PID file
-v, --version Show version
-w, --workers NUMBER For clusters, the number of workers to run. Default: number of processors
Up! implements the Rack Spec as of Rack 3.2 with the following differences:
rack.hijackis not implemented, butrack.upgradeinstead is, see "Websockets" below- Tempfile support is currently incomplete, affecting a few keys in the Rack Env ('tempfile' missing in Opal).
- Some Rack modules/classes still have issues when run in Opal and may not work as expected
Websockets are supported following the Iodine SPEC-WebSocket-Draft. PubSub is supported following the Iodine SPEC-PubSub-Draft, except for engines.
A example RackApp using WebSockets and PubSub is provided in the 'example_rack_ws_app' directory
Roda works fine with up_ruby. A example app for Roda is provided and appears working with up_node or up (uWebSockets.js) with the following patches applied:
Please note the phrase "appear to work" in above sentences. To try:
- clone Rack 3.0.9 and Roda 3.76
- apply the patch sets above
- set paths in the example_roda_app to point to your cloned rack & roda repos
- and up! the server
... currently only work with up_ruby, with up_node or up (uWebSockets.js) they do not work! A example app for Sinatra is provided, for convenience of developing and expanding the capabilities of Opal.
The benchmarks mainly test the overhead introduced by the rack server.
In the 'env.to_s' benchmark, the Rack environment access and response header handling overhead are measured. Simply calling env.to_s accesses all keys and serializes them briefly. If the Rack app accesses the keys of the Rack environment and sets response headers, the overhead/latency as measured can be expected, or that amount of requests per second can be expected at most.
The "hello_world" benchmark measures the overhead for the simplest possible version of a meaningful Rack response and should provide maximum performance. If the Rack app just replies with a string, that overhead/latency can be expected, or that amount of requests per second can be expected at most.
- bombardier, the tool used for benchmarking: https://github.com/codesenberg/bombardier