71.2 RAS Reads
API
An asynchronous RAS read is represented by a caller-owned request object:
struct RASReadRequest
{
RASOffset offset;
void* buffer;
ssize_t size;
};
void Read(RASReadRequest& request);
void OnReadComplete(RASReadRequest& request);
The request object is both the description and identity of the operation. It specifies the file
offset, destination buffer and exact number of bytes to read. Returning the same object to
OnReadComplete() means that the RAS does not need to allocate a
Read Sequence Number, maintain a completed-read prefix or return a separate caller context.
Request and buffer lifetime
The caller owns the RASReadRequest and destination buffer, but must
keep both alive at stable addresses from the call to Read() until
the corresponding call to OnReadComplete(). While the request is
pending, the caller must not modify its offset,
buffer or size fields, and must
not move, destroy or resubmit the request object.
The destination buffer belongs to the RAS while the read is pending. The caller must not read,
modify, move or destroy it. OnReadComplete() transfers the buffer
back to the caller: the entire requested range has been filled successfully, the RAS will not access
the request or buffer again, and the caller may inspect the data. The request object can then be
modified and reused for another read.
Thread safety and completion order
Read() is thread-safe. Different threads may submit different
request objects concurrently. It is forbidden to submit the same request object again while it is
pending.
Native reads may execute and complete in any order. The RAS reports an individual completion as soon
as that logical read has completed, so a slow earlier request does not prevent the caller from using
the result of a later independent request. Calls to OnReadComplete()
must nevertheless be sequential and must never overlap. This permits out-of-order completion without
requiring concurrent mutation of LSS state.
The RAS must not invoke OnReadComplete() for a request before the
corresponding call to Read() has returned. Even if a native read
completes immediately, its logical completion is delivered later. This prevents inline reentrancy and
keeps Read() a submission-only operation.
Exact-read semantics
One RASReadRequest represents one complete logical read.
OnReadComplete() means that every byte in the requested file range
[offset,offset+size) has been copied into the destination buffer. The
API does not expose partial-read progress or a byte-count result.
A native platform read may transfer fewer bytes than requested. The RAS can continue reading the remaining suffix as part of the same request, or treat the short transfer as a fatal read error. It must not report completion until the entire logical read has succeeded. An unexpected end of file is therefore an error rather than a successful partial result.
Overlapping ranges
Pending read requests must not have overlapping destination-memory ranges, because two native operations could otherwise write to the same memory concurrently. A RAS implementation can detect reuse of a pending request object and overlapping destination buffers in diagnostic builds.
Overlapping source file ranges do not create the same problem. Two requests can safely read common file bytes into different destination buffers, although the LSS segment cache normally prevents duplicate segment reads.
Use by the LSS
The LSS uses RAS reads in two places. When a store is opened, it reads the root block into a buffer
owned by the opening state. During normal operation and recovery, the segment cache reads segments
into their segment buffers. In both cases the owning state has a stable lifetime and can contain the
RASReadRequest for the pending operation.
The segment cache ensures that a segment is not read more than once concurrently. A segment being
loaded remains in its loading state until OnReadComplete() returns
its request. The LSS can then validate the segment, mark it as resident and continue operations that
were waiting for it.
Errors
A read error is reported through the uniform fatal RAS error mechanism as a read operation error. It does not require a request-specific failure callback because the LSS enters the zombie state and does not continue processing other operations. Request objects and destination buffers must nevertheless remain alive until all native operations have completed or been cancelled during shutdown.
Validation occurs after a successful read has returned the buffer to the LSS. An invalid checksum, root block, LFU or log record is an integrity failure detected above the RAS, but it causes the same transition to the zombie state as a native read error.