Class: Clacky::Server::EPIPESafeIO
- Inherits:
-
SimpleDelegator
- Object
- SimpleDelegator
- Clacky::Server::EPIPESafeIO
- Defined in:
- lib/clacky/server/epipe_safe_io.rb
Overview
EPIPESafeIO — wraps an IO ($stdout / $stderr) so that writes never raise Errno::EPIPE / IOError to the calling code.
Why this exists:
The server worker process inherits fd 0/1/2 from the Master. If the
Master itself was launched in a way where its stdout/stderr eventually
becomes a broken pipe (e.g. launched by an installer that exits, or by
a GUI/IDE process that closes its end), the worker's first `puts` after
that pipe breaks raises Errno::EPIPE. Unhandled, this kills the worker
— taking all in-memory sessions, agent loops, and SSE connections down
with it, and triggering a crash loop because the new worker inherits
the same broken fd.
Behavior:
- Healthy state: delegates every method to the underlying IO. Users
see normal terminal output (banner, request logs, etc.).
- First broken-pipe failure: catches Errno::EPIPE / IOError, swaps
the underlying IO to /dev/null permanently, and returns silently.
Subsequent writes succeed (into /dev/null) so the worker stays alive.
- Session state, agent loops, SSE connections all preserved.
Scope:
We only wrap $stdout / $stderr (the global variables that Kernel#puts,
Kernel#print, Kernel#warn, etc. use under the hood). We do NOT touch
the STDOUT / STDERR constants — a codebase audit confirmed nothing in
Clacky writes to those constants directly (only `STDOUT.flush` which
cannot raise EPIPE).
Constant Summary collapse
- WRITE_METHODS =
Methods that perform writes and may raise Errno::EPIPE. We override each one to rescue and degrade gracefully.
%i[write write_nonblock syswrite puts print printf << putc].freeze
Instance Method Summary collapse
-
#fell_back? ⇒ Boolean
Whether this wrapper has already fallen back to /dev/null.
-
#flush ⇒ Object
Some callers do ‘$stdout.flush`.
Instance Method Details
#fell_back? ⇒ Boolean
Whether this wrapper has already fallen back to /dev/null. Useful for tests and diagnostics.
73 74 75 |
# File 'lib/clacky/server/epipe_safe_io.rb', line 73 def fell_back? @fell_back == true end |
#flush ⇒ Object
Some callers do ‘$stdout.flush`. Make it safe too.
64 65 66 67 68 69 |
# File 'lib/clacky/server/epipe_safe_io.rb', line 64 def flush __getobj__.flush rescue Errno::EPIPE, IOError => e fall_back_to_null!(e) nil end |