Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for rust-s3 backend. #30

Merged
merged 14 commits into from
Feb 2, 2024
Merged

Add support for rust-s3 backend. #30

merged 14 commits into from
Feb 2, 2024

Conversation

lseelenbinder
Copy link
Member

  • Adds support for a S3 backend (which is optionally authenticated)

Fixes #11 and could be used to address maplibre/martin#1125.

Copy link
Collaborator

@nyurik nyurik left a comment

Choose a reason for hiding this comment

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

looks good! A few minor nits, and we need to re-export the underlying s3 crate somehow, but it conflicts with the s3.rs file. I would recommend NOT exporting the s3, http, and mmap namespaces. Instead, publish their content directly.

// --- this is lib.rs ---

pub use s3;  // re-export s3 crate as pmtiles::s3

mod s3_backend;
pub use s3_backend S3Backend;

mod http;
pub use http::HttpBackend;

Cargo.toml Outdated
@@ -13,6 +13,8 @@ categories = ["science::geo"]
[features]
default = []
http-async = ["dep:tokio", "dep:reqwest"]
s3-async = ["dep:tokio", "dep:rust-s3", "rust-s3/tokio-native-tls"]
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe call it s3-async-native ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call. Done.

src/error.rs Outdated
@@ -32,6 +32,9 @@ pub enum PmtError {
#[cfg(feature = "http-async")]
#[error("{0}")]
Http(#[from] PmtHttpError),
#[cfg(any(feature = "s3-async-rustls", feature = "s3-async"))]
#[error("{0}")]
Copy link
Collaborator

Choose a reason for hiding this comment

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

And above as well

Suggested change
#[error("{0}")]
#[error(transparent)]

src/s3.rs Outdated
let response_bytes = response.bytes();

if response_bytes.len() > length {
Err(crate::error::PmtS3Error::ResponseBodyTooLong(response_bytes.len(), length).into())
Copy link
Collaborator

Choose a reason for hiding this comment

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

shorten it with use ... at the top?

@lseelenbinder
Copy link
Member Author

looks good! A few minor nits, and we need to re-export the underlying s3 crate somehow, but it conflicts with the s3.rs file. I would recommend NOT exporting the s3, http, and mmap namespaces. Instead, publish their content directly.

// --- this is lib.rs ---

pub use s3;  // re-export s3 crate as pmtiles::s3

mod s3_backend;
pub use s3_backend S3Backend;

mod http;
pub use http::HttpBackend;

Do we want to re-export s3 or allow users to bring their own (as we do with reqwest)? I tried really hard to make this TLS implementation agnostic, but rust-s3 doesn't build properly when it doesn't have a concrete credentials type.

@lseelenbinder lseelenbinder self-assigned this Jan 31, 2024
@lseelenbinder lseelenbinder marked this pull request as ready for review January 31, 2024 13:10
Copy link
Collaborator

@nyurik nyurik left a comment

Choose a reason for hiding this comment

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

Do we want to re-export s3 or allow users to bring their own (as we do with reqwest)? I tried really hard to make this TLS implementation agnostic, but rust-s3 doesn't build properly when it doesn't have a concrete credentials type.

There is really no such thing as "bring your own" here. If we do not re-export a dependency, users must play the magic game of "match my dependencies with the dependencies of my dependency" - i.e. user must use the same version of reqwest lib as used by the API of the pmtiles crate. This is an antipattern because it will only work if Cargo ends up using identical version of reqwest for both. Otherwise, it might not compile. That's why sometimes people opt to use reqwest = "*" - which again is an antipattern, but they have to do it to match the API of a crate they want to use.

Instead, I think a much safer way is to either (1) ensure all pmtiles crate API only uses standard Rust types (or types declared in the pmtiles crate itself), or (2) re-export all 3rd party types together with all the other stuff related to those types - i.e. re-export reqwest and s3 sub-dependencies in their own namespaces like pmtiles::reqwest::* and pmtiles::s3::*.

Also, a few inline comments.

src/error.rs Outdated

#[cfg(any(feature = "s3-async-rustls", feature = "s3-async-native"))]
#[derive(Debug, Error)]
pub enum PmtS3Error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not flatten all errors - just add these to PmtError with a conditional #[cfg(...)] ? I am not sure there is much value in having multi-level errors here, is there?

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right. Done.

Comment on lines 1 to 8
#[cfg(any(feature = "s3-async-native", feature = "s3-async-rustls"))]
pub(crate) mod s3;

#[cfg(feature = "http-async")]
pub(crate) mod http;

#[cfg(feature = "mmap-async-tokio")]
pub(crate) mod mmap;
Copy link
Collaborator

Choose a reason for hiding this comment

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

you don't really need to control the access here - enough to say pub mod s3 - that shouldn't make it public on the crate level, as long as you do not use pub mod backend in lib.rs.

That said, mod.rs offer a better way to compartmentalize things - you don't need to expose internals of the backend mod to the rest of the crate - I think it is cleaner to expose just the needed structs (up to you though) - you can also just remove the (crate) and keep sub-namespaces.

Suggested change
#[cfg(any(feature = "s3-async-native", feature = "s3-async-rustls"))]
pub(crate) mod s3;
#[cfg(feature = "http-async")]
pub(crate) mod http;
#[cfg(feature = "mmap-async-tokio")]
pub(crate) mod mmap;
#[cfg(any(feature = "s3-async-native", feature = "s3-async-rustls"))]
mod s3;
#[cfg(any(feature = "s3-async-native", feature = "s3-async-rustls"))]
pub use s3::S3Backend;
#[cfg(feature = "http-async")]
mod http;
#[cfg(feature = "http-async")]
pub use http::HttpBackend;
#[cfg(feature = "mmap-async-tokio")]
mod mmap;
#[cfg(feature = "mmap-async-tokio")]
pub use mmap::MmapBackend;

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice catch, cleaned up.

@lseelenbinder
Copy link
Member Author

There is really no such thing as "bring your own" here. If we do not re-export a dependency, users must play the magic game of "match my dependencies with the dependencies of my dependency" - i.e. user must use the same version of reqwest lib as used by the API of the pmtiles crate. This is an antipattern because it will only work if Cargo ends up using identical version of reqwest for both. Otherwise, it might not compile. That's why sometimes people opt to use reqwest = "*" - which again is an antipattern, but they have to do it to match the API of a crate they want to use.

Instead, I think a much safer way is to either (1) ensure all pmtiles crate API only uses standard Rust types (or types declared in the pmtiles crate itself), or (2) re-export all 3rd party types together with all the other stuff related to those types - i.e. re-export reqwest and s3 sub-dependencies in their own namespaces like pmtiles::reqwest::* and pmtiles::s3::*.

Fair enough. And users can always bring their own, if they want—just because we're nice and re-export doesn't mean users will use the exports. 😄

Copy link
Collaborator

@nyurik nyurik left a comment

Choose a reason for hiding this comment

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

thanks!!

@lseelenbinder lseelenbinder merged commit 29be5e0 into main Feb 2, 2024
1 check passed
@lseelenbinder lseelenbinder deleted the add-s3-backend branch February 13, 2024 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for S3 (or compatible) backend
2 participants