vielite's blog

Scatter Read Bypass of Concurrent Read Limit in Async IO

April 6, 2026
1 min read
Table of Contents
scatter-read-bypass-of-concurrent-read-limit

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.cpp
  • monad/category/async/io.hpp

Details

The implementation tracks:

  • records_.inflight_rd for single-buffer reads
  • records_.inflight_rd_scatter for 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.

2025-09-monad/monad/category/async/io.hpp
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.