A high performance Rack server for Opal Ruby and Matz Ruby, Tech Demo
Response type: env.to_s "hello_world"
Requests/Second Latency Requests/Second Latency
Puma: 8884.53 req/s 15.18 ms 50822.38 req/s 2.62 ms
Unicorn: 12302.35 req/s 10.22 ms 16329.68 req/s 7.68 ms
Falcon: 13168.82 req/s 9.49 ms 24041.63 req/s 5.26 ms
Racer: 14536.88 req/s 8.94 ms 15354.14 req/s 8.44 ms
Agoo: 49078.57 req/s 2.54 ms 89022.91 req/s 1.51 ms
Iodine: 59116.53 req/s 2.11 ms <<< 134267.79 req/s 0.93 ms
Up! bun: 3900.44 req/s 32.00 ms 47334.16 req/s 2.64 ms
Up! ruby cluster: 53641.29 req/s 2.35 ms 128237.52 req/s 0.97 ms
Up! uWS cluster: 20143.62 req/s 6.20 ms 152353.97 req/s 0.82 ms <<<
<<< denotes the fastest for the response type
running on/with:
Linux, Kernel 6.5.0-x
ruby 3.3.0, YJit enabled
Opal 2.0-dev as of 9. Feb 2024, with node v20.11.0
Puma 6.4.2, 4 workers, 4 threads
Falcon 0.43.0, 4 workers, 4 threads
Racer 0.1.3, defaults
Unicorn 6.1.0, 4 workers
Agoo 2.15.8, 4 workers, 4 threads
Iodine 0.7.57, 4 workers, 1 thread
Up! bun 0.0.4, 1 worker
Up! uWS cluster 0.0.4, 4 workers
Up! Ruby cluster 0.0.4, 4 workers
running the example_rack_app from this repo, benchmarked with:
bombardier http://localhost:3000/
and taking the Avg
on a Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
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.
To start experimenting:
- clone this repo
- cd into it, bundle install
- cd example_rack_app
- bundle install
- bundle exec up
You may want to change the gem 'opal-up'
line in the Gemfile to use up from rubygems, if you want to run your app outside of the cloned repo.
For a Gemfile available from rubygems:
gem 'opal-up'
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 with uWebSockets, fastest serverup_bun
- starts single worker server using Bun, requires Opal bun support from PR#2622up_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.0 with the following differences:
rack.hijack
is not implemented, butrack.upgrade
instead 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
A example app for Roda is provided and appears working 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 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