Summary
The Async IO layer enforces concurrent_read_io_limit_ only for the single-buffer read path.
Scatter reads submitted through the iovec-based path increment a separate in-flight counter and are
sent directly to io_uring without passing through the same gate.
That mismatch allows scatter reads to bypass the configured read throttle.
Affected Area
monad/category/async/io.cppmonad/category/async/io.hpp
Details
The implementation tracks:
records_.inflight_rdfor single-buffer readsrecords_.inflight_rd_scatterfor scatter reads
Only the single-buffer path checks concurrent_read_io_limit_ before submission. The scatter path
immediately increments records_.inflight_rd_scatter and submits the request. The restart logic in
poll_uring_() also checks only records_.inflight_rd, so the queueing policy is inconsistent in
both directions.
size_t submit_read_request( std::span<std::byte> buffer, chunk_offset_t offset, erased_connected_operation *uring_data){ if (concurrent_read_io_limit_ > 0) { if (records_.inflight_rd >= concurrent_read_io_limit_) { // queue single-buffer reads return size_t(-1); } } submit_request_(buffer, offset, uring_data, uring_data->io_priority()); if (++records_.inflight_rd > records_.max_inflight_rd) { records_.max_inflight_rd = records_.inflight_rd; }}
size_t submit_read_request( std::span<const struct iovec> buffers, chunk_offset_t offset, erased_connected_operation *uring_data){ submit_request_(buffers, offset, uring_data, uring_data->io_priority()); if (++records_.inflight_rd_scatter > records_.max_inflight_rd_scatter) { records_.max_inflight_rd_scatter = records_.inflight_rd_scatter; }}Impact
A workload that routes reads through the scatter/iovec path can exceed the intended cap on concurrent physical reads. In practice, that increases pressure on:
- ring entries
- memory and internal queues
- descriptor and buffer resources
The net effect is avoidable resource exhaustion and degraded node availability.
Recommendation
Gate all read submissions against the combined in-flight read count. If the combined number of single-buffer and scatter reads is already at the configured limit, queue the scatter request instead of submitting it immediately.