Skip to content

Instantly share code, notes, and snippets.

@nateberkopec
Created January 23, 2026 00:33
Show Gist options
  • Select an option

  • Save nateberkopec/bd229d067da6a891e171640173641fb6 to your computer and use it in GitHub Desktop.

Select an option

Save nateberkopec/bd229d067da6a891e171640173641fb6 to your computer and use it in GitHub Desktop.
Puma closed_socket? tradeoff benchmark

Puma closed_socket? tradeoff benchmark (Docker)

This repo/gist is a tiny, self-contained repro to demonstrate the tradeoff discussed in puma/puma#3867.

What it does

  • Spins up Puma in Docker.
  • Provides two endpoints:
    • /ok (fast)
    • /slow (CPU burn for SLOW_BURN_MS, default 50ms)
  • Runs an “abort storm” client that sends a request to /slow then immediately RSTs the socket (SO_LINGER=0).
  • Measures /ok throughput with wrk while the abort storm is running.

Files

  • Dockerfile – builds a Ruby image with Puma.
  • config.ru – app with /ok and /slow.
  • puma.rb – allows disabling closed_socket? via DISABLE_CLOSED_SOCKET_CHECK=1.

Build

cd /tmp/puma-closed-socket-bench

docker build -t puma-closed-socket-bench:latest .

Run server (closed_socket? enabled)

docker run -d --rm -p 9292:9292 --name puma-bench -e SLOW_BURN_MS=50 puma-closed-socket-bench:latest

Abort storm (RST after sending request)

docker run -d --rm --name abort-storm ruby:3.3-slim bash -lc 'ruby -rsocket -e "req=\"GET /slow HTTP/1.1\\r\\nHost: x\\r\\n\\r\\n\"; loop { begin; s=TCPSocket.new(\"host.docker.internal\",9292); s.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1,0].pack(\"ii\")); s.write(req); s.close; sleep 0.005; rescue; end }"'

Measure (host wrk)

wrk -t4 -c64 -d15s http://127.0.0.1:9292/ok

Disable closed_socket? (compare)

docker stop puma-bench

docker run -d --rm -p 9292:9292 --name puma-bench -e SLOW_BURN_MS=50 -e DISABLE_CLOSED_SOCKET_CHECK=1 puma-closed-socket-bench:latest

Cleanup

docker stop abort-storm puma-bench
# frozen_string_literal: true
SLOW_BURN = (ENV["SLOW_BURN_MS"] || "50").to_i / 1000.0
app = lambda do |env|
if env["PATH_INFO"] == "/slow"
t = Process.clock_gettime(Process::CLOCK_MONOTONIC) + SLOW_BURN
while Process.clock_gettime(Process::CLOCK_MONOTONIC) < t
# busy
end
[200, { "Content-Type" => "text/plain" }, ["slow\n"]]
else
[200, { "Content-Type" => "text/plain" }, ["ok\n"]]
end
end
run app
FROM ruby:3.3-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential \
&& rm -rf /var/lib/apt/lists/*
RUN gem install puma -v 6.4.2
WORKDIR /app
COPY config.ru puma.rb /app/
EXPOSE 9292
CMD ["puma", "-C", "/app/puma.rb"]
# frozen_string_literal: true
if ENV["DISABLE_CLOSED_SOCKET_CHECK"] == "1"
Puma::Server.class_eval do
def closed_socket?(_socket) = false
end
end
threads 0, 16
workers 0
quiet
bind "tcp://0.0.0.0:9292"
preload_app! false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment