Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 30 additions & 34 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,33 @@ jobs:
steps:
- uses: actions/checkout@v5

# - uses: dtolnay/rust-toolchain@stable
# with:
# components: clippy
#
# - uses: Swatinem/rust-cache@v2
# with:
# shared-key: cargo-${{ inputs.runner }}
#
# - name: Install nightly rustfmt
# run: rustup toolchain install nightly --profile minimal --component rustfmt
#
# - name: Rustfmt
# run: |
# cargo +nightly fmt --all
# if ! git diff --quiet; then
# git status --short
# git diff --stat
# exit 1
# fi
#
# - name: Clippy
# run: cargo clippy --all-features --all-targets -- -D warnings
#
# - name: Feature compile checks
# run: |
# cargo check
# cargo check --tests
# cargo check --features file
# cargo check --features http
# cargo check --features debug
# cargo check --all-features
#
# - name: Docs.rs feature set
# run: RUSTDOCFLAGS='--cfg docsrs' cargo doc --all-features --no-deps
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy

- uses: Swatinem/rust-cache@v2
with:
shared-key: cargo-${{ inputs.runner }}
Comment on lines +25 to +27

- name: Install nightly rustfmt
run: rustup toolchain install nightly --profile minimal --component rustfmt

- name: Rustfmt
run: |
cargo +nightly fmt --all
if ! git diff --quiet; then
git status --short
git diff --stat
exit 1
fi

- name: Clippy
run: cargo clippy --all-features --all-targets -- -D warnings

- name: Feature compile checks
run: |
cargo check
cargo check --tests

- name: Docs.rs feature set
run: RUSTDOCFLAGS='--cfg docsrs' cargo doc --all-features --no-deps
33 changes: 15 additions & 18 deletions .github/workflows/unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,18 @@ jobs:
steps:
- uses: actions/checkout@v5

# - uses: dtolnay/rust-toolchain@stable
#
# - uses: taiki-e/install-action@v2
# with:
# tool: nextest
#
# - uses: Swatinem/rust-cache@v2
# with:
# shared-key: cargo-${{ inputs.runner }}
#
# - name: Unit tests
# run: cargo nextest run --features utils
#
# - name: Debug harness tests
# run: cargo nextest run --features utils,debug
#
# - name: Doc tests
# run: cargo test --doc --all-features
- uses: dtolnay/rust-toolchain@stable

- uses: taiki-e/install-action@v2
with:
tool: nextest

- uses: Swatinem/rust-cache@v2
with:
shared-key: cargo-${{ inputs.runner }}
Comment on lines +27 to +29

- name: Unit tests (no features)
run: cargo nextest run

- name: Doc tests
run: cargo test --doc --all-features
87 changes: 87 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ keywords = ["storage", "tokio", "fs", "filesystem", "stream"]
readme = "README.md"
license = "Apache-2.0"

[dependencies]
parking_lot = "0.12"
bytes = "1.11"
16 changes: 8 additions & 8 deletions docs/architecture/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ a HuggingFace repository, a remote FTP server, or any other distant data source.
ability to read explicit byte ranges from the data source. As such the user must provide two functions to
produce a functional Reader:

- `func len() -> usize`: The number of bytes in the data source.
- `func read(offset: usize, length: usize) -> io::Result<Bytes>`: Read a byte range from the data source.
- `fn len(&self) -> io::Result<usize>`: The number of bytes in the data source.
- `fn read_at(&self, offset: usize, length: usize) -> io::Result<Bytes>`: Read a byte range from the data source.

**Note**: It is up to the Developer to ensure access patterns made by the Reader are efficient for the underlying
data source (i.e. managing rate limits, connection pooling, etc.). SparseIO will not attempt to optimize access patterns
Expand All @@ -20,19 +20,19 @@ The Writer component is responsible for writing data to the downstream cache in
speeds on future reads. This could be a local disk, a remote cache server, or any other location where data
can be written. As such the user is expected to provide three functions to produce a functional Writer:

- `func write(key: &str, offset: usize, data: Bytes)`: Write a byte range to the cache.
- `func read(key: &str, offset: usize, length: usize) -> Result<Option<Bytes>>`: Read a byte range from the cache.
- `func delete(key: &str)`: Delete a key from the cache.
- `fn write(&self, key: &str) -> io::Result<()>`: Write to the cache under `key`.
- `fn read(&self, key: &str) -> io::Result<Option<Bytes>>`: Read bytes from the cache under `key`.
- `fn delete(&self, key: &str) -> io::Result<()>`: Delete a key from the cache.

## Metadata

The Metadata component is responsible for keeping track of the data in the cache, the state of cache coverage for
individual data sources, and other relevant metadata for the application. As such it is just a generic interface
to a key-value store and the user is expected to provide three functions to produce a functional Metadata store:

- `func get(key: &str) -> Result<Option<Bytes>>`: Get a value from the metadata store.
- `func set(key: &str, value: Bytes)`: Set a value in the metadata store.
- `func delete(key: &str)`: Delete a key from the metadata store.
- `fn get(&self, key: &str) -> io::Result<Option<Bytes>>`: Get a value from the metadata store.
- `fn set(&self, key: &str, value: Bytes) -> io::Result<()>`: Set a value in the metadata store.
- `fn delete(&self, key: &str) -> io::Result<()>`: Delete a key from the metadata store.

## Sample User Application Diagram

Expand Down
4 changes: 2 additions & 2 deletions docs/architecture/FLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ In this section we will go over the flow of how a read request is processed in S

When a user initially constructs a SparseIO object they are required to provide a `Metadata`
implementation, a `Writer` implementation, and a `ReaderRegistry` implementation loaded with
the `Reader` implementations they want to use. This is all done through the `SparseIOBuilder` object, which
the `Reader` implementations they want to use. This is all done through the `Builder` object, which
provides a nice interface for constructing the `SparseIO` object without having to worry about defaults.

Users can then call `SparseIO::open` with a canonicalized path to get back a `Viewer` object.
This looks something like `instance.open(mysite+/path/to/object)`. The main component of the
This looks something like `instance.open("mysite+/path/to/object")`. The main component of the
viewer object is the `read_at()` method because it provides the core functionality
behind the bytestream, and general reads.

Expand Down
12 changes: 6 additions & 6 deletions docs/architecture/OBJECTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The SparseIO object is the main interface for users of the library. It provides
backing logic management behind the interaction between the `Reader`, `Writer`,
and `Metadata` objects to maintain the abstraction from the user.

When a user creates a SparseIO object, they do it through a SparseIOBuilder, which
When a user creates a SparseIO object, they do it through a `Builder`, which
is a [builder pattern](https://www.lurklurk.org/effective-rust/builders.html) making
it easy to construct the object without having to worry about defaults.

Expand All @@ -17,8 +17,8 @@ Three things are required to construct a SparseIO object:
- A [ReaderRegistry](#ReaderRegistry) implementation

There are some other tunable parameters that can be set in the builder, such as the
`chunk_size`, control over prefetching behavior, etc. However these are explained a
bit further in the [SparseIOBuilder docs](http://docs.rs/sparseio/latest/sparseio/struct.SparseIOBuilder.html).
`chunk_size`. However these are explained a bit further in the
[`Builder` docs](http://docs.rs/sparseio/latest/sparseio/struct.Builder.html).
Comment on lines +20 to +21

## ReaderRegistry

Expand Down Expand Up @@ -50,6 +50,6 @@ When a user attempts to read an object through the `SparseIO` object through `Sp
(e.g. `open("mysite+/path/to/object")`) they are actually getting back a `Viewer` object.
The Viewer provides a couple of core methods for interacting with the data including:

- `read(offset: usize, length: usize) -> io::Result<Bytes>`: Read a byte range from the object.
- `len() -> usize`: Get the total length of the object in bytes.
- `to_bytestream() -> impl Stream<Item = io::Result<Bytes>>`: Convert the Viewer to a bytestream for easier integration with async applications.
- `read_at(&self, offset: usize, length: usize) -> io::Result<Bytes>`: Read a byte range from the object.
- `len(&self) -> io::Result<usize>`: Get the total length of the object in bytes.
- `bytestream(&self) -> ByteStream`: Convert the Viewer to a bytestream for easier integration with applications.
Loading
Loading