This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new d53142ba9 feat(services/azdls): add recursive deletion for azdls 
(#7142)
d53142ba9 is described below

commit d53142ba94fe997dca366887af2b1f15559e7966
Author: meteorgan <[email protected]>
AuthorDate: Mon Jan 12 22:02:49 2026 +0800

    feat(services/azdls): add recursive deletion for azdls (#7142)
    
    * feat(services/azdls): add recursive deletion for azdls
    
    * set paginated=true only when hns is enabled
    
    * fix azdls config default value
---
 core/services/azdls/src/backend.rs | 10 +++++++
 core/services/azdls/src/config.rs  |  5 ++++
 core/services/azdls/src/core.rs    | 57 ++++++++++++++++++++++++++++++++++++++
 core/services/azdls/src/deleter.rs |  8 ++++--
 4 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/core/services/azdls/src/backend.rs 
b/core/services/azdls/src/backend.rs
index 19ef30188..7f6120a78 100644
--- a/core/services/azdls/src/backend.rs
+++ b/core/services/azdls/src/backend.rs
@@ -218,6 +218,12 @@ impl AzdlsBuilder {
 
         Ok(AzdlsConfig::from(config).into_builder())
     }
+
+    /// Enable or disable HNS (Hierarchical Namespace) for this backend.
+    pub fn enable_hns(mut self, enable: bool) -> Self {
+        self.config.enable_hns = enable;
+        self
+    }
 }
 
 impl Builder for AzdlsBuilder {
@@ -282,7 +288,10 @@ impl Builder for AzdlsBuilder {
                             write_with_if_not_exists: true,
 
                             create_dir: true,
+
                             delete: true,
+                            delete_with_recursive: true,
+
                             rename: true,
 
                             list: true,
@@ -297,6 +306,7 @@ impl Builder for AzdlsBuilder {
                 filesystem: self.config.filesystem.clone(),
                 root,
                 endpoint,
+                enable_hns: self.config.enable_hns,
                 loader: cred_loader,
                 signer,
             }),
diff --git a/core/services/azdls/src/config.rs 
b/core/services/azdls/src/config.rs
index f05c87705..b2b805132 100644
--- a/core/services/azdls/src/config.rs
+++ b/core/services/azdls/src/config.rs
@@ -25,6 +25,7 @@ use super::backend::AzdlsBuilder;
 
 /// Azure Data Lake Storage Gen2 Support.
 #[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
+#[serde(default)]
 pub struct AzdlsConfig {
     /// Root of this backend.
     pub root: Option<String>,
@@ -58,6 +59,10 @@ pub struct AzdlsConfig {
     /// - required for client_credentials authentication
     /// - default value: `https://login.microsoftonline.com`
     pub authority_host: Option<String>,
+    /// Whether hierarchical namespace (HNS) is enabled for the storage 
account.
+    /// When enabled, recursive deletion can use pagination to avoid timeouts 
on large directories.
+    /// - default value: `false`
+    pub enable_hns: bool,
 }
 
 impl Debug for AzdlsConfig {
diff --git a/core/services/azdls/src/core.rs b/core/services/azdls/src/core.rs
index f40cfc636..cc8d27622 100644
--- a/core/services/azdls/src/core.rs
+++ b/core/services/azdls/src/core.rs
@@ -38,6 +38,7 @@ use opendal_core::*;
 const X_MS_RENAME_SOURCE: &str = "x-ms-rename-source";
 const X_MS_VERSION: &str = "x-ms-version";
 pub const X_MS_VERSION_ID: &str = "x-ms-version-id";
+const X_MS_CONTINUATION: &str = "x-ms-continuation";
 pub const DIRECTORY: &str = "directory";
 pub const FILE: &str = "file";
 
@@ -46,6 +47,7 @@ pub struct AzdlsCore {
     pub filesystem: String,
     pub root: String,
     pub endpoint: String,
+    pub enable_hns: bool,
 
     pub loader: AzureStorageLoader,
     pub signer: AzureStorageSigner,
@@ -57,6 +59,7 @@ impl Debug for AzdlsCore {
             .field("filesystem", &self.filesystem)
             .field("root", &self.root)
             .field("endpoint", &self.endpoint)
+            .field("enable_hns", &self.enable_hns)
             .finish_non_exhaustive()
     }
 }
@@ -366,6 +369,60 @@ impl AzdlsCore {
         self.send(req).await
     }
 
+    pub async fn azdls_recursive_delete(&self, path: &str) -> 
Result<Response<Buffer>> {
+        let p = build_abs_path(&self.root, path)
+            .trim_end_matches('/')
+            .to_string();
+
+        let base = format!(
+            "{}/{}/{}",
+            self.endpoint,
+            self.filesystem,
+            percent_encode_path(&p)
+        );
+
+        let mut continuation = String::new();
+
+        loop {
+            let mut url = QueryPairsWriter::new(&base).push("recursive", 
"true");
+
+            if self.enable_hns {
+                url = url.push("paginated", "true");
+            }
+
+            if !continuation.is_empty() {
+                url = url.push("continuation", 
&percent_encode_path(&continuation));
+            }
+
+            let mut req = Request::delete(url.finish())
+                .extension(Operation::Delete)
+                .body(Buffer::new())
+                .map_err(new_request_build_error)?;
+
+            self.sign(&mut req).await?;
+            let resp = self.send(req).await?;
+
+            let status = resp.status();
+            match status {
+                StatusCode::OK | StatusCode::ACCEPTED | StatusCode::NOT_FOUND 
=> {}
+                _ => return Err(parse_error(resp)),
+            }
+
+            let next = resp
+                .headers()
+                .get(X_MS_CONTINUATION)
+                .and_then(|v| v.to_str().ok())
+                .unwrap_or_default()
+                .trim();
+
+            if next.is_empty() {
+                return Ok(resp);
+            }
+
+            continuation = next.to_string();
+        }
+    }
+
     pub async fn azdls_list(
         &self,
         path: &str,
diff --git a/core/services/azdls/src/deleter.rs 
b/core/services/azdls/src/deleter.rs
index f90c87c8a..56dc359be 100644
--- a/core/services/azdls/src/deleter.rs
+++ b/core/services/azdls/src/deleter.rs
@@ -35,8 +35,12 @@ impl AzdlsDeleter {
 }
 
 impl oio::OneShotDelete for AzdlsDeleter {
-    async fn delete_once(&self, path: String, _: OpDelete) -> Result<()> {
-        let resp = self.core.azdls_delete(&path).await?;
+    async fn delete_once(&self, path: String, args: OpDelete) -> Result<()> {
+        let resp = if args.recursive() {
+            self.core.azdls_recursive_delete(&path).await?
+        } else {
+            self.core.azdls_delete(&path).await?
+        };
 
         let status = resp.status();
 

Reply via email to