Skip to content

RFC: Add limited read - #7945

Open
Xuanwo wants to merge 3 commits into
mainfrom
xuanwo/rfc-limited-read
Open

RFC: Add limited read#7945
Xuanwo wants to merge 3 commits into
mainfrom
xuanwo/rfc-limited-read

Conversation

@Xuanwo

@Xuanwo Xuanwo commented Jul 24, 2026

Copy link
Copy Markdown
Member

Which issue does this PR close?

Refs #7938.

Rationale for this change

A bounded read range is exact, so callers that only need up to a small number of bytes must stat the object first or handle a short object as an error. OpenDAL needs an at-most read for probes such as file header and format detection without an extra metadata request.

What changes are included in this PR?

This PR proposes ReadOptions::limit and Operator::read_with(...).limit(n). The proposal keeps exact bounded range behavior, uses the existing raw open and read split, and carries only a private exact boolean in core instead of adding a new raw operation or planning abstraction.

Are there any user-facing changes?

Yes. The proposed API returns between zero and the configured limit on clean EOF. Existing reads that do not set limit keep their current behavior. This PR contains the RFC only; it does not implement the API.

AI Usage Statement

This proposal was drafted with assistance from OpenAI Codex powered by GPT-5.

@Xuanwo
Xuanwo marked this pull request as ready for review July 27, 2026 05:25
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. releases-note/feat The PR implements a new feature or has a title that begins with "feat" labels Jul 27, 2026
.await?;
```

This returns at most 1024 bytes starting at offset 4096. If the offset is at or

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this aligns with the current behavior? I checked with quick unit test seems we return RangeNotSatisfied for out-of-bound access.

Example-1 for memory backend

// under core/core/src/services/memory/backend.rs
#[cfg(test)]
mod tests {
    use super::MEMORY_SCHEME;
    use crate::Buffer;
    use crate::BytesRange;
    use crate::ErrorKind;
    use crate::Operator;
    use crate::Result;

    #[tokio::test]
    async fn test_bounded_read_beyond_eof_returns_range_not_satisfied() -> Result<()> {
        let op = Operator::via_iter(MEMORY_SCHEME, [])?;
        op.write("test", Buffer::from("abc")).await?;

        // RFC-7945 proposes normalizing range(4096..).limit(1024) into this
        // bounded physical range. Memory rejects it before core can apply
        // at-most completion semantics.
        let physical_range = BytesRange::new(4096, Some(1024));
        let err = op
            .reader("test")
            .await?
            .read(physical_range)
            .await
            .unwrap_err();

        assert_eq!(err.kind(), ErrorKind::RangeNotSatisfied);

        Ok(())
    }
}

Example-2 for local filesystem backend

// under core/services/fs/src/backend.rs
#[cfg(test)]
mod tests {
    use opendal_core::Buffer;
    use opendal_core::BytesRange;
    use opendal_core::ErrorKind;
    use opendal_core::Operator;

    use super::*;

    #[tokio::test]
    async fn test_bounded_read_beyond_eof_returns_range_not_satisfied() -> Result<()> {
        let root = tempfile::TempDir::new().expect("tempdir must be created");
        let op = Operator::new(FsBuilder::default().root(&root.path().to_string_lossy()))?;
        op.write("test", Buffer::from("abc")).await?;

        let physical_range = BytesRange::new(4096, Some(1024));
        let err = op
            .reader("test")
            .await?
            .read(physical_range)
            .await
            .unwrap_err();

        assert_eq!(err.kind(), ErrorKind::RangeNotSatisfied);

        Ok(())
    }
}

@Xuanwo Xuanwo Jul 27, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I didn't get this. We are going to add a new option called limit, and this examples shows how limit works. It's true and of course that current tests will fail.

@dentiny dentiny Jul 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RFC here mentioned

If the offset is at or beyond the end of the object, the result is empty.

It reads to me that, with limit option specified, users will get an empty buffer instead of an error.

So I'm wondering if it's better to keep the current behavior?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yes. Makes sense 👍

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, starting a read past end of file sounds like it should be RangeNotSatisfied, even if short reads would be allowed by the caller. 👍

Comment on lines +113 to +115
The initial API rejects combining a suffix range with `limit`. A suffix is
relative to the object end, so resolving it without metadata would defeat the
no-`stat` property.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this rejection is needed. If suffix ranges are currently supported anyway, a suffix range (END-1024..END) + limit 512, say, is equivalent to (END-1024..END-512) and resolvable just the same?

Comment on lines +138 to +140
The `exact` flag belongs to the public read execution path. It is not passed to
services because services should not decide whether a caller accepts a short
result.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this. Could a service do a better job if it knew about the request?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

releases-note/feat The PR implements a new feature or has a title that begins with "feat" size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants