You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2026/08/27 10:14:35 UTC
(opendal) branch main updated: fix: repair azfile, b2, and conditional read behavior (#8175)
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 d5dd6dfa39 fix: repair azfile, b2, and conditional read behavior (#8175)
d5dd6dfa39 is described below
commit d5dd6dfa39ea97189aad46a6be611c6cb32b5351
Author: Xuanwo <gi...@xuanwo.io>
AuthorDate: Thu Aug 27 18:14:27 2026 +0800
fix: repair azfile, b2, and conditional read behavior (#8175)
---
.../dotnet/OpenDAL.Tests/Behavior/ReadBehaviorTest.cs | 11 +++++++++--
core/services/azdls/src/backend.rs | 8 ++++++--
core/services/azdls/src/core.rs | 19 +++++++++++++++++--
core/services/azdls/src/writer.rs | 2 +-
core/services/azfile/src/backend.rs | 3 +++
core/services/b2/src/backend.rs | 7 +++++--
6 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/bindings/dotnet/OpenDAL.Tests/Behavior/ReadBehaviorTest.cs b/bindings/dotnet/OpenDAL.Tests/Behavior/ReadBehaviorTest.cs
index b90d7a0409..11772c418c 100644
--- a/bindings/dotnet/OpenDAL.Tests/Behavior/ReadBehaviorTest.cs
+++ b/bindings/dotnet/OpenDAL.Tests/Behavior/ReadBehaviorTest.cs
@@ -77,7 +77,10 @@ public sealed class ReadBehaviorTest : BehaviorTestBase
// Covers the whole path end to end at a size worth measuring. The chunk
// walking itself is pinned by the unit tests in src/buffer.rs, which build
// multi-part buffers a backend may or may not produce.
- const int size = 8 * 1024 * 1024;
+ const int defaultSize = 8 * 1024 * 1024;
+ var size = (int)Math.Min(
+ (ulong)defaultSize,
+ Capability.WriteTotalMaxSize ?? ulong.MaxValue);
var path = NewPath("read-large");
var content = RandomBytes(size);
@@ -96,8 +99,12 @@ public sealed class ReadBehaviorTest : BehaviorTestBase
return;
}
+ const int defaultSize = 8 * 1024 * 1024;
+ var size = (int)Math.Min(
+ (ulong)defaultSize,
+ Capability.WriteTotalMaxSize ?? ulong.MaxValue);
var path = NewPath("read-large-async");
- var content = RandomBytes(8 * 1024 * 1024);
+ var content = RandomBytes(size);
await Op.WriteAsync(path, content, CT);
var actual = await Op.ReadAsync(path, CT);
diff --git a/core/services/azdls/src/backend.rs b/core/services/azdls/src/backend.rs
index 287a03081f..0a321d28b5 100644
--- a/core/services/azdls/src/backend.rs
+++ b/core/services/azdls/src/backend.rs
@@ -334,6 +334,10 @@ impl Builder for AzdlsBuilder {
let info = ServiceInfo::new(AZDLS_SCHEME, &root, filesystem);
let capability = Capability {
stat: true,
+ stat_with_if_match: true,
+ stat_with_if_none_match: true,
+ stat_with_if_modified_since: true,
+ stat_with_if_unmodified_since: true,
read: true,
read_with_if_match: true,
@@ -417,14 +421,14 @@ impl Service for AzdlsBackend {
}
}
- async fn stat(&self, ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
+ async fn stat(&self, ctx: &OperationContext, path: &str, args: OpStat) -> Result<RpStat> {
// Stat root always returns a DIR.
// TODO: include metadata for the root (#4746)
if path == "/" {
return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
}
- let metadata = self.core.azdls_stat_metadata(ctx, path).await?;
+ let metadata = self.core.azdls_stat_metadata(ctx, path, &args).await?;
Ok(RpStat::new(metadata))
}
fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
diff --git a/core/services/azdls/src/core.rs b/core/services/azdls/src/core.rs
index a63b226d38..f509ccd0c6 100644
--- a/core/services/azdls/src/core.rs
+++ b/core/services/azdls/src/core.rs
@@ -339,6 +339,7 @@ impl AzdlsCore {
&self,
ctx: &OperationContext,
path: &str,
+ args: &OpStat,
) -> Result<Response<Buffer>> {
let p = build_abs_path(&self.root, path)
.trim_end_matches('/')
@@ -351,7 +352,20 @@ impl AzdlsCore {
percent_encode_path(&p)
);
- let req = Request::head(&url);
+ let mut req = Request::head(&url);
+
+ if let Some(if_match) = args.if_match() {
+ req = req.header(IF_MATCH, if_match);
+ }
+ if let Some(if_none_match) = args.if_none_match() {
+ req = req.header(IF_NONE_MATCH, if_none_match);
+ }
+ if let Some(if_modified_since) = args.if_modified_since() {
+ req = req.header(IF_MODIFIED_SINCE, if_modified_since.format_http_date());
+ }
+ if let Some(if_unmodified_since) = args.if_unmodified_since() {
+ req = req.header(IF_UNMODIFIED_SINCE, if_unmodified_since.format_http_date());
+ }
let req = req
.extension(Operation::Stat)
@@ -367,8 +381,9 @@ impl AzdlsCore {
&self,
ctx: &OperationContext,
path: &str,
+ args: &OpStat,
) -> Result<Metadata> {
- let resp = self.azdls_get_properties(ctx, path).await?;
+ let resp = self.azdls_get_properties(ctx, path, args).await?;
if resp.status() != StatusCode::OK {
return Err(parse_error(resp));
diff --git a/core/services/azdls/src/writer.rs b/core/services/azdls/src/writer.rs
index 3bea6a0051..8e40933e56 100644
--- a/core/services/azdls/src/writer.rs
+++ b/core/services/azdls/src/writer.rs
@@ -162,7 +162,7 @@ impl oio::AppendWrite for AzdlsWriter {
async fn offset(&self) -> Result<u64> {
let resp = self
.core
- .azdls_get_properties(&self.ctx, &self.path)
+ .azdls_get_properties(&self.ctx, &self.path, &OpStat::default())
.await?;
let status = resp.status();
diff --git a/core/services/azfile/src/backend.rs b/core/services/azfile/src/backend.rs
index d3437bc07c..57b0dfedd9 100644
--- a/core/services/azfile/src/backend.rs
+++ b/core/services/azfile/src/backend.rs
@@ -237,6 +237,9 @@ impl Builder for AzfileBuilder {
write: true,
write_with_user_metadata: true,
+ // Azure Files limits each Put Range update to 4 MiB.
+ // ref: <https://learn.microsoft.com/en-us/rest/api/storageservices/put-range>
+ write_total_max_size: Some(4 * 1024 * 1024),
create_dir: true,
delete: true,
diff --git a/core/services/b2/src/backend.rs b/core/services/b2/src/backend.rs
index 883c0b0c09..bf3047e062 100644
--- a/core/services/b2/src/backend.rs
+++ b/core/services/b2/src/backend.rs
@@ -393,7 +393,7 @@ impl Service for B2Backend {
parts.headers,
)))
}
- PresignOperation::Read(_, _) => {
+ PresignOperation::Read(range, _) => {
let resp = self
.core
.get_download_authorization(ctx, path, args.expire())
@@ -410,7 +410,10 @@ impl Service for B2Backend {
resp.authorization_token
);
- let req = Request::get(url);
+ let mut req = Request::get(url);
+ if !range.is_full() {
+ req = req.header(http::header::RANGE, range.to_header());
+ }
let req = req.body(Buffer::new()).map_err(new_request_build_error)?;