# Rust metrics endpoint: shared client I/O deadline and cancellable shutdown

## Reproduced baseline

The actual baseline is d46fa5a4272e58ad1614244705f3fbb5bafb61a8. A separate scratch
crate exercises the public MetricsEndpoint and FIXP metrics wrappers against that
unchanged checkout. Two complete fragmented-request controls pass; all twelve
slow-client/deadline/shutdown cases fail. Silent-client stop takes roughly 1.8
seconds, and the Unix non-reader stop case takes 3.973 seconds. These failures
occur before any production handler change. The baseline test input, compiler
metadata and complete failure output are retained.

The handler previously reset the effective budget on every read and each partial
write_all step. It did not observe the endpoint running flag while serving a
client. The shared serving thread could therefore keep accepting progress from
one slow client long after the stated two-second timeout, delay the next scrape
and make stop/drop wait for its next socket timeout. Stop's old one-accept-poll
promise did not hold for an active connection.

## Contract and implementation

One Instant deadline starts when the worker accepts the connection and enters
its handler. Header reads, rendering elapsed time, response headers and response
body share the two-second budget. Progress does not reset it. The socket stays
blocking, with 50 ms read/write timeouts; each iteration checks the deadline and
running flag. Interrupted, WouldBlock and TimedOut errors retry only while that
budget remains. Rust documents platform-dependent timeout error kinds for
[TCP](https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_read_timeout)
and [Unix streams](https://doc.rust-lang.org/std/os/unix/net/struct.UnixStream.html#method.set_read_timeout). Zero-length writes and other errors stop the response. Every
positive short write advances its slice without duplicating bytes.

Waiting socket I/O observes stop within one poll plus scheduler delay. I/O can
overshoot its deadline by one poll plus scheduling delay; this is not a realtime
guarantee. Capture callbacks are caller code running on the worker. Stop must
wait for them to finish, and the public docs now require prompt callbacks. Their
elapsed time consumes the existing budget, and cancellation during capture
prevents later response output. No arbitrary callback interruption is promised.

Only a completely written 200 response increments scrape_count; timeout, stop,
partial output and error-status responses do not. TCP, Unix sockets and the FIXP
schema use this same handler. Normal fragmented headers, /metrics query handling,
404/405/500 behavior, source/label limits and snapshot contracts are retained.
No session producer path, wire format or global telemetry fixture changes.

## Regression coverage

The final public integration test contains sixteen cases. The original fourteen
retain the baseline controls and failures: stop/drop on partial requests, FIXP
stop on both transports, continuous header/response progress with a healthy
scrape queued behind it, stop on a blocked response and exact output for fragmented
headers. Two additional TCP/Unix cases wait 1.25 seconds before completing headers
and then apply response backpressure: the subsequent scrape must complete within
1.4 seconds, proving that writing does not receive a new two-second budget.

Actual socket backpressure uses all 256 permitted sources and 503-byte labels,
yielding over 5 MB of valid metrics, without altering native socket buffer sizes.
The healthy response must carry the exact Content-Length and full expected body,
and the worker's joined scrape count must exclude the incomplete first response.
Normal header fragments are spaced beyond the 50 ms socket timeout so that timeout
retry itself is exercised. Fixture directories delete on teardown, and each client
thread is signalled and joined even when a test fails.

Four internal cases inject three-byte reads/writes and Interrupted, WouldBlock and
TimedOut results; they check complete literal output, positive write progress
concurrent with cancellation and cancellation during rendering. This fault
adapter exists only in the unit-test configuration.

## Verification

The first candidate `c16742eaf51f48a38f410b7f5f52c48c74c6fa4d` passes
1,175 default and 1,009 open tests, and strict all-target Clippy in both editions.
Main then adds classic FIX recovery/logout and FIXP termination/frontier fixes in
four Rust files. The integrated candidate `29d1b250f0fdc307c7887e64af5b2265059e7253`
therefore repeats the focused selections, complete Rust suites and strict Clippy.
Both owned metrics files remain byte-identical across that integration.

All 345 tracked Rust input hashes match the build host before each candidate's
verification. Cargo/Rust 1.92.0 runs offline at one job and one test thread, with
absolute session LIBHFT_BUILD_ROOT and target paths. Development/test debug
symbols are disabled. Strict Clippy uses the isolated Rust 1.93.1 toolchain,
offline at one job, all targets and -D warnings.

| Gate on the integrated candidate | Result |
|---|---|
| Actual unchanged baseline public socket cases | 2 controls pass; 12 regressions fail |
| Socket/FIXP integration selection | 16 passed, 0 failed, 0 ignored |
| Telemetry contracts, rendering, endpoint and scripted I/O selection | 16 passed, 0 failed, 0 ignored |
| rust-engine-tests: complete default Rust Cargo suite | 1,188 passed, 0 failed, 1 existing ignored builders doctest |
| rust-open-edition-tests: complete open Rust Cargo suite | 1,022 passed, 0 failed, the same 1 ignored doctest |
| Strict all-target Clippy | Passed in both editions |
| Rustfmt on the integration test and changed handler/test block | Passed |
| Public-export scan on both owned files | 2 files, 0 findings |
| API inventory and generated coverage matrix --check | Both passed; no inventory change |

Each selected conformance stage reports 1 ran, 0 failed and 0 skipped. Its
PARTIAL/exit 2 result describes selection of 1 of 359 monorepo stages, not a
full-monorepo-suite pass. The retained wrapper captures the real stage's successful
stdout, stderr, command and exit metadata without changing its command/result or
process-group cleanup. The single ignored doctest already exists at
engine/rust/src/builders.rs; all integration cases run without ignores.

The integrated code pin is `b0c2e8f28db3a3a7f0cc4c9f6456bb84164c08d7`. Subsequent main changes leave
all 345 Rust inputs byte-identical to the tested combined candidate; the final
manifest and publication pins are retained. No unchanged Rust suite is repeated
for those non-Rust changes.

Owned jobs run serially on HP with one Cargo job, an 8 GiB available-memory
floor and checks for other compiler/suite processes. When another heavy job
appears, the guard stops only its own stage and retries when the host clears.
Conformance cleanup forwards interruption to the owned stage process group.
No heavy job runs on BEELINK2 for this slice.

Exact baseline input/diagnostics, both candidates' full gate logs and JSON reports,
source manifests, integration diff/proofs and candidate/publication Git bundles
are kept under ~/archive/libhft/rust-metrics-http-io-20261004. Initial evidence
is retained in its initial/ subdirectory; HP retains the original logs in the
matching archive. SHA256SUMS verifies kept evidence.


## Boundaries and next review

Other runtimes were source-reviewed, not fault-tested. C++ repeats recv/send with
per-call timeouts; Java TCP uses per-byte InputStream reads with SoTimeout; Java
Unix has a read deadline checked only on zero progress; .NET uses per-byte
Stream/Socket reads with receive timeouts. Managed close uses a timed join without
closing the active client. These observations support a coordination request for
their owners, not a claim of reproduced cross-runtime failures.

A separate Rust review found Unix pathname ownership risks: start unlinks an
existing socket without establishing staleness, and stop unlinks the path without
an identity check. Persistent accept errors also retry without a wait. Both need
focused lifecycle/fault regressions and are outside this slice.

The API inventory remains 82/82 applicable supported capabilities (11 n/a).
Production feed/order endpoint evidence and venue/FIXP schema certification remain
partial; these socket tests provide no customer/venue authorization or certification.
