blob: 6d7c6abb588743bbd3edd2ec95d63802485d299e [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::future::pending;
use std::sync::{Arc, Mutex};
use anyhow::Result as AnyhowResult;
use bytes::Bytes;
use od::raw::oio;
use od::raw::*;
use od::{
Buffer, BytesRange, Capability, EntryMode, Error, ErrorKind, Metadata, OperationContext, Result,
};
use opendal as od;
use crate::Operator;
use crate::layer::LayerBuilder;
static RETRY_ATTEMPT: std::sync::LazyLock<Arc<Mutex<usize>>> =
std::sync::LazyLock::new(|| Arc::new(Mutex::new(0)));
pub fn retryable_attempt_count() -> usize {
*RETRY_ATTEMPT.lock().unwrap()
}
fn reset_retryable_attempts() {
*RETRY_ATTEMPT.lock().unwrap() = 0;
}
fn build_operator(
layers: &LayerBuilder,
service: impl Service + 'static,
) -> AnyhowResult<*mut Operator> {
let op = od::Operator::from_parts(OperationContext::default(), Arc::new(service));
let op = layers.apply(op);
let op = crate::into_blocking_operator(op)?;
Ok(Box::into_raw(Box::new(op)))
}
pub fn new_hanging_operator(layers: &LayerBuilder) -> AnyhowResult<*mut Operator> {
build_operator(layers, HangingService)
}
pub fn new_retryable_operator(layers: &LayerBuilder) -> AnyhowResult<*mut Operator> {
reset_retryable_attempts();
build_operator(
layers,
RetryableService {
attempt: RETRY_ATTEMPT.clone(),
},
)
}
#[derive(Debug, Clone, Default)]
struct HangingService;
impl Service for HangingService {
type Reader = oio::StreamReader<HangingReader>;
type Writer = ();
type Lister = ();
type Deleter = ();
type Copier = ();
fn info(&self) -> ServiceInfo {
ServiceInfo::with_scheme("mock-hanging")
}
fn capability(&self) -> Capability {
Capability {
read: true,
stat: true,
..Default::default()
}
}
async fn create_dir(
&self,
_: &OperationContext,
_: &str,
_: OpCreateDir,
) -> Result<RpCreateDir> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
async fn stat(&self, _: &OperationContext, _: &str, _: OpStat) -> Result<RpStat> {
Ok(RpStat::new(
Metadata::new(EntryMode::FILE).with_content_length(13),
))
}
fn read(&self, _: &OperationContext, _: &str, _: OpRead) -> Result<Self::Reader> {
Ok(oio::StreamReader::new(HangingReader))
}
fn write(&self, _: &OperationContext, _: &str, _: OpWrite) -> Result<Self::Writer> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
fn delete(&self, _: &OperationContext) -> Result<Self::Deleter> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
fn list(&self, _: &OperationContext, _: &str, _: OpList) -> Result<Self::Lister> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
fn copy(
&self,
_: &OperationContext,
_: &str,
_: &str,
_: OpCopy,
_: OpCopier,
) -> Result<Self::Copier> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
async fn rename(
&self,
_: &OperationContext,
_: &str,
_: &str,
_: OpRename,
) -> Result<RpRename> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
async fn presign(&self, _: &OperationContext, _: &str, _: OpPresign) -> Result<RpPresign> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
}
#[derive(Debug, Clone, Default)]
struct HangingReader;
impl oio::StreamRead for HangingReader {
async fn open(&self, _: BytesRange) -> Result<(RpRead, Box<dyn oio::ReadStreamDyn>)> {
pending::<()>().await;
unreachable!()
}
}
#[derive(Debug, Clone)]
struct RetryableService {
attempt: Arc<Mutex<usize>>,
}
impl Service for RetryableService {
type Reader = oio::StreamReader<RetryableReader>;
type Writer = ();
type Lister = ();
type Deleter = ();
type Copier = ();
fn info(&self) -> ServiceInfo {
ServiceInfo::with_scheme("mock-retryable")
}
fn capability(&self) -> Capability {
Capability {
read: true,
stat: true,
..Default::default()
}
}
async fn create_dir(
&self,
_: &OperationContext,
_: &str,
_: OpCreateDir,
) -> Result<RpCreateDir> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
async fn stat(&self, _: &OperationContext, _: &str, _: OpStat) -> Result<RpStat> {
Ok(RpStat::new(
Metadata::new(EntryMode::FILE).with_content_length(13),
))
}
fn read(&self, _: &OperationContext, _: &str, _: OpRead) -> Result<Self::Reader> {
Ok(oio::StreamReader::new(RetryableReader {
attempt: self.attempt.clone(),
}))
}
fn write(&self, _: &OperationContext, _: &str, _: OpWrite) -> Result<Self::Writer> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
fn delete(&self, _: &OperationContext) -> Result<Self::Deleter> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
fn list(&self, _: &OperationContext, _: &str, _: OpList) -> Result<Self::Lister> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
fn copy(
&self,
_: &OperationContext,
_: &str,
_: &str,
_: OpCopy,
_: OpCopier,
) -> Result<Self::Copier> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
async fn rename(
&self,
_: &OperationContext,
_: &str,
_: &str,
_: OpRename,
) -> Result<RpRename> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
async fn presign(&self, _: &OperationContext, _: &str, _: OpPresign) -> Result<RpPresign> {
Err(Error::new(
ErrorKind::Unsupported,
"operation is not supported",
))
}
}
#[derive(Debug, Clone)]
struct RetryableReader {
attempt: Arc<Mutex<usize>>,
}
impl oio::StreamRead for RetryableReader {
async fn open(&self, range: BytesRange) -> Result<(RpRead, Box<dyn oio::ReadStreamDyn>)> {
let rp = RpRead::new(Metadata::new(EntryMode::FILE).with_content_length(0));
Ok((
rp,
Box::new(RetryableReadStream {
buf: Bytes::from_static(b"Hello, World!").into(),
range,
attempt: self.attempt.clone(),
}) as Box<dyn oio::ReadStreamDyn>,
))
}
}
#[derive(Debug, Clone)]
struct RetryableReadStream {
buf: Buffer,
range: BytesRange,
attempt: Arc<Mutex<usize>>,
}
impl oio::ReadStream for RetryableReadStream {
async fn read(&mut self) -> Result<Buffer> {
let mut attempt = self.attempt.lock().unwrap();
*attempt += 1;
match *attempt {
1 | 2 | 4 => Err(
Error::new(ErrorKind::Unexpected, "retryable_error from reader").set_temporary(),
),
3 | 5 => Ok(self.buf.slice(self.range.to_range_as_usize())),
n => Err(Error::new(
ErrorKind::Unexpected,
format!("unexpected retryable attempt {n}"),
)),
}
}
}