This is an automated email from the ASF dual-hosted git repository.
alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs-object-store.git
The following commit(s) were added to refs/heads/main by this push:
new f3fe994 Return AlreadyExists in azure backend when using
PutMode::Create and precondition fails (#830)
f3fe994 is described below
commit f3fe9943f1139ea28411251bea98bc6b19e1bada
Author: June <[email protected]>
AuthorDate: Mon Aug 17 17:08:58 2026 -0400
Return AlreadyExists in azure backend when using PutMode::Create and
precondition fails (#830)
* Return AlreadyExists in azure backend when using PutMode::Create and
precondition fails
* chore: Add test coverage
---------
Co-authored-by: Andrew Lamb <[email protected]>
---
src/azure/client.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)
diff --git a/src/azure/client.rs b/src/azure/client.rs
index e0c7a5b..c5b0a52 100644
--- a/src/azure/client.rs
+++ b/src/azure/client.rs
@@ -764,7 +764,16 @@ impl AzureClient {
}
};
- let response = builder.header(&BLOB_TYPE, "BlockBlob").send().await?;
+ // based on
https://learn.microsoft.com/en-us/azure/storage/blobs/concurrency-manage, azure
+ // responds with `Precondition` when any put with a precondition
fails, but we promise to
+ // return `AlreadyExists` when that put mode is `Create`.
+ let response = match (builder.header(&BLOB_TYPE,
"BlockBlob").send().await, mode) {
+ (Err(crate::Error::Precondition { path, source }),
PutMode::Create) => {
+ return Err(crate::Error::AlreadyExists { path, source });
+ }
+ (r, _) => r?,
+ };
+
Ok(
get_put_result(response, VERSION_HEADER)
.map_err(|source| Error::Metadata { source })?,
@@ -2243,6 +2252,41 @@ Authorization: Bearer static-token\r
assert!(!msg.contains(&endpoint), "{msg}");
}
+ #[cfg(feature = "reqwest")]
+ #[tokio::test]
+ async fn test_put_mode_create_translates_precondition_to_already_exists() {
+ let server = crate::client::mock_server::MockServer::new().await;
+ let client = test_client(server.url());
+ let update = PutMode::Update(crate::UpdateVersion {
+ e_tag: Some("\"etag\"".to_string()),
+ version: None,
+ });
+
+ // Real Azure reports a failed put precondition as `412 Precondition
Failed`,
+ // Azurite as `409 Conflict`; both must surface as `AlreadyExists` for
+ // `PutMode::Create`, while `PutMode::Update` failures remain
`Precondition`
+ for (status, mode, want_already_exists) in [
+ (412, PutMode::Create, true),
+ (409, PutMode::Create, true),
+ (412, update, false),
+ ] {
+ server.push(
+ http::Response::builder()
+ .status(status)
+ .body(String::new())
+ .unwrap(),
+ );
+ let err = client
+ .put_blob(&Path::from("file.txt"), "data".into(), mode.into())
+ .await
+ .unwrap_err();
+ match want_already_exists {
+ true => assert!(matches!(err, crate::Error::AlreadyExists { ..
}), "{err}"),
+ false => assert!(matches!(err, crate::Error::Precondition { ..
}), "{err}"),
+ }
+ }
+ }
+
#[tokio::test]
async fn test_parse_blob_batch_delete_body() {
let response_body =
b"--batchresponse_66925647-d0cb-4109-b6d3-28efe3e1e5ed\r