use std::ops::Range;
use std::sync::Arc;
use bytes::Bytes;
use log::warn;
use object_store::path::Path;
use object_store::{Extensions, GetOptions, GetRange, ObjectStore};
use crate::blob::ReadOnlyBlob;
use crate::error::SlateDBError;
use crate::object_store_tag::ObjectStoreCallTag;
macro_rules! read_obj {
($object_store:expr, $path:expr, $tag:expr, |$obj:ident| $read:expr) => {{
let object_store = $object_store;
let path = $path;
$crate::sst_io::read_with_validation_retry($tag, move |tag| {
let object_store = object_store.clone();
let path = path.clone();
async move {
let $obj = $crate::sst_io::ReadOnlyObject {
object_store,
path,
tag,
};
$read.await.map_err(|error| error.with_path(&$obj.path))
}
})
}};
}
pub(crate) use read_obj;
pub(crate) struct ReadOnlyObject {
pub(crate) object_store: Arc<dyn ObjectStore>,
pub(crate) path: Path,
pub(crate) tag: ObjectStoreCallTag,
}
impl ReadOnlyObject {
fn extensions(&self) -> Extensions {
self.tag.into()
}
}
impl ReadOnlyBlob for ReadOnlyObject {
async fn len(&self) -> Result<u64, SlateDBError> {
let opts = GetOptions {
head: true,
extensions: self.extensions(),
..GetOptions::default()
};
let result = self.object_store.get_opts(&self.path, opts).await?;
Ok(result.meta.size)
}
async fn read_range(&self, range: Range<u64>) -> Result<Bytes, SlateDBError> {
let opts = GetOptions {
range: Some(GetRange::Bounded(range)),
extensions: self.extensions(),
..GetOptions::default()
};
let result = self.object_store.get_opts(&self.path, opts).await?;
Ok(result.bytes().await?)
}
async fn read(&self) -> Result<Bytes, SlateDBError> {
let opts = GetOptions {
extensions: self.extensions(),
..GetOptions::default()
};
let result = self.object_store.get_opts(&self.path, opts).await?;
Ok(result.bytes().await?)
}
}
pub(crate) const MAX_VALIDATION_RETRIES: usize = 1;
pub(crate) async fn read_with_validation_retry<T, Fut>(
mut tag: ObjectStoreCallTag,
mut read: impl FnMut(ObjectStoreCallTag) -> Fut,
) -> Result<T, SlateDBError>
where
Fut: std::future::Future<Output = Result<T, SlateDBError>>,
{
for _ in 0..MAX_VALIDATION_RETRIES {
let result = read(tag).await;
match result {
Err(ref err) => match err.maybe_validation_retry_reason() {
Some(reason) => {
warn!(
"retrying SST read after validation failure [reason={:?}, error={}]",
reason, err
);
tag.retry = Some(reason);
}
None => return result,
},
Ok(_) => return result,
}
}
read(tag).await
}