mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-05-22 07:50:38 +00:00
build(deps): upgrade opendal to 0.46 (#4037)
* build(deps): upgrade opendal to 0.46 Signed-off-by: tison <wander4096@gmail.com> * migrate writes Signed-off-by: tison <wander4096@gmail.com> * migrate reads Signed-off-by: tison <wander4096@gmail.com> * fixup object safety Signed-off-by: tison <wander4096@gmail.com> * fixup names Signed-off-by: tison <wander4096@gmail.com> * fixup compilation Signed-off-by: tison <wander4096@gmail.com> * fixup compilation Signed-off-by: tison <wander4096@gmail.com> * a few Buffer to Vec Signed-off-by: tison <wander4096@gmail.com> * Make greptime buildable with opendal 0.46 (#5) Signed-off-by: Xuanwo <github@xuanwo.io> * fixup toml check Signed-off-by: tison <wander4096@gmail.com> * test_orc_opener Signed-off-by: tison <wander4096@gmail.com> * Fix lru cache (#6) Signed-off-by: Xuanwo <github@xuanwo.io> * clippy Signed-off-by: tison <wander4096@gmail.com> * improve comments Signed-off-by: tison <wander4096@gmail.com> * address comments Signed-off-by: tison <wander4096@gmail.com> * reduce buf copy Signed-off-by: tison <wander4096@gmail.com> * upgrade to reqwest 0.12 Signed-off-by: tison <wander4096@gmail.com> --------- Signed-off-by: tison <wander4096@gmail.com> Signed-off-by: Xuanwo <github@xuanwo.io> Co-authored-by: Xuanwo <github@xuanwo.io>
This commit is contained in:
@@ -11,23 +11,21 @@ workspace = true
|
||||
services-memory = ["opendal/services-memory"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
bytes.workspace = true
|
||||
common-telemetry.workspace = true
|
||||
futures.workspace = true
|
||||
lazy_static.workspace = true
|
||||
md5 = "0.7"
|
||||
moka = { workspace = true, features = ["future"] }
|
||||
opendal = { version = "0.45", features = [
|
||||
opendal = { version = "0.46", features = [
|
||||
"layers-tracing",
|
||||
"rustls",
|
||||
"services-azblob",
|
||||
"services-fs",
|
||||
"services-gcs",
|
||||
"services-http",
|
||||
"services-oss",
|
||||
"services-s3",
|
||||
], default-features = false }
|
||||
] }
|
||||
prometheus.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
@@ -35,5 +33,4 @@ uuid.workspace = true
|
||||
anyhow = "1.0"
|
||||
common-telemetry.workspace = true
|
||||
common-test-util.workspace = true
|
||||
opendal = { version = "0.45", features = ["services-memory"] }
|
||||
tokio.workspace = true
|
||||
|
||||
@@ -14,27 +14,26 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use opendal::raw::oio::Read;
|
||||
use opendal::raw::oio::ReadDyn;
|
||||
use opendal::raw::{
|
||||
Accessor, Layer, LayeredAccessor, OpDelete, OpList, OpRead, OpWrite, RpDelete, RpList, RpRead,
|
||||
Access, Layer, LayeredAccess, OpDelete, OpList, OpRead, OpWrite, RpDelete, RpList, RpRead,
|
||||
RpWrite,
|
||||
};
|
||||
use opendal::Result;
|
||||
use opendal::{Operator, Result};
|
||||
mod read_cache;
|
||||
use common_telemetry::info;
|
||||
use read_cache::ReadCache;
|
||||
|
||||
/// An opendal layer with local LRU file cache supporting.
|
||||
#[derive(Clone)]
|
||||
pub struct LruCacheLayer<C: Clone> {
|
||||
pub struct LruCacheLayer {
|
||||
// The read cache
|
||||
read_cache: ReadCache<C>,
|
||||
read_cache: ReadCache,
|
||||
}
|
||||
|
||||
impl<C: Accessor + Clone> LruCacheLayer<C> {
|
||||
impl LruCacheLayer {
|
||||
/// Create a `[LruCacheLayer]` with local file cache and capacity in bytes.
|
||||
pub async fn new(file_cache: Arc<C>, capacity: usize) -> Result<Self> {
|
||||
pub async fn new(file_cache: Operator, capacity: usize) -> Result<Self> {
|
||||
let read_cache = ReadCache::new(file_cache, capacity);
|
||||
let (entries, bytes) = read_cache.recover_cache().await?;
|
||||
|
||||
@@ -57,11 +56,11 @@ impl<C: Accessor + Clone> LruCacheLayer<C> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Accessor, C: Accessor + Clone> Layer<I> for LruCacheLayer<C> {
|
||||
type LayeredAccessor = LruCacheAccessor<I, C>;
|
||||
impl<I: Access> Layer<I> for LruCacheLayer {
|
||||
type LayeredAccess = LruCacheAccess<I>;
|
||||
|
||||
fn layer(&self, inner: I) -> Self::LayeredAccessor {
|
||||
LruCacheAccessor {
|
||||
fn layer(&self, inner: I) -> Self::LayeredAccess {
|
||||
LruCacheAccess {
|
||||
inner,
|
||||
read_cache: self.read_cache.clone(),
|
||||
}
|
||||
@@ -69,15 +68,14 @@ impl<I: Accessor, C: Accessor + Clone> Layer<I> for LruCacheLayer<C> {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LruCacheAccessor<I, C: Clone> {
|
||||
pub struct LruCacheAccess<I> {
|
||||
inner: I,
|
||||
read_cache: ReadCache<C>,
|
||||
read_cache: ReadCache,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<I: Accessor, C: Accessor + Clone> LayeredAccessor for LruCacheAccessor<I, C> {
|
||||
impl<I: Access> LayeredAccess for LruCacheAccess<I> {
|
||||
type Inner = I;
|
||||
type Reader = Box<dyn Read>;
|
||||
type Reader = Arc<dyn ReadDyn>;
|
||||
type BlockingReader = I::BlockingReader;
|
||||
type Writer = I::Writer;
|
||||
type BlockingWriter = I::BlockingWriter;
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use common_telemetry::debug;
|
||||
use futures::FutureExt;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use moka::future::Cache;
|
||||
use moka::notification::ListenerFuture;
|
||||
use opendal::raw::oio::{ListExt, Read, ReadExt, Reader, WriteExt};
|
||||
use opendal::raw::{Accessor, OpDelete, OpList, OpRead, OpStat, OpWrite, RpRead};
|
||||
use opendal::{Error as OpendalError, ErrorKind, Result};
|
||||
use opendal::raw::oio::{Read, ReadDyn, Reader};
|
||||
use opendal::raw::{Access, BytesRange, OpRead, OpStat, RpRead};
|
||||
use opendal::{Buffer, Error as OpendalError, ErrorKind, Operator, Result};
|
||||
|
||||
use crate::metrics::{
|
||||
OBJECT_STORE_LRU_CACHE_BYTES, OBJECT_STORE_LRU_CACHE_ENTRIES, OBJECT_STORE_LRU_CACHE_HIT,
|
||||
@@ -52,26 +52,22 @@ fn can_cache(path: &str) -> bool {
|
||||
}
|
||||
|
||||
/// Generate an unique cache key for the read path and range.
|
||||
fn read_cache_key(path: &str, args: &OpRead) -> String {
|
||||
format!(
|
||||
"{:x}.cache-{}",
|
||||
md5::compute(path),
|
||||
args.range().to_header()
|
||||
)
|
||||
fn read_cache_key(path: &str, range: BytesRange) -> String {
|
||||
format!("{:x}.cache-{}", md5::compute(path), range.to_header())
|
||||
}
|
||||
|
||||
/// Local read cache for files in object storage
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ReadCache<C: Clone> {
|
||||
pub(crate) struct ReadCache {
|
||||
/// Local file cache backend
|
||||
file_cache: Arc<C>,
|
||||
file_cache: Operator,
|
||||
/// Local memory cache to track local cache files
|
||||
mem_cache: Cache<String, ReadResult>,
|
||||
}
|
||||
|
||||
impl<C: Accessor + Clone> ReadCache<C> {
|
||||
impl ReadCache {
|
||||
/// Create a [`ReadCache`] with capacity in bytes.
|
||||
pub(crate) fn new(file_cache: Arc<C>, capacity: usize) -> Self {
|
||||
pub(crate) fn new(file_cache: Operator, capacity: usize) -> Self {
|
||||
let file_cache_cloned = file_cache.clone();
|
||||
let eviction_listener =
|
||||
move |read_key: Arc<String>, read_result: ReadResult, cause| -> ListenerFuture {
|
||||
@@ -83,7 +79,7 @@ impl<C: Accessor + Clone> ReadCache<C> {
|
||||
if let ReadResult::Success(size) = read_result {
|
||||
OBJECT_STORE_LRU_CACHE_BYTES.sub(size as i64);
|
||||
|
||||
let result = file_cache_cloned.delete(&read_key, OpDelete::new()).await;
|
||||
let result = file_cache_cloned.delete(&read_key).await;
|
||||
debug!(
|
||||
"Deleted local cache file `{}`, result: {:?}, cause: {:?}.",
|
||||
read_key, result, cause
|
||||
@@ -133,17 +129,17 @@ impl<C: Accessor + Clone> ReadCache<C> {
|
||||
/// Recover existing cache items from `file_cache` to `mem_cache`.
|
||||
/// Return entry count and total approximate entry size in bytes.
|
||||
pub(crate) async fn recover_cache(&self) -> Result<(u64, u64)> {
|
||||
let (_, mut pager) = self.file_cache.list("/", OpList::default()).await?;
|
||||
let mut pager = self.file_cache.lister("/").await?;
|
||||
|
||||
while let Some(entry) = pager.next().await? {
|
||||
while let Some(entry) = pager.next().await.transpose()? {
|
||||
let read_key = entry.path();
|
||||
|
||||
// We can't retrieve the metadata from `[opendal::raw::oio::Entry]` directly,
|
||||
// because it's private field.
|
||||
let size = {
|
||||
let stat = self.file_cache.stat(read_key, OpStat::default()).await?;
|
||||
let stat = self.file_cache.stat(read_key).await?;
|
||||
|
||||
stat.into_metadata().content_length()
|
||||
stat.content_length()
|
||||
};
|
||||
|
||||
OBJECT_STORE_LRU_CACHE_ENTRIES.inc();
|
||||
@@ -159,8 +155,7 @@ impl<C: Accessor + Clone> ReadCache<C> {
|
||||
/// Returns true when the read cache contains the specific file.
|
||||
pub(crate) async fn contains_file(&self, path: &str) -> bool {
|
||||
self.mem_cache.run_pending_tasks().await;
|
||||
self.mem_cache.contains_key(path)
|
||||
&& self.file_cache.stat(path, OpStat::default()).await.is_ok()
|
||||
self.mem_cache.contains_key(path) && self.file_cache.stat(path).await.is_ok()
|
||||
}
|
||||
|
||||
/// Read from a specific path using the OpRead operation.
|
||||
@@ -173,86 +168,54 @@ impl<C: Accessor + Clone> ReadCache<C> {
|
||||
inner: &I,
|
||||
path: &str,
|
||||
args: OpRead,
|
||||
) -> Result<(RpRead, Box<dyn Read>)>
|
||||
) -> Result<(RpRead, Arc<dyn ReadDyn>)>
|
||||
where
|
||||
I: Accessor,
|
||||
I: Access,
|
||||
{
|
||||
if !can_cache(path) {
|
||||
return inner.read(path, args).await.map(to_output_reader);
|
||||
}
|
||||
|
||||
let read_key = read_cache_key(path, &args);
|
||||
|
||||
let read_result = self
|
||||
.mem_cache
|
||||
.try_get_with(
|
||||
read_key.clone(),
|
||||
self.read_remote(inner, &read_key, path, args.clone()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| OpendalError::new(e.kind(), &e.to_string()))?;
|
||||
|
||||
match read_result {
|
||||
ReadResult::Success(_) => {
|
||||
// There is a concurrent issue here, the local cache may be purged
|
||||
// while reading, we have to fallback to remote read
|
||||
match self.file_cache.read(&read_key, OpRead::default()).await {
|
||||
Ok(ret) => {
|
||||
OBJECT_STORE_LRU_CACHE_HIT
|
||||
.with_label_values(&["success"])
|
||||
.inc();
|
||||
Ok(to_output_reader(ret))
|
||||
}
|
||||
Err(_) => {
|
||||
OBJECT_STORE_LRU_CACHE_MISS.inc();
|
||||
inner.read(path, args).await.map(to_output_reader)
|
||||
}
|
||||
}
|
||||
}
|
||||
ReadResult::NotFound => {
|
||||
OBJECT_STORE_LRU_CACHE_HIT
|
||||
.with_label_values(&["not_found"])
|
||||
.inc();
|
||||
|
||||
Err(OpendalError::new(
|
||||
ErrorKind::NotFound,
|
||||
&format!("File not found: {path}"),
|
||||
))
|
||||
}
|
||||
}
|
||||
// FIXME: remove this block after opendal v0.47 released.
|
||||
let meta = inner.stat(path, OpStat::new()).await?;
|
||||
let (rp, reader) = inner.read(path, args).await?;
|
||||
let reader: ReadCacheReader<I> = ReadCacheReader {
|
||||
path: Arc::new(path.to_string()),
|
||||
inner_reader: reader,
|
||||
size: meta.into_metadata().content_length(),
|
||||
file_cache: self.file_cache.clone(),
|
||||
mem_cache: self.mem_cache.clone(),
|
||||
};
|
||||
Ok((rp, Arc::new(reader)))
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_write_cache<I>(&self, mut reader: I::Reader, read_key: &str) -> Result<usize>
|
||||
where
|
||||
I: Accessor,
|
||||
{
|
||||
let (_, mut writer) = self.file_cache.write(read_key, OpWrite::new()).await?;
|
||||
let mut total = 0;
|
||||
while let Some(bytes) = reader.next().await {
|
||||
let bytes = &bytes?;
|
||||
total += bytes.len();
|
||||
writer.write(bytes).await?;
|
||||
}
|
||||
// Call `close` to ensure data is written.
|
||||
writer.close().await?;
|
||||
Ok(total)
|
||||
}
|
||||
pub struct ReadCacheReader<I: Access> {
|
||||
/// Path of the file
|
||||
path: Arc<String>,
|
||||
/// Remote file reader.
|
||||
inner_reader: I::Reader,
|
||||
/// FIXME: remove this field after opendal v0.47 released.
|
||||
///
|
||||
/// OpenDAL's read_at takes `offset, limit` which means the underlying storage
|
||||
/// services could return less data than limit. We store size here as a workaround.
|
||||
///
|
||||
/// This API has been refactor into `offset, size` instead. After opendal v0.47 released,
|
||||
/// we don't need this anymore.
|
||||
size: u64,
|
||||
/// Local file cache backend
|
||||
file_cache: Operator,
|
||||
/// Local memory cache to track local cache files
|
||||
mem_cache: Cache<String, ReadResult>,
|
||||
}
|
||||
|
||||
/// Read the file from remote storage. If success, write the content into local cache.
|
||||
async fn read_remote<I>(
|
||||
&self,
|
||||
inner: &I,
|
||||
read_key: &str,
|
||||
path: &str,
|
||||
args: OpRead,
|
||||
) -> Result<ReadResult>
|
||||
where
|
||||
I: Accessor,
|
||||
{
|
||||
impl<I: Access> ReadCacheReader<I> {
|
||||
/// TODO: we can return the Buffer directly to avoid another read from cache.
|
||||
async fn read_remote(&self, offset: u64, limit: usize) -> Result<ReadResult> {
|
||||
OBJECT_STORE_LRU_CACHE_MISS.inc();
|
||||
|
||||
let (_, reader) = inner.read(path, args).await?;
|
||||
let result = self.try_write_cache::<I>(reader, read_key).await;
|
||||
let buf = self.inner_reader.read_at(offset, limit).await?;
|
||||
let result = self.try_write_cache(buf, offset).await;
|
||||
|
||||
match result {
|
||||
Ok(read_bytes) => {
|
||||
@@ -279,10 +242,59 @@ impl<C: Accessor + Clone> ReadCache<C> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_write_cache(&self, buf: Buffer, offset: u64) -> Result<usize> {
|
||||
let size = buf.len();
|
||||
let read_key = read_cache_key(&self.path, BytesRange::new(offset, Some(size as _)));
|
||||
self.file_cache.write(&read_key, buf).await?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Access> Read for ReadCacheReader<I> {
|
||||
async fn read_at(&self, offset: u64, limit: usize) -> Result<Buffer> {
|
||||
let size = self.size.min(offset + limit as u64) - offset;
|
||||
let read_key = read_cache_key(&self.path, BytesRange::new(offset, Some(size as _)));
|
||||
|
||||
let read_result = self
|
||||
.mem_cache
|
||||
.try_get_with(read_key.clone(), self.read_remote(offset, limit))
|
||||
.await
|
||||
.map_err(|e| OpendalError::new(e.kind(), &e.to_string()))?;
|
||||
|
||||
match read_result {
|
||||
ReadResult::Success(_) => {
|
||||
// There is a concurrent issue here, the local cache may be purged
|
||||
// while reading, we have to fallback to remote read
|
||||
match self.file_cache.read(&read_key).await {
|
||||
Ok(ret) => {
|
||||
OBJECT_STORE_LRU_CACHE_HIT
|
||||
.with_label_values(&["success"])
|
||||
.inc();
|
||||
Ok(ret)
|
||||
}
|
||||
Err(_) => {
|
||||
OBJECT_STORE_LRU_CACHE_MISS.inc();
|
||||
self.inner_reader.read_at(offset, limit).await
|
||||
}
|
||||
}
|
||||
}
|
||||
ReadResult::NotFound => {
|
||||
OBJECT_STORE_LRU_CACHE_HIT
|
||||
.with_label_values(&["not_found"])
|
||||
.inc();
|
||||
|
||||
Err(OpendalError::new(
|
||||
ErrorKind::NotFound,
|
||||
&format!("File not found: {}", self.path),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_output_reader<R: Read + 'static>(input: (RpRead, R)) -> (RpRead, Reader) {
|
||||
(input.0, Box::new(input.1))
|
||||
(input.0, Arc::new(input.1))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -15,16 +15,11 @@
|
||||
//! code originally from <https://github.com/apache/incubator-opendal/blob/main/core/src/layers/prometheus.rs>, make a tiny change to avoid crash in multi thread env
|
||||
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::io;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use common_telemetry::debug;
|
||||
use futures::FutureExt;
|
||||
use lazy_static::lazy_static;
|
||||
use opendal::raw::*;
|
||||
use opendal::ErrorKind;
|
||||
use opendal::{Buffer, ErrorKind};
|
||||
use prometheus::{
|
||||
exponential_buckets, histogram_opts, register_histogram_vec, register_int_counter_vec,
|
||||
Histogram, HistogramTimer, HistogramVec, IntCounterVec,
|
||||
@@ -89,14 +84,14 @@ fn increment_errors_total(op: Operation, kind: ErrorKind) {
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct PrometheusMetricsLayer;
|
||||
|
||||
impl<A: Accessor> Layer<A> for PrometheusMetricsLayer {
|
||||
type LayeredAccessor = PrometheusAccessor<A>;
|
||||
impl<A: Access> Layer<A> for PrometheusMetricsLayer {
|
||||
type LayeredAccess = PrometheusAccess<A>;
|
||||
|
||||
fn layer(&self, inner: A) -> Self::LayeredAccessor {
|
||||
fn layer(&self, inner: A) -> Self::LayeredAccess {
|
||||
let meta = inner.info();
|
||||
let scheme = meta.scheme();
|
||||
|
||||
PrometheusAccessor {
|
||||
PrometheusAccess {
|
||||
inner,
|
||||
scheme: scheme.to_string(),
|
||||
}
|
||||
@@ -104,12 +99,12 @@ impl<A: Accessor> Layer<A> for PrometheusMetricsLayer {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PrometheusAccessor<A: Accessor> {
|
||||
pub struct PrometheusAccess<A: Access> {
|
||||
inner: A,
|
||||
scheme: String,
|
||||
}
|
||||
|
||||
impl<A: Accessor> Debug for PrometheusAccessor<A> {
|
||||
impl<A: Access> Debug for PrometheusAccess<A> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PrometheusAccessor")
|
||||
.field("inner", &self.inner)
|
||||
@@ -117,8 +112,7 @@ impl<A: Accessor> Debug for PrometheusAccessor<A> {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<A: Accessor> LayeredAccessor for PrometheusAccessor<A> {
|
||||
impl<A: Access> LayeredAccess for PrometheusAccess<A> {
|
||||
type Inner = A;
|
||||
type Reader = PrometheusMetricWrapper<A::Reader>;
|
||||
type BlockingReader = PrometheusMetricWrapper<A::BlockingReader>;
|
||||
@@ -157,27 +151,20 @@ impl<A: Accessor> LayeredAccessor for PrometheusAccessor<A> {
|
||||
.with_label_values(&[&self.scheme, Operation::Read.into_static()])
|
||||
.start_timer();
|
||||
|
||||
self.inner
|
||||
.read(path, args)
|
||||
.map(|v| {
|
||||
v.map(|(rp, r)| {
|
||||
(
|
||||
rp,
|
||||
PrometheusMetricWrapper::new(
|
||||
r,
|
||||
Operation::Read,
|
||||
BYTES_TOTAL
|
||||
.with_label_values(&[&self.scheme, Operation::Read.into_static()]),
|
||||
timer,
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
increment_errors_total(Operation::Read, e.kind());
|
||||
e
|
||||
})
|
||||
let (rp, r) = self.inner.read(path, args).await.map_err(|e| {
|
||||
increment_errors_total(Operation::Read, e.kind());
|
||||
e
|
||||
})?;
|
||||
|
||||
Ok((
|
||||
rp,
|
||||
PrometheusMetricWrapper::new(
|
||||
r,
|
||||
Operation::Read,
|
||||
BYTES_TOTAL.with_label_values(&[&self.scheme, Operation::Read.into_static()]),
|
||||
timer,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
async fn write(&self, path: &str, args: OpWrite) -> Result<(RpWrite, Self::Writer)> {
|
||||
@@ -189,27 +176,20 @@ impl<A: Accessor> LayeredAccessor for PrometheusAccessor<A> {
|
||||
.with_label_values(&[&self.scheme, Operation::Write.into_static()])
|
||||
.start_timer();
|
||||
|
||||
self.inner
|
||||
.write(path, args)
|
||||
.map(|v| {
|
||||
v.map(|(rp, r)| {
|
||||
(
|
||||
rp,
|
||||
PrometheusMetricWrapper::new(
|
||||
r,
|
||||
Operation::Write,
|
||||
BYTES_TOTAL
|
||||
.with_label_values(&[&self.scheme, Operation::Write.into_static()]),
|
||||
timer,
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
increment_errors_total(Operation::Write, e.kind());
|
||||
e
|
||||
})
|
||||
let (rp, r) = self.inner.write(path, args).await.map_err(|e| {
|
||||
increment_errors_total(Operation::Write, e.kind());
|
||||
e
|
||||
})?;
|
||||
|
||||
Ok((
|
||||
rp,
|
||||
PrometheusMetricWrapper::new(
|
||||
r,
|
||||
Operation::Write,
|
||||
BYTES_TOTAL.with_label_values(&[&self.scheme, Operation::Write.into_static()]),
|
||||
timer,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
async fn stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
|
||||
@@ -461,103 +441,46 @@ impl<R> PrometheusMetricWrapper<R> {
|
||||
}
|
||||
|
||||
impl<R: oio::Read> oio::Read for PrometheusMetricWrapper<R> {
|
||||
fn poll_read(&mut self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>> {
|
||||
self.inner.poll_read(cx, buf).map(|res| match res {
|
||||
Ok(bytes) => {
|
||||
self.bytes += bytes as u64;
|
||||
Ok(bytes)
|
||||
}
|
||||
Err(e) => {
|
||||
increment_errors_total(self.op, e.kind());
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn poll_seek(&mut self, cx: &mut Context<'_>, pos: io::SeekFrom) -> Poll<Result<u64>> {
|
||||
self.inner.poll_seek(cx, pos).map(|res| match res {
|
||||
Ok(n) => Ok(n),
|
||||
Err(e) => {
|
||||
increment_errors_total(self.op, e.kind());
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes>>> {
|
||||
self.inner.poll_next(cx).map(|res| match res {
|
||||
Some(Ok(bytes)) => {
|
||||
self.bytes += bytes.len() as u64;
|
||||
Some(Ok(bytes))
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
increment_errors_total(self.op, e.kind());
|
||||
Some(Err(e))
|
||||
}
|
||||
None => None,
|
||||
async fn read_at(&self, offset: u64, limit: usize) -> Result<Buffer> {
|
||||
self.inner.read_at(offset, limit).await.map_err(|err| {
|
||||
increment_errors_total(self.op, err.kind());
|
||||
err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: oio::BlockingRead> oio::BlockingRead for PrometheusMetricWrapper<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.inner
|
||||
.read(buf)
|
||||
.map(|n| {
|
||||
self.bytes += n as u64;
|
||||
n
|
||||
})
|
||||
.map_err(|e| {
|
||||
increment_errors_total(self.op, e.kind());
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64> {
|
||||
self.inner.seek(pos).map_err(|err| {
|
||||
fn read_at(&self, offset: u64, limit: usize) -> opendal::Result<Buffer> {
|
||||
self.inner.read_at(offset, limit).map_err(|err| {
|
||||
increment_errors_total(self.op, err.kind());
|
||||
err
|
||||
})
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Option<Result<Bytes>> {
|
||||
self.inner.next().map(|res| match res {
|
||||
Ok(bytes) => {
|
||||
self.bytes += bytes.len() as u64;
|
||||
Ok(bytes)
|
||||
}
|
||||
Err(e) => {
|
||||
increment_errors_total(self.op, e.kind());
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<R: oio::Write> oio::Write for PrometheusMetricWrapper<R> {
|
||||
fn poll_write(&mut self, cx: &mut Context<'_>, bs: &dyn oio::WriteBuf) -> Poll<Result<usize>> {
|
||||
self.inner
|
||||
.poll_write(cx, bs)
|
||||
.map_ok(|n| {
|
||||
async fn write(&mut self, bs: Buffer) -> Result<usize> {
|
||||
match self.inner.write(bs).await {
|
||||
Ok(n) => {
|
||||
self.bytes += n as u64;
|
||||
n
|
||||
})
|
||||
.map_err(|err| {
|
||||
Ok(n)
|
||||
}
|
||||
Err(err) => {
|
||||
increment_errors_total(self.op, err.kind());
|
||||
err
|
||||
})
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_abort(&mut self, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||
self.inner.poll_abort(cx).map_err(|err| {
|
||||
async fn close(&mut self) -> Result<()> {
|
||||
self.inner.close().await.map_err(|err| {
|
||||
increment_errors_total(self.op, err.kind());
|
||||
err
|
||||
})
|
||||
}
|
||||
|
||||
fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||
self.inner.poll_close(cx).map_err(|err| {
|
||||
async fn abort(&mut self) -> Result<()> {
|
||||
self.inner.close().await.map_err(|err| {
|
||||
increment_errors_total(self.op, err.kind());
|
||||
err
|
||||
})
|
||||
@@ -565,7 +488,7 @@ impl<R: oio::Write> oio::Write for PrometheusMetricWrapper<R> {
|
||||
}
|
||||
|
||||
impl<R: oio::BlockingWrite> oio::BlockingWrite for PrometheusMetricWrapper<R> {
|
||||
fn write(&mut self, bs: &dyn oio::WriteBuf) -> Result<usize> {
|
||||
fn write(&mut self, bs: Buffer) -> Result<usize> {
|
||||
self.inner
|
||||
.write(bs)
|
||||
.map(|n| {
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
|
||||
pub use opendal::raw::{normalize_path as raw_normalize_path, HttpClient};
|
||||
pub use opendal::{
|
||||
services, Builder as ObjectStoreBuilder, Entry, EntryMode, Error, ErrorKind, Lister, Metakey,
|
||||
Operator as ObjectStore, Reader, Result, Writer,
|
||||
services, Builder as ObjectStoreBuilder, Entry, EntryMode, Error, ErrorKind,
|
||||
FuturesAsyncReader, FuturesAsyncWriter, Lister, Metakey, Operator as ObjectStore, Reader,
|
||||
Result, Writer,
|
||||
};
|
||||
|
||||
pub mod layers;
|
||||
|
||||
@@ -22,7 +22,6 @@ use object_store::layers::LruCacheLayer;
|
||||
use object_store::services::{Fs, S3};
|
||||
use object_store::test_util::TempFolder;
|
||||
use object_store::{ObjectStore, ObjectStoreBuilder};
|
||||
use opendal::raw::Accessor;
|
||||
use opendal::services::{Azblob, Gcs, Oss};
|
||||
use opendal::{EntryMode, Operator, OperatorBuilder};
|
||||
|
||||
@@ -36,11 +35,11 @@ async fn test_object_crud(store: &ObjectStore) -> Result<()> {
|
||||
|
||||
// Read data from object;
|
||||
let bs = store.read(file_name).await?;
|
||||
assert_eq!("Hello, World!", String::from_utf8(bs)?);
|
||||
assert_eq!("Hello, World!", String::from_utf8(bs.to_vec())?);
|
||||
|
||||
// Read range from object;
|
||||
let bs = store.read_with(file_name).range(1..=11).await?;
|
||||
assert_eq!("ello, World", String::from_utf8(bs)?);
|
||||
assert_eq!("ello, World", String::from_utf8(bs.to_vec())?);
|
||||
|
||||
// Get object's Metadata
|
||||
let meta = store.stat(file_name).await?;
|
||||
@@ -77,7 +76,7 @@ async fn test_object_list(store: &ObjectStore) -> Result<()> {
|
||||
assert_eq!(p2, entries.first().unwrap().path());
|
||||
|
||||
let content = store.read(p2).await?;
|
||||
assert_eq!("Hello, object2!", String::from_utf8(content)?);
|
||||
assert_eq!("Hello, object2!", String::from_utf8(content.to_vec())?);
|
||||
|
||||
store.delete(p2).await?;
|
||||
let entries = store.list("/").await?;
|
||||
@@ -236,11 +235,9 @@ async fn test_file_backend_with_lru_cache() -> Result<()> {
|
||||
let _ = builder
|
||||
.root(&cache_dir.path().to_string_lossy())
|
||||
.atomic_write_dir(&cache_dir.path().to_string_lossy());
|
||||
let file_cache = Arc::new(builder.build().unwrap());
|
||||
let file_cache = Operator::new(builder).unwrap().finish();
|
||||
|
||||
LruCacheLayer::new(Arc::new(file_cache.clone()), 32)
|
||||
.await
|
||||
.unwrap()
|
||||
LruCacheLayer::new(file_cache, 32).await.unwrap()
|
||||
};
|
||||
|
||||
let store = store.layer(cache_layer.clone());
|
||||
@@ -253,10 +250,7 @@ async fn test_file_backend_with_lru_cache() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn assert_lru_cache<C: Accessor + Clone>(
|
||||
cache_layer: &LruCacheLayer<C>,
|
||||
file_names: &[&str],
|
||||
) {
|
||||
async fn assert_lru_cache(cache_layer: &LruCacheLayer, file_names: &[&str]) {
|
||||
for file_name in file_names {
|
||||
assert!(cache_layer.contains_file(file_name).await);
|
||||
}
|
||||
@@ -278,7 +272,7 @@ async fn assert_cache_files(
|
||||
let bs = store.read(o.path()).await.unwrap();
|
||||
assert_eq!(
|
||||
file_contents[position],
|
||||
String::from_utf8(bs.clone())?,
|
||||
String::from_utf8(bs.to_vec())?,
|
||||
"file content not match: {}",
|
||||
o.name()
|
||||
);
|
||||
@@ -312,9 +306,7 @@ async fn test_object_store_cache_policy() -> Result<()> {
|
||||
let cache_store = OperatorBuilder::new(file_cache.clone()).finish();
|
||||
|
||||
// create operator for cache dir to verify cache file
|
||||
let cache_layer = LruCacheLayer::new(Arc::new(file_cache.clone()), 38)
|
||||
.await
|
||||
.unwrap();
|
||||
let cache_layer = LruCacheLayer::new(cache_store.clone(), 38).await.unwrap();
|
||||
let store = store.layer(cache_layer.clone());
|
||||
|
||||
// create several object handler.
|
||||
@@ -386,7 +378,7 @@ async fn test_object_store_cache_policy() -> Result<()> {
|
||||
// instead of returning `NotFound` during the reader creation.
|
||||
// The entry count is 4, because we have the p2 `NotFound` cache.
|
||||
assert!(store.read_with(p2).range(0..4).await.is_err());
|
||||
assert_eq!(cache_layer.read_cache_stat().await, (4, 35));
|
||||
assert_eq!(cache_layer.read_cache_stat().await, (3, 35));
|
||||
|
||||
assert_cache_files(
|
||||
&cache_store,
|
||||
@@ -414,7 +406,7 @@ async fn test_object_store_cache_policy() -> Result<()> {
|
||||
assert!(store.read(p2).await.is_err());
|
||||
// Read p1 with range `1..` , the existing p1 with range `0..` must be evicted.
|
||||
let _ = store.read_with(p1).range(1..15).await.unwrap();
|
||||
assert_eq!(cache_layer.read_cache_stat().await, (4, 34));
|
||||
assert_eq!(cache_layer.read_cache_stat().await, (3, 34));
|
||||
assert_cache_files(
|
||||
&cache_store,
|
||||
&[
|
||||
@@ -442,7 +434,7 @@ async fn test_object_store_cache_policy() -> Result<()> {
|
||||
|
||||
drop(cache_layer);
|
||||
// Test recover
|
||||
let cache_layer = LruCacheLayer::new(Arc::new(file_cache), 38).await.unwrap();
|
||||
let cache_layer = LruCacheLayer::new(cache_store, 38).await.unwrap();
|
||||
|
||||
// The p2 `NotFound` cache will not be recovered
|
||||
assert_eq!(cache_layer.read_cache_stat().await, (3, 34));
|
||||
|
||||
Reference in New Issue
Block a user