Skip to content

Commit ef9fd5a

Browse files
implement list_with_deleted for azblob
1 parent 06f088d commit ef9fd5a

File tree

4 files changed

+49
-3
lines changed

4 files changed

+49
-3
lines changed

core/services/azblob/src/backend.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ impl AzblobBuilder {
237237
self
238238
}
239239

240+
/// Set the soft delete feature for this backend.
241+
///
242+
/// If enabled, deleted blobs will be retained for the configured retention period
243+
/// and can be listed using list_with_deleted.
244+
pub fn enable_soft_deletes(mut self, enabled: bool) -> Self {
245+
self.config.enable_soft_deletes = enabled;
246+
self
247+
}
248+
240249
/// from_connection_string will make a builder from connection string
241250
///
242251
/// connection string looks like:
@@ -402,6 +411,12 @@ impl Builder for AzblobBuilder {
402411

403412
list: true,
404413
list_with_recursive: true,
414+
list_has_etag: true,
415+
list_has_content_length: true,
416+
list_has_content_md5: true,
417+
list_has_content_type: true,
418+
list_has_last_modified: true,
419+
list_with_deleted: self.config.enable_soft_deletes,
405420

406421
presign: self.config.sas_token.is_some(),
407422
presign_stat: self.config.sas_token.is_some(),

core/services/azblob/src/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ pub struct AzblobConfig {
7676

7777
/// The maximum batch operations of Azblob service backend.
7878
pub batch_max_operations: Option<usize>,
79+
80+
/// Enable soft deletes for this storage account.
81+
#[serde(default)]
82+
pub enable_soft_deletes: bool,
7983
}
8084

8185
impl Debug for AzblobConfig {
@@ -84,6 +88,7 @@ impl Debug for AzblobConfig {
8488
.field("root", &self.root)
8589
.field("container", &self.container)
8690
.field("endpoint", &self.endpoint)
91+
.field("enable_soft_deletes", &self.enable_soft_deletes)
8792
.finish_non_exhaustive()
8893
}
8994
}

core/services/azblob/src/core.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@ impl AzblobCore {
599599
next_marker: &str,
600600
delimiter: &str,
601601
limit: Option<usize>,
602+
include_deleted: bool,
602603
) -> Result<Response<Buffer>> {
603604
let p = build_abs_path(&self.root, path);
604605
let mut url = QueryPairsWriter::new(&format!("{}/{}", self.endpoint, self.container))
@@ -618,6 +619,10 @@ impl AzblobCore {
618619
url = url.push("marker", next_marker);
619620
}
620621

622+
if include_deleted {
623+
url = url.push("include", "deleted");
624+
}
625+
621626
let mut req = Request::get(url.finish())
622627
.extension(Operation::List)
623628
.body(Buffer::new())
@@ -685,6 +690,8 @@ pub struct BlobPrefix {
685690
pub struct Blob {
686691
pub properties: Properties,
687692
pub name: String,
693+
#[serde(rename = "Deleted")]
694+
pub deleted: Option<bool>,
688695
}
689696

690697
#[derive(Default, Debug, Deserialize)]

core/services/azblob/src/lister.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,25 @@ pub struct AzblobLister {
3232
path: String,
3333
delimiter: &'static str,
3434
limit: Option<usize>,
35+
deleted: bool,
3536
}
3637

3738
impl AzblobLister {
38-
pub fn new(core: Arc<AzblobCore>, path: String, recursive: bool, limit: Option<usize>) -> Self {
39+
pub fn new(
40+
core: Arc<AzblobCore>,
41+
path: String,
42+
recursive: bool,
43+
limit: Option<usize>,
44+
deleted: bool,
45+
) -> Self {
3946
let delimiter = if recursive { "" } else { "/" };
4047

4148
Self {
4249
core,
4350
path,
4451
delimiter,
4552
limit,
53+
deleted,
4654
}
4755
}
4856
}
@@ -51,7 +59,13 @@ impl oio::PageList for AzblobLister {
5159
async fn next_page(&self, ctx: &mut oio::PageContext) -> Result<()> {
5260
let resp = self
5361
.core
54-
.azblob_list_blobs(&self.path, &ctx.token, self.delimiter, self.limit)
62+
.azblob_list_blobs(
63+
&self.path,
64+
&ctx.token,
65+
self.delimiter,
66+
self.limit,
67+
self.deleted,
68+
)
5569
.await?;
5670

5771
if resp.status() != http::StatusCode::OK {
@@ -88,7 +102,7 @@ impl oio::PageList for AzblobLister {
88102
path = "/".to_string();
89103
}
90104

91-
let meta = Metadata::new(EntryMode::from_path(&path))
105+
let mut meta = Metadata::new(EntryMode::from_path(&path))
92106
// Keep fit with ETag header.
93107
.with_etag(format!("\"{}\"", object.properties.etag.as_str()))
94108
.with_content_length(object.properties.content_length)
@@ -98,6 +112,11 @@ impl oio::PageList for AzblobLister {
98112
object.properties.last_modified.as_str(),
99113
)?);
100114

115+
// Mark as deleted if this is a delete marker
116+
if object.deleted.unwrap_or(false) {
117+
meta = meta.with_is_deleted(true);
118+
}
119+
101120
let de = oio::Entry::with(path, meta);
102121
ctx.entries.push_back(de);
103122
}

0 commit comments

Comments
 (0)