Skip to content

PATTERN Cited by 1 source

Fix at shutdown boundary

Fix at shutdown boundary is a defensive design pattern where a data-loss guard is placed at the exact point where irreversible action occurs (connection close, process exit, file truncation), rather than modifying the upstream control flow. This minimizes unintended side-effects on other code paths.

Rationale

When a bug allows premature termination of a data pipeline, there are typically two fix locations:

  1. Upstream (in the processing loop): Return Pending/block when flush is incomplete, preventing the loop from ever reaching the shutdown decision. This can cause regressions in other loop behaviors (e.g., reducing read-poll frequency, breaking keepalive connections).

  2. At the boundary (in the shutdown function): Before executing the irreversible action, verify that all buffered data has been drained. This is narrower in blast radius — it only fires at the moment of no return.

hyper's Implementation

Cloudflare initially fixed the bug in the dispatch loop (option 1), but this blocked reads during incomplete flushes and broke keepalive semantics. The upstream-accepted fix places a ready!(self.poll_flush(cx)?) guard inside poll_shutdown, ensuring all internal buffer data is flushed to the socket before shutdown(fd, SHUT_WR) is issued:

pub(crate) fn poll_shutdown(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
    ready!(self.poll_flush(cx)?);
    Pin::new(&mut self.io).poll_shutdown(cx)
}

The dispatch loop remains unchanged; the fix activates only at the exact moment data would otherwise be lost (Source: sources/2026-06-22-cloudflare-how-we-found-a-bug-in-the-hyper-http-library).

Applicability

  • Any system with an internal write buffer above a kernel/transport buffer.
  • Graceful shutdown paths in servers, database connections, file writers.
  • Process exit handlers that must drain queues before termination.

Seen In

Last updated · 559 distilled / 1,651 read