Skip to content

Conversation

@zhaohaidao
Copy link
Contributor

Which issue does this PR close?

Closes #.

Rationale for this change

What changes are included in this PR?

Are there any user-facing changes?

@zhaohaidao zhaohaidao requested a review from Xuanwo as a code owner February 9, 2025 05:17
@github-actions github-actions bot added the releases-note/feat The PR implements a new feature or has a title that begins with "feat" label Feb 9, 2025
@zhaohaidao zhaohaidao changed the title feat(hdfs_native): implement HdfsNativeWriter write and close method (WIP)feat(hdfs_native): implement HdfsNativeWriter write and close method Feb 9, 2025
@zhaohaidao
Copy link
Contributor Author

@Xuanwo Hi, where can I add test cases for newly submitted code?

@zhaohaidao zhaohaidao changed the title (WIP)feat(hdfs_native): implement HdfsNativeWriter write and close method (WIP)feat(hdfs_native): implement HdfsNativeWriter write/read/list methods Feb 9, 2025
@zhaohaidao zhaohaidao changed the title (WIP)feat(hdfs_native): implement HdfsNativeWriter write/read/list methods (WIP)feat(services/hdfs_native): implement write/read/list methods Feb 9, 2025
@zhaohaidao
Copy link
Contributor Author

@Xuanwo Hi, where can I add test cases for newly submitted code?

ping @Xuanwo

@Xuanwo
Copy link
Member

Xuanwo commented Feb 12, 2025

@Xuanwo Hi, where can I add test cases for newly submitted code?

OpenDAL performs behavior tests on all services. Please check https://github.com/apache/opendal/blob/main/core/tests/behavior/README.md for more details.

@zhaohaidao
Copy link
Contributor Author

@Xuanwo Hi, where can I add test cases for newly submitted code?

OpenDAL performs behavior tests on all services. Please check https://github.com/apache/opendal/blob/main/core/tests/behavior/README.md for more details.

I have a question. These methods were not actually implemented before. How did the test cases pass?

@zhaohaidao zhaohaidao changed the title (WIP)feat(services/hdfs_native): implement write/read/list methods feat(services/hdfs_native): implement write/read/list methods Feb 12, 2025
@zhaohaidao
Copy link
Contributor Author

@Xuanwo Hi, where can I add test cases for newly submitted code?

OpenDAL performs behavior tests on all services. Please check https://github.com/apache/opendal/blob/main/core/tests/behavior/README.md for more details.

I have another question. I can't find the problem of CI when I run clippy locally. Do you have any troubleshooting suggestions?

@meteorgan
Copy link
Contributor

I have a question. These methods were not actually implemented before. How did the test cases pass?

You could check behavior test codes in the core/test/behavior directory. There's code like this:

pub fn tests(op: &Operator, tests: &mut Vec<Trial>) {
let cap = op.info().full_capability();
if cap.read && cap.write {
tests.extend(async_trials!(
op,
test_read_full,

so, if a service hasn't implemented those functionalities, they won't be tested. in this PR, you should enable those capabilities.

@meteorgan
Copy link
Contributor

meteorgan commented Feb 16, 2025

I have another question. I can't find the problem of CI when I run clippy locally. Do you have any troubleshooting suggestions?

Have you have checked the command used by the CI ? Maybe it differs from what you're using locally, or the versions might be different.

@zhaohaidao
Copy link
Contributor Author

I have a question. These methods were not actually implemented before. How did the test cases pass?

You could check behavior test codes in the core/test/behavior directory. There's code like this:

pub fn tests(op: &Operator, tests: &mut Vec<Trial>) {
let cap = op.info().full_capability();
if cap.read && cap.write {
tests.extend(async_trials!(
op,
test_read_full,

so, if a service hasn't implemented those functionalities, they won't be tested. in this PR, you should enable those capabilities.

Thank you very much. I have enabled the relevant configuration according to your suggestion.

@zhaohaidao
Copy link
Contributor Author

@Xuanwo @meteorgan CI passed. PTAL

Copy link
Member

@Xuanwo Xuanwo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a behavior test for this service.

We can add a new dir called hdfs_native under https://github.com/apache/opendal/tree/main/.github/services/:

And inside it, we build something similiar to https://github.com/apache/opendal/blob/main/.github/services/hdfs/hdfs_cluster/action.yml

Just change all OPENDAL_HDFS_xx to OPENDAL_HDFS_NATIVE_xx.

let p = build_rooted_abs_path(&self.root, path);
let l = HdfsNativeLister::new(p, self.client.clone());
Ok((RpList::default(), Some(l)))
let iter = self.client.list_status_iter(path, true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, please use OpList::recursive instead of always using true.

impl oio::Read for HdfsNativeReader {
async fn read(&mut self) -> Result<Buffer> {
todo!()
let bytes: Bytes = self.f.read(self.size).await.map_err(parse_hdfs_error)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will read all data in memory at once. Please use read_range_stream to get a bytes stream and yield bytes everytime users call read.

async fn write(&mut self, _bs: Buffer) -> Result<()> {
todo!()
async fn write(&mut self, mut bs: Buffer) -> Result<()> {
while bs.has_remaining() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bytes::copy_from_slice will copy the entire buffer while writing, it will add much cost here. How about this:

while let Some(bs) = bs.next() {
    self.f.write(bs).await
}

@zhaohaidao
Copy link
Contributor Author

Checking virtiofs_opendal v0.0.0 (/home/runner/work/opendal/opendal/integrations/virtiofs)
error: operator precedence can trip the unwary
   --> src/virtiofs.rs:218:9
    |
218 | /         1 << VIRTIO_F_VERSION_1
219 | |             | 1 << VIRTIO_RING_F_INDIRECT_DESC
220 | |             | 1 << VIRTIO_RING_F_EVENT_IDX
    | |__________________________________________^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#precedence
    = note: `-D clippy::precedence` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::precedence)]`
help: consider parenthesizing your expression
    |
218 ~         1 << VIRTIO_F_VERSION_1
219 +             | 1 << VIRTIO_RING_F_INDIRECT_DESC | (1 << VIRTIO_RING_F_EVENT_IDX)
    |

error: operator precedence can trip the unwary
   --> src/virtiofs.rs:218:9
    |
218 | /         1 << VIRTIO_F_VERSION_1
219 | |             | 1 << VIRTIO_RING_F_INDIRECT_DESC
    | |______________________________________________^ help: consider parenthesizing your expression: `(1 << VIRTIO_F_VERSION_1) | (1 << VIRTIO_RING_F_INDIRECT_DESC)`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#precedence

The error doesn't seem to be related to my submission. I don't quite understand why this error is reported. Can you give me some suggestions? @Xuanwo @meteorgan

@Xuanwo
Copy link
Member

Xuanwo commented Feb 21, 2025

The error doesn't seem to be related to my submission. I don't quite understand why this error is reported. Can you give me some suggestions? @Xuanwo @meteorgan

Yes, not related. New rust has been released.

@Xuanwo
Copy link
Member

Xuanwo commented Mar 3, 2025

Hi, @zhaohaidao it looks like you are blocked here. Would you like to share what's wrong happened?

@Xuanwo
Copy link
Member

Xuanwo commented Mar 3, 2025

For error like:

---- behavior::test_blocking_write_with_append_returns_metadata ----
test panicked: append to an existing file must success: AlreadyExists (persistent) at write => /tmp/opendal/f286eb91-202c-490b-99dd-04415556e07e/c9203715-f602-4237-9e34-86ed1c8d4297 for client 127.0.0.1 already exists
	at org.apache.hadoop.hdfs.server.namenode.FSDirWriteFileOp.startFile(FSDirWriteFileOp.java:388)
	at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.startFileInt(FSNamesystem.java:2536)
	at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.startFile(FSNamesystem.java:2433)
	at org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.create(NameNodeRpcServer.java:791)
	at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolServerSideTranslatorPB.create(ClientNamenodeProtocolServerSideTranslatorPB.java:475)
	at org.apache.hadoop.hdfs.protocol.proto.ClientNamenodeProtocolProtos$ClientNamenodeProtocol$2.callBlockingMethod(ClientNamenodeProtocolProtos.java)
	at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.call(ProtobufRpcEngine.java:528)
	at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:1070)
	at org.apache.hadoop.ipc.Server$RpcCall.run(Server.java:999)
	at org.apache.hadoop.ipc.Server$RpcCall.run(Server.java:927)
	at java.security.AccessController.doPrivileged(Native Method)
	at javax.security.auth.Subject.doAs(Subject.java:422)
	at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1730)
	at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2915)

You might need to check the WirteOptions inside Access::write call, this function might be useful: https://docs.rs/hdfs-native/0.11.1/hdfs_native/client/struct.Client.html#method.append

@Xuanwo
Copy link
Member

Xuanwo commented Mar 3, 2025

If you want debug them locally, you can use docker compose -f <docker-compose-file> and cargo test behavior --features=tests,services-hdfs-native for them.

@zhaohaidao
Copy link
Contributor Author

If you want debug them locally, you can use docker compose -f <docker-compose-file> and cargo test behavior --features=tests,services-hdfs-native for them.

Thanks, I'll try debugging locally

@zhaohaidao
Copy link
Contributor Author

Hi, @zhaohaidao it looks like you are blocked here. Would you like to share what's wrong happened?

Thanks, I am tracking down the problem of case failure when option recursive=true, for example test_list_empty_dir.
The failure stack is as follows, I am trying to analyze whether the behavior of hdfs_native is as expected

2025-03-03T16:42:04.462290Z DEBUG opendal::services: service=hdfs_native name= path=dc62cd36-b2be-40ef-8cdf-be3a27b16216/: list created lister
    at src/layers/logging.rs:220

  2025-03-03T16:42:04.462342Z TRACE opendal::services: service=hdfs_native name= path=dc62cd36-b2be-40ef-8cdf-be3a27b16216/ listed=0: List::next started
    at src/layers/logging.rs:220

  2025-03-03T16:42:04.462376Z  INFO opendal::services::hdfs_native::backend: backend list started. path dc62cd36-b2be-40ef-8cdf-be3a27b16216/ p /tmp/opendal/a2045853-d659-43e0-ae94-76f79c917547/dc62cd36-b2be-40ef-8cdf-be3a27b16216/
    at src/services/hdfs_native/backend.rs:254

  2025-03-03T16:42:04.467877Z TRACE opendal::services: service=hdfs_native name= path=dc62cd36-b2be-40ef-8cdf-be3a27b16216/ listed=1 entry=dc62cd36-b2be-40ef-8cdf-be3a27b16216/: List::next succeeded

thread '<unnamed>' panicked at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/unfold.rs:108:21:
Unfold must not be polled after it returned `Poll::Ready(None)`
stack backtrace:
    at src/layers/logging.rs:220

  2025-03-03T16:42:04.467933Z TRACE opendal::services: service=hdfs_native name= path=dc62cd36-b2be-40ef-8cdf-be3a27b16216/ listed=1: List::next started
    at src/layers/logging.rs:220

   0: rust_begin_unwind
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:692:5
   1: core::panicking::panic_fmt
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:75:14
   2: core::panicking::panic
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:145:5
   3: <futures_util::stream::unfold::Unfold<T,F,Fut> as futures_core::stream::Stream>::poll_next
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/unfold.rs:108:21
   4: <core::pin::Pin<P> as futures_core::stream::Stream>::poll_next
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-core-0.3.31/src/stream.rs:130:9
   5: futures_util::stream::stream::StreamExt::poll_next_unpin
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/stream/mod.rs:1638:9
   6: <futures_util::stream::stream::next::Next<St> as core::future::future::Future>::poll
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/stream/next.rs:32:9
   7: <opendal::services::hdfs_native::lister::HdfsNativeLister as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/services/hdfs_native/lister.rs:60:34
   8: <core::option::Option<P> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/raw/oio/list/api.rs:46:33
   9: <opendal::layers::error_context::ErrorContextWrapper<T> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/layers/error_context.rs:424:14
  10: <opendal::raw::oio::list::flat_list::FlatLister<A,L> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/raw/oio/list/flat_list.rs:104:33
  11: <opendal::raw::enum_utils::FourWays<ONE,TWO,THREE,FOUR> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/raw/enum_utils.rs:224:38
  12: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/future/future.rs:124:9
  13: <alloc::boxed::Box<T> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/raw/oio/list/api.rs:64:37
  14: <opendal::layers::logging::LoggingLister<P,I> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/layers/logging.rs:1198:37
  15: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/future/future.rs:124:9
  16: <alloc::boxed::Box<T> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/raw/oio/list/api.rs:64:37
  17: <tokio::time::timeout::Timeout<T> as core::future::future::Future>::poll
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/time/timeout.rs:202:33
  18: opendal::layers::timeout::TimeoutWrapper<R>::io_timeout::{{closure}}
             at ./src/layers/timeout.rs:348:44
  19: <opendal::layers::timeout::TimeoutWrapper<R> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/layers/timeout.rs:384:82
  20: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/future/future.rs:124:9
  21: <alloc::boxed::Box<T> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/raw/oio/list/api.rs:64:37
  22: <opendal::layers::retry::RetryWrapper<P,I> as opendal::raw::oio::list::api::List>::next::{{closure}}::{{closure}}::{{closure}}
             at ./src/layers/retry.rs:707:36
  23: <backon::retry_with_context::RetryWithContext<B,T,E,Ctx,Fut,FutureFn,SF,RF,NF> as core::future::future::Future>::poll
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/backon-1.3.0/src/retry_with_context.rs:327:45
  24: <opendal::layers::retry::RetryWrapper<P,I> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/layers/retry.rs:716:10
  25: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/future/future.rs:124:9
  26: <alloc::boxed::Box<T> as opendal::raw::oio::list::api::List>::next::{{closure}}
             at ./src/raw/oio/list/api.rs:64:37
  27: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/future/future.rs:124:9
  28: <opendal::types::list::Lister as futures_core::stream::Stream>::poll_next::{{closure}}
             at ./src/types/list.rs:70:45
  29: <opendal::types::list::Lister as futures_core::stream::Stream>::poll_next
             at ./src/types/list.rs:77:42
  30: <S as futures_core::stream::TryStream>::try_poll_next
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-core-0.3.31/src/stream.rs:206:9
  31: futures_util::stream::try_stream::TryStreamExt::try_poll_next_unpin
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/try_stream/mod.rs:1131:9
  32: <futures_util::stream::try_stream::try_next::TryNext<St> as core::future::future::Future>::poll
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/try_stream/try_next.rs:32:9
  33: behavior::async_list::test_list_empty_dir::{{closure}}
             at ./tests/behavior/async_list.rs:190:41
  34: tokio::runtime::park::CachedParkThread::block_on::{{closure}}
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/runtime/park.rs:281:63
  35: tokio::runtime::coop::with_budget
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/runtime/coop.rs:107:5
  36: tokio::runtime::coop::budget
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/runtime/coop.rs:73:5
  37: tokio::runtime::park::CachedParkThread::block_on
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/runtime/park.rs:281:31
  38: tokio::runtime::context::blocking::BlockingRegionGuard::block_on
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/runtime/context/blocking.rs:66:9
  39: tokio::runtime::handle::Handle::block_on_inner::{{closure}}
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/runtime/handle.rs:327:13
  40: tokio::runtime::context::runtime::enter_runtime
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/runtime/context/runtime.rs:65:16
  41: tokio::runtime::handle::Handle::block_on_inner
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/runtime/handle.rs:326:9
  42: tokio::runtime::handle::Handle::block_on
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.42.0/src/runtime/handle.rs:305:13
  43: behavior::utils::build_async_trial::{{closure}}
             at ./tests/behavior/utils.rs:71:9
  44: libtest_mimic::Trial::test::{{closure}}
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libtest-mimic-0.8.1/src/lib.rs:119:54
  45: core::ops::function::FnOnce::call_once{{vtable.shim}}
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/ops/function.rs:250:5
  46: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/alloc/src/boxed.rs:1993:9
  47: libtest_mimic::run_single::{{closure}}
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libtest-mimic-0.8.1/src/lib.rs:576:43
  48: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panic/unwind_safe.rs:272:9
  49: std::panicking::try::do_call
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:584:40
  50: __rust_try
  51: std::panicking::try
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:547:19
  52: std::panic::catch_unwind
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panic.rs:358:14
  53: libtest_mimic::run_single
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libtest-mimic-0.8.1/src/lib.rs:576:5
  54: libtest_mimic::run::{{closure}}::{{closure}}
             at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libtest-mimic-0.8.1/src/lib.rs:531:43
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
test behavior::test_list_empty_dir ... FAILED

failures:

---- behavior::test_list_empty_dir ----
test panicked: Unfold must not be polled after it returned `Poll::Ready(None)`

@zhaohaidao
Copy link
Contributor Author

@Xuanwo CI passed again. PTAL.

Copy link
Member

@Xuanwo Xuanwo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Congrats!

@Xuanwo Xuanwo merged commit b8a3fd5 into apache:main Mar 14, 2025
242 checks passed
kezhuw added a commit to kezhuw/opendal that referenced this pull request May 27, 2025
kezhuw added a commit to kezhuw/opendal that referenced this pull request May 27, 2025
Xuanwo pushed a commit that referenced this pull request May 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

releases-note/feat The PR implements a new feature or has a title that begins with "feat"

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants