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

Reply via email to