Skip to content

Puma plugin: Solid Queue processes persist after terminal close due to Errno::EIO blocking shutdown #737

@bradlord

Description

@bradlord

Summary

When a terminal window is closed while running a Rails server with the Solid Queue Puma plugin enabled, the Solid Queue supervisor and worker processes are not cleaned up and continue holding the port, preventing the server from being restarted.

Steps to Reproduce

rails new repro --api
cd repro
RAILS_ENV=production SOLID_QUEUE_IN_PUMA=1 ./bin/rails s
# Close terminal window (do not use Ctrl-C)
lsof -i :3000
RAILS_ENV=production SOLID_QUEUE_IN_PUMA=1 ./bin/rails s # Fails because port is in use

Expected Behavior

All Solid Queue processes exit when the terminal is closed and the port is freed.

Actual Behavior

The Solid Queue supervisor and worker processes remain running and hold the port. The server cannot be restarted without manually killing them:

$ lsof -i :3000
COMMAND   PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
ruby    72516 brad    9u  IPv4  ...      0t0  TCP localhost:hbci (LISTEN)
ruby    72521 brad    9u  IPv4  ...      0t0  TCP localhost:hbci (LISTEN)
ruby    72522 brad    9u  IPv4  ...      0t0  TCP localhost:hbci (LISTEN)
ruby    72523 brad    9u  IPv4  ...      0t0  TCP localhost:hbci (LISTEN)

Root Cause

When the terminal is closed, the PTY is torn down. The monitor_puma thread in the Puma plugin correctly detects that Puma has died by observing Process.ppid change to 1, and calls log before sending Process.kill(:INT, $$). However, log_writer.log attempts to write to STDOUT, which raises Errno::EIO because the PTY no longer exists. This exception bubbles up and prevents Process.kill from ever being reached, leaving the supervisor and its children running indefinitely.

Because the terminal is already closed, this error is silently swallowed. Adding explicit error logging to a file revealed the cause:

[2026-04-14 11:44:12 -0400] Failed to write to Puma log: Errno::EIO Input/output error @ rb_sys_fail_on_write - "Detected Puma has gone away, stopping Solid Queue.."

The relevant code in lib/puma/plugin/solid_queue.rb:

def monitor(process_dead, message)
  loop do
    if send(process_dead)
      log message          # <-- raises Errno::EIO, Process.kill never reached
      Process.kill(:INT, $$)
      break
    end
    sleep 2
  end
end

def log(...)
  log_writer.log(...)    # <-- Errno::EIO when PTY is torn down
end

Fix

Rescue Errno::EIO in the log method so that the kill signal is always reached:

def log(*args)
  log_writer.log(*args)
rescue Errno::EIO
  # Terminal is gone, ignore logging errors during shutdown
end

Note: There may be other relevant errors that should be handled, such as Errno::EPIPE or Errno::EBADF, but I haven't managed to produce them in my testing.

Environment

  • macOS, Ubuntu (with tmux)
  • Puma 8.0.0
  • solid_queue 1.4.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions