Skip to content
HN On Hacker News ↗

GitHub - yaroslav/kino: A high-performance Ractor web server for Ruby 4.0+: Rack 3-based, with a Rust Tokio/Hyper front-end and Ractor-parallel Ruby workers and threaded fallback mode.

▲ 76 points 20 comments by ksec 2d ago HN discussion ↗

Pangram verdict · v3.3

We believe that this text is a mix of AI and human-written content.

74 %

AI likelihood · overall

AI
18% human-written 82% AI-generated
SEGMENTS · HUMAN 3 of 8
SEGMENTS · AI 2 of 8
WORD COUNT 990
PEAK AI % 79% · §3
Analyzed
Aug 21
backend: pangram/v3.3
Segments scanned
8 windows
avg 124 words each
Distribution
18 / 82%
human / AI fraction
Verdict
AI
Pangram v3.3

Article text · 990 words · 8 segments analyzed

Human AI-generated
§1 Mixed · 60%

Kino is a high-performance Ractor web server for Ruby 4.0+. Ruby threads cannot run Ruby code in parallel, so production setups fork a process per core and pay for each copy in memory. Kino runs your code on every core in one small process. A Rust (tokio + hyper) front-end owns the network, parallel Ractors run your Rack 3 app, and a threaded fallback mode runs everything else, Rails included. Fast. On a real 8-core server, every Kino mode is 1.5-2× ahead of a Puma fork cluster on I/O-light endpoints. Ractor mode also wins on pure CPU, 30%+.

§2 Human · 13%

Benchmarks below. A fraction of the memory. About ~7× on the simplistic bench Ractor app, and about 4× less memory than a Puma cluster serving Rails in fallback threaded mode. Parallel without forking.

§3 AI · 79%

Ractor mode runs CPU work more than 5× faster than Kino's own GVL-bound threaded mode, in the same small process. Production plumbing included. Graceful drain, crash supervision and respawn, bounded queues with 503 backpressure, request timeouts, hardened intake (slowloris and TLS-handshake deadlines, connection and body-size caps), an on_error hook for your error tracker, TLS (rustls), live stats, async access and app logging. Tells you why. kino --check lists exactly what blocks your app from ractor mode, finding by finding, so you do not have to decode Ractor::IsolationError yourself. Puma-shaped. The same workers × threads topology, a familiar config DSL, a kino CLI. If you can run Puma, you can run Kino.

§4 Mixed · 32%

N.B.: Ractors are officially experimental in Ruby 4.0, and so is this server. The threaded mode is solid. Still, Kino aims to be the best way to experiment with Ractors today—and the best Ractor server when they become stable. Table of Contents Why Benchmarks Install Usage Config file and CLI kino --check Request timeouts Stats Logging Timer waits Rack 3 compliance Rails Why The GVL allows only one Ruby thread to run at a time.

§5 AI · 79%

To use all cores, Ruby servers fork processes, and every fork costs a full copy of the app. Ractors do not have this limit: each one has its own lock, so one process can run Ruby in parallel. What was missing is a server that dispatches requests to them. Ruby 4.0 reworked Ractors (Ractor::Port, shareable_proc, less lock contention) and made this worth building. Why a Ractor server has to be built this way, and which Rust parts make Ractors fast here: doc/why-kino.md. The full design notes live in doc/architecture.md. Benchmarks Measured on a real server: AWS c7a.2xlarge (8-core AMD EPYC 9R14, 16 GB, Amazon Linux 2023). This is a realistic app-server size. These tables run a tiny synthetic Rack app—plaintext, a 10 KB body, a CPU-bound fib, a 5 ms wait—deliberately small, to measure the server rather than an app. It is Ractor-shareable, so Kino runs it in :ractor mode (and :threaded for comparison). A real Rails app is a different story: it is not Ractor-shareable, so it runs only in Kino's :threaded fallback, with its own numbers—see Rails below.

§6 Human · 27%

Ruby 4.0.5 with YJIT, every server at its defaults: Puma forks 8 workers × 3 threads, Kino stays in one process (8 workers; 1 thread each in ractor modes, 3 in threaded). Numbers are req/s by wrk (8-second windows, 64 connections, same host). Methodology: doc/benchmarks.md. endpoint Kino :ractor + lanes :ractor, workers 32² Kino :threaded Puma (cluster) /plaintext 229,534 250,222 182,997 216,994 118,176 /10k 178,083 189,862 151,034 160,400 106,768 /cpu (fib) 77,999¹ 70,885 66,100 13,429 58,006 /io (5 ms) 1,552 1,551 5,888 4,709 4,693 /io_native 1,570 1,571 6,274 4,695 4,691 Memory tells two different stories depending on the app, both by PSS (proportional set size; see note) after sustained load. The tiny benchmark app (Ractor-shareable, so Kino runs it in :ractor or :threaded). Kino is ~7× lighter in :ractor mode, ~10× in :threaded than the Puma cluster — the gap stays large because a trivial app is almost all private per-worker heap, which copy-on-write can't share: tiny app, Kino Kino (one process) Puma cluster (8 workers) ratio :ractor (8×1) 148 MB 1,068 MB ~7× :threaded (8×3) 107 MB³ 1,068 MB ~10× A real Rails app (not Ractor-shareable—Kino's :threaded fallback only, below). The gap is ~4×, smaller because Rails' large framework is shared copy-on-write across Puma's forks: Rails hello-world Kino :threaded Puma cluster (8 workers) ratio PSS 92 MB 389 MB ~4× "+ lanes" is the experimental per-worker-queue dispatcher (lanes true). It posts the fastest plaintext/10k of any configuration here. Details: doc/benchmarks.md. ¹ Stock settings, no tuning.

§7 Mixed · 70%

Ractor mode beats the fork cluster on pure CPU by +34% (+22% with lanes). Threaded mode shows the GVL ceiling that every single-process Ruby server hits. The old CPU-tuning recipe is retired: its threads 1 half is the default now, and its tokio_threads 1 half costs −12% on real hardware; see doc/benchmarks.md. ² Wait-bound throughput is slots ÷ wait, and the default columns bring 8 single-thread workers against the cluster's 24 threads. Kino slots are threads, not processes—when your app waits a lot, raise workers. The workers 32 column is that tuning: +25% over the cluster on /io (+34% via Kino.sleep) while still ahead of it on pure CPU, all in one small process. The cost is the CPU-light rows (32 ractors oversubscribe 8 cores); pick the topology your app's wait profile needs. See doc/benchmarks.md. ³ With MALLOC_ARENA_MAX=2 (the standard Ruby deployment setting; Heroku's default). Without it, 24 threads churning 10 KB responses through one glibc heap balloon to ~670 MB—an arena-fragmentation footgun, not a leak, and ractor mode sidesteps it.

§8 Human · 26%

See doc/benchmarks.md. A common first idea is to keep your current server and wrap the app in a ractor pool. We measured that too (same box; the analysis is in the doc): endpoint Kino :ractor (8×3) Puma + ractor wrapper Falcon + ractor wrapper /plaintext 193,826 19,480 99,776 /cpu (fib) 68,061 17,755 48,721 /io (5 ms) 4,530 1,454 1,549 Rails Rails is not Ractor-shareable today, so Kino serves it in :threaded fallback — one GVL-bound process.