11use std:: sync:: Arc ;
2- use std:: time:: Instant ;
2+ use std:: time:: { Duration , Instant } ;
33
44use bytes:: Bytes ;
55use delta_kernel_derive:: internal_api;
@@ -14,80 +14,111 @@ use crate::engine::default::executor::TaskExecutor;
1414use crate :: metrics:: { MetricEvent , MetricsReporter } ;
1515use 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 ) ]
92123pub 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