RubyFlow The Ruby and Rails community linklog

×

The Ruby and Rails community linklog

Made a library? Written a blog post? Found a useful tutorial? Share it with the Ruby community here or just enjoy what everyone else has found!

Building a Ractor-Native Ruby 4 Stack: RESP, Redis and Background Jobs

Building a Ractor-Native Ruby 4 Stack: RESP, Redis and Background Jobs

I’ve been experimenting with what a Ruby infrastructure stack can look like when Ractor is treated as an architectural primitive rather than added afterwards.

This work resulted in three open-source gems:

SolidRESPRactor → SolidRedis → SolidJobs

The idea is to build the stack from the protocol layer upward:

  • SolidRESPRactor provides Ractor-oriented RESP encoding, parsing and I/O.
  • SolidRedis builds a Redis client around Ractor-local mutable state and Ractor-owned connections.
  • SolidJobs uses that foundation to run background jobs in parallel across multiple CPU cores.

The core architectural rule is simple:

Share immutable configuration. Keep mutable runtime state local to its owning Ractor.

An interesting optimization along the way

While benchmarking SolidRESPRactor, I discovered that a small Redis GET over TCP was allocating approximately:

16,609.8 bytes/op

The RESP parser itself wasn’t responsible for most of it.

The problem was in the socket-to-buffer path.

After changing the I/O path to reuse the read buffer, the result became:

16,609.8 → 120.8 bytes/op (-99.27%)

Allocations also dropped:

6.0 → 4.0 allocations/op

And the improvement became more visible as Ractor concurrency increased:

Ractors Before After 1 40,447 ops/s 40,173 ops/s 2 62,692 ops/s 66,235 ops/s 4 83,028 ops/s 89,146 ops/s 8 94,345 ops/s 102,060 ops/s

That work then propagated through SolidRedis and ultimately SolidJobs.

SolidJobs and multicore Ruby

On my current Ruby 4.0.1 CPU-bound benchmark, SolidJobs scales like this:

Ractors Throughput Scaling efficiency 1 282 jobs/s 100.0% 2 550 jobs/s 97.7% 4 1,099 jobs/s 97.6% 8 2,054 jobs/s 91.2%

That’s about 7.28× the throughput from 8× Ractor concurrency.

The interesting question for me isn’t just whether Ractors can make one benchmark faster.

It’s whether Ractor-native infrastructure can eventually allow Ruby applications to achieve better CPU and memory density.

Better multicore utilization could mean fewer Ruby processes for some workloads, less duplicated runtime state and potentially fewer containers or servers.

That could ultimately translate into a lower infrastructure cost per completed job.

But I don’t want to assume that from microbenchmarks.

The next important experiment is comparing SolidJobs and traditional multi-process job processing with the same CPU budget, same hardware and same workload.

Looking for independent results

The three projects are open source and the benchmark methodology is published.

SolidRESPRactor
https://github.com/nicolasva/solid-resp-ractor

SolidRedis
https://github.com/nicolasva/solid-redis

SolidJobs
https://github.com/nicolasva/solid-jobs

I’ve also written a longer article explaining the architecture, reliability model, benchmarks and infrastructure implications:

https://dev.to/nicolasva/from-resp-to-background-jobs-building-a-ractor-native-ruby-4-stack-51p5

I’d especially like to see independent benchmark results from:

  • Linux x86-64
  • Linux ARM64
  • larger multicore machines
  • different Redis environments

Positive or negative results are equally useful.

The goal isn’t to demonstrate that Ractors always win.

It’s to understand where Ractor-native Ruby infrastructure actually makes sense.

Post a comment

You can use basic HTML markup (e.g. <a>) or Markdown.

As you are not logged in, you will be
directed via GitHub to signup or sign in