Add reader for S3 (picks up #26)#47
Conversation
Co-authored-by: Gaute Hope <eg@gaute.vetsj.com>
|
@gauteh I'm looking for a light compiled option that's faster than and can replace h5coro . Did some initial benchmarking, and hidefix works really well, almost as fast as the C++ bindings, but with much less dependencies to manage and package:
This PR was mostly done using the new Fable / Mythos anthropic model (before they start charging for it next week), which seems to be doing pretty well so far. Let me know if you have any questions or have followup asks. |
|
Thanks! Very interesting! Just a couple of comments as they pop into my head:
All in all, I think this can be merged and then it can be extended later. Thanks for keeping it small and incremental when using AI, otherwise hard to review. I see some tests are failing, I bet Mythos/Fable can figure those out quickly :) |
|
Thanks for the quick look! Point by point:
|
|
Ok. Sounds good. Hopefully the tests can be fixed, I suspect most of the errors are due to outdated code / CI and not this PR. But it would be nice to see things compile to be sure that the new changes do not break things. Feel free to update dependencies also across major versions. |
|
Thanks, merged. The other errors are unrelated to this PR. |
Picks up and finishes #26 ("Add reader for S3"), ported onto current
main(the reader modules have moved around quite a bit since Aug 2023, so this is a hand-port of 876cdd2 rather than a rebase).I tried to follow the design you laid out in #26:
and your sketch in #38 (comment) (index built locally / deserialized via serde, another reader on top of the existing traits and slicing code).
What this does
S3Readerinsrc/reader/s3.rs, behind a news3cargo feature (off by default):rust-s3(alive and current — bumped 0.33 → 0.37, rustls kept). One deliberate change from your original commit:tokio-rustls-tlsinstead ofsync-rustls-tls, so requests can actually be made async per your comment above. Flagging it since it swapsattohttpcforreqwest(already in the dev-deps) in the feature-gated tree.read_to_asyncissues rangedGETs with up to 16 requests in flight (buffer_unordered);decode_chunk(gzip/shuffle) runs synchronously as responses arrive.group_chunk_slicessorted by file address; chunks closer than 32 KiB are coalesced into a single rangedGET(gap bytes fetched and discarded), capped at 8 MiB per request. Constants are ins3.rs, happy to tune.Readerimpl on top, soi.reader()-style usage andvalues::<T, _>()work unchanged: it drives the async path viaHandle::try_current()+block_in_placeinside a runtime, or a lazily-created static runtime outside one.Bucket(region, endpoint, credentials) is configured by the caller and passed toS3Reader::with_datasettogether with the object key; the reader only issues rangedGETs against it. This keeps Minio/AWS/custom endpoints out of the reader itself.Structure follows
direct.rs/cache.rs(struct shape,with_dataset, test style).Tests
Unit tests for the request-coalescing logic (contiguous merge, gap merge, max-size split, repeated slices of one chunk).
Integration tests against Minio, per your benchmark-rig suggestion in Add reader for S3 #26:
docker compose -f tests/docker-compose.minio.yml up -d HIDEFIX_S3_ENDPOINT=http://localhost:9000 cargo test --features s3They upload
tests/data/{coads_climatology.nc4,dmrpp/*.h5}and compareS3Readervalues against the hdf5 crate / local readers: full reads, a sliced read (exercises batching + segment copy), gzipped and shuffled chunks, and both the async (read_to_async) and the sync-inside-tokio paths. WhenHIDEFIX_S3_ENDPOINTis not set they skip cleanly (similar spirit to the#[ignore]d norkyst tests, but cheap enough to run whenever the endpoint is up).cargo testwithout the feature is green and the compiled dependency set is unchanged;cargo fmt --checkandcargo clippy --features s3 --all-targetsare clean for the new code. One honest caveat on the lockfile: addingrust-s3(even optional) forces semver-compatible bumps of a few shared locked deps (serde1.0.210→1.0.228 which now splits outserde_core,syn,libc—sysinfofrom the rust-s3 chain requires the newersyn/libc). The default-feature dependency set is otherwise identical.Not benchmarked against real S3 — local Minio has none of the latency that makes the batching/concurrency constants matter, so the Thredds-vs-Minio benchmark from #26 is still open.
Small rider
One extra line in
.github/workflows/python.yml: aubuntu-24.04-armentry in the wheel matrix, so tagged releases also publish linux aarch64 wheels (PyPI currently has none for that platform — relevant for Graviton/Lambda users like us).Questions for review
ParReadertoo; I left rayon-parallel reads out since request concurrency already overlaps the fetches and the decode is cheap relative to network. Want aParReader/Streamerimpl as a follow-up, or in this PR?CONCURRENT_REQUESTS/COALESCE_GAP/MAX_REQUEST_SZare consts; fine, or would you rather have them configurable onS3Reader?