Skip to content

Commit 83ea63e

Browse files
committed
how it looks with a separate trait
1 parent 50532ef commit 83ea63e

1 file changed

Lines changed: 86 additions & 55 deletions

File tree

kernel/src/engine/default/filesystem.rs

Lines changed: 86 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::sync::Arc;
2-
use std::time::Instant;
2+
use std::time::{Duration, Instant};
33

44
use bytes::Bytes;
55
use delta_kernel_derive::internal_api;
@@ -14,80 +14,111 @@ use crate::engine::default::executor::TaskExecutor;
1414
use crate::metrics::{MetricEvent, MetricsReporter};
1515
use crate::{DeltaResult, Error, FileMeta, FileSlice, StorageHandler};
1616

17-
/// Iterator wrapper that emits metrics when exhausted
18-
struct ListMetricsIterator<I> {
19-
inner: I,
20-
reporter: Option<Arc<dyn MetricsReporter>>,
21-
start: Instant,
22-
count: u64,
23-
metrics_emitted: bool,
17+
/// Trait for collecting metrics during iteration.
18+
///
19+
/// Different implementations track different statistics based on the operation type.
20+
trait MetricsCollector: Sized {
21+
type Item;
22+
23+
/// Called for each successful item to collect per-item statistics.
24+
fn collect(&mut self, item: &Self::Item);
25+
26+
/// Called when iterator is exhausted or dropped to emit metrics.
27+
/// The item_count is tracked by the iterator itself.
28+
fn emit(&self, reporter: &dyn MetricsReporter, duration: Duration, item_count: u64);
2429
}
2530

26-
impl<I: Iterator<Item = DeltaResult<FileMeta>>> Iterator for ListMetricsIterator<I> {
27-
type Item = DeltaResult<FileMeta>;
31+
/// Collector for list operations (FileMeta iteration).
32+
struct ListCollector;
2833

29-
fn next(&mut self) -> Option<Self::Item> {
30-
match self.inner.next() {
31-
Some(item) => {
32-
if item.is_ok() {
33-
self.count += 1;
34-
}
35-
Some(item)
36-
}
37-
None => {
38-
if !self.metrics_emitted {
39-
self.reporter.as_ref().inspect(|r| {
40-
r.report(MetricEvent::StorageListCompleted {
41-
duration: self.start.elapsed(),
42-
num_files: self.count,
43-
});
44-
});
45-
self.metrics_emitted = true;
46-
}
47-
None
48-
}
49-
}
34+
impl MetricsCollector for ListCollector {
35+
type Item = FileMeta;
36+
37+
fn collect(&mut self, _item: &FileMeta) {
38+
// No per-item work needed for listing
39+
}
40+
41+
fn emit(&self, reporter: &dyn MetricsReporter, duration: Duration, item_count: u64) {
42+
reporter.report(MetricEvent::StorageListCompleted {
43+
duration,
44+
num_files: item_count,
45+
});
5046
}
5147
}
5248

53-
/// Iterator wrapper for read operations that tracks bytes read
54-
struct ReadMetricsIterator<I> {
49+
/// Collector for read operations (Bytes iteration).
50+
struct ReadCollector {
51+
bytes_read: u64,
52+
}
53+
54+
impl MetricsCollector for ReadCollector {
55+
type Item = Bytes;
56+
57+
fn collect(&mut self, bytes: &Bytes) {
58+
self.bytes_read += bytes.len() as u64;
59+
}
60+
61+
fn emit(&self, reporter: &dyn MetricsReporter, duration: Duration, item_count: u64) {
62+
reporter.report(MetricEvent::StorageReadCompleted {
63+
duration,
64+
num_files: item_count,
65+
bytes_read: self.bytes_read,
66+
});
67+
}
68+
}
69+
70+
/// Unified iterator wrapper that emits metrics when exhausted or dropped.
71+
struct MetricsIterator<I, C: MetricsCollector> {
5572
inner: I,
5673
reporter: Option<Arc<dyn MetricsReporter>>,
5774
start: Instant,
58-
num_files: u64,
59-
bytes_read: u64,
75+
collector: C,
76+
item_count: u64,
6077
metrics_emitted: bool,
6178
}
6279

63-
impl<I: Iterator<Item = DeltaResult<Bytes>>> Iterator for ReadMetricsIterator<I> {
64-
type Item = DeltaResult<Bytes>;
80+
impl<I, C: MetricsCollector> MetricsIterator<I, C> {
81+
/// Emit metrics once, ensuring no double reporting.
82+
fn emit_metrics_once(&mut self) {
83+
if !self.metrics_emitted {
84+
self.reporter.as_ref().inspect(|r| {
85+
self.collector.emit(r.as_ref(), self.start.elapsed(), self.item_count);
86+
});
87+
self.metrics_emitted = true;
88+
}
89+
}
90+
}
91+
92+
impl<I, C> Iterator for MetricsIterator<I, C>
93+
where
94+
I: Iterator<Item = DeltaResult<C::Item>>,
95+
C: MetricsCollector,
96+
{
97+
type Item = DeltaResult<C::Item>;
6598

6699
fn next(&mut self) -> Option<Self::Item> {
67100
match self.inner.next() {
68101
Some(item) => {
69-
if let Ok(ref bytes) = item {
70-
self.bytes_read += bytes.len() as u64;
102+
if let Ok(ref value) = item {
103+
self.item_count += 1;
104+
self.collector.collect(value);
71105
}
72106
Some(item)
73107
}
74108
None => {
75-
if !self.metrics_emitted {
76-
self.reporter.as_ref().inspect(|r| {
77-
r.report(MetricEvent::StorageReadCompleted {
78-
duration: self.start.elapsed(),
79-
num_files: self.num_files,
80-
bytes_read: self.bytes_read,
81-
});
82-
});
83-
self.metrics_emitted = true;
84-
}
109+
self.emit_metrics_once();
85110
None
86111
}
87112
}
88113
}
89114
}
90115

116+
impl<I, C: MetricsCollector> Drop for MetricsIterator<I, C> {
117+
fn drop(&mut self) {
118+
self.emit_metrics_once();
119+
}
120+
}
121+
91122
#[derive(Debug)]
92123
pub struct ObjectStoreStorageHandler<E: TaskExecutor> {
93124
inner: Arc<DynObjectStore>,
@@ -206,11 +237,12 @@ impl<E: TaskExecutor> StorageHandler for ObjectStoreStorageHandler<E> {
206237

207238
Ok(Box::new(fms.into_iter().map(Ok)))
208239
} else {
209-
Ok(Box::new(ListMetricsIterator {
240+
Ok(Box::new(MetricsIterator {
210241
inner: receiver.into_iter(),
211242
reporter,
212243
start,
213-
count: 0,
244+
collector: ListCollector,
245+
item_count: 0,
214246
metrics_emitted: false,
215247
}))
216248
}
@@ -227,7 +259,6 @@ impl<E: TaskExecutor> StorageHandler for ObjectStoreStorageHandler<E> {
227259
files: Vec<FileSlice>,
228260
) -> DeltaResult<Box<dyn Iterator<Item = DeltaResult<Bytes>>>> {
229261
let store = self.inner.clone();
230-
let num_files = files.len() as u64;
231262

232263
// This channel will become the output iterator.
233264
// Because there will already be buffering in the stream, we set the
@@ -274,12 +305,12 @@ impl<E: TaskExecutor> StorageHandler for ObjectStoreStorageHandler<E> {
274305
}),
275306
);
276307

277-
Ok(Box::new(ReadMetricsIterator {
308+
Ok(Box::new(MetricsIterator {
278309
inner: receiver.into_iter(),
279310
reporter: self.reporter.clone(),
280311
start: Instant::now(),
281-
num_files,
282-
bytes_read: 0,
312+
collector: ReadCollector { bytes_read: 0 },
313+
item_count: 0,
283314
metrics_emitted: false,
284315
}))
285316
}

0 commit comments

Comments
 (0)