@@ -10,21 +10,102 @@ use url::Url;
1010
1111use super :: UrlExt ;
1212use crate :: engine:: default:: executor:: TaskExecutor ;
13+ use crate :: metrics:: { MetricEvent , MetricsReporter , Timer } ;
1314use crate :: { DeltaResult , Error , FileMeta , FileSlice , StorageHandler } ;
1415
16+ /// Iterator wrapper that emits metrics when exhausted
17+ struct ListMetricsIterator < I > {
18+ inner : I ,
19+ reporter : Option < Arc < dyn MetricsReporter > > ,
20+ timer : Timer ,
21+ count : u64 ,
22+ metrics_emitted : bool ,
23+ }
24+
25+ impl < I : Iterator < Item = DeltaResult < FileMeta > > > Iterator for ListMetricsIterator < I > {
26+ type Item = DeltaResult < FileMeta > ;
27+
28+ fn next ( & mut self ) -> Option < Self :: Item > {
29+ match self . inner . next ( ) {
30+ Some ( item) => {
31+ if item. is_ok ( ) {
32+ self . count += 1 ;
33+ }
34+ Some ( item)
35+ }
36+ None => {
37+ if !self . metrics_emitted {
38+ if let Some ( ref r) = self . reporter {
39+ r. report ( MetricEvent :: StorageListCompleted {
40+ duration : self . timer . elapsed ( ) ,
41+ num_files : self . count ,
42+ } ) ;
43+ }
44+ self . metrics_emitted = true ;
45+ }
46+ None
47+ }
48+ }
49+ }
50+ }
51+
52+ /// Iterator wrapper for read operations that tracks bytes read
53+ struct ReadMetricsIterator < I > {
54+ inner : I ,
55+ reporter : Option < Arc < dyn MetricsReporter > > ,
56+ timer : Timer ,
57+ num_files : u64 ,
58+ bytes_read : u64 ,
59+ metrics_emitted : bool ,
60+ }
61+
62+ impl < I : Iterator < Item = DeltaResult < Bytes > > > Iterator for ReadMetricsIterator < I > {
63+ type Item = DeltaResult < Bytes > ;
64+
65+ fn next ( & mut self ) -> Option < Self :: Item > {
66+ match self . inner . next ( ) {
67+ Some ( item) => {
68+ if let Ok ( ref bytes) = item {
69+ self . bytes_read += bytes. len ( ) as u64 ;
70+ }
71+ Some ( item)
72+ }
73+ None => {
74+ if !self . metrics_emitted {
75+ if let Some ( ref r) = self . reporter {
76+ r. report ( MetricEvent :: StorageReadCompleted {
77+ duration : self . timer . elapsed ( ) ,
78+ num_files : self . num_files ,
79+ bytes_read : self . bytes_read ,
80+ } ) ;
81+ }
82+ self . metrics_emitted = true ;
83+ }
84+ None
85+ }
86+ }
87+ }
88+ }
89+
1590#[ derive( Debug ) ]
1691pub struct ObjectStoreStorageHandler < E : TaskExecutor > {
1792 inner : Arc < DynObjectStore > ,
1893 task_executor : Arc < E > ,
94+ reporter : Option < Arc < dyn MetricsReporter > > ,
1995 readahead : usize ,
2096}
2197
2298impl < E : TaskExecutor > ObjectStoreStorageHandler < E > {
2399 #[ internal_api]
24- pub ( crate ) fn new ( store : Arc < DynObjectStore > , task_executor : Arc < E > ) -> Self {
100+ pub ( crate ) fn new (
101+ store : Arc < DynObjectStore > ,
102+ task_executor : Arc < E > ,
103+ reporter : Option < Arc < dyn MetricsReporter > > ,
104+ ) -> Self {
25105 Self {
26106 inner : store,
27107 task_executor,
108+ reporter,
28109 readahead : 10 ,
29110 }
30111 }
@@ -106,13 +187,31 @@ impl<E: TaskExecutor> StorageHandler for ObjectStoreStorageHandler<E> {
106187 }
107188 } ) ;
108189
190+ let timer = Timer :: new ( ) ;
191+ let reporter = self . reporter . clone ( ) ;
192+
109193 if !has_ordered_listing {
110194 // This FS doesn't return things in the order we require
111195 let mut fms: Vec < FileMeta > = receiver. into_iter ( ) . try_collect ( ) ?;
112196 fms. sort_unstable ( ) ;
197+
198+ let num_files = fms. len ( ) as u64 ;
199+ if let Some ( ref r) = reporter {
200+ r. report ( MetricEvent :: StorageListCompleted {
201+ duration : timer. elapsed ( ) ,
202+ num_files,
203+ } ) ;
204+ }
205+
113206 Ok ( Box :: new ( fms. into_iter ( ) . map ( Ok ) ) )
114207 } else {
115- Ok ( Box :: new ( receiver. into_iter ( ) ) )
208+ Ok ( Box :: new ( ListMetricsIterator {
209+ inner : receiver. into_iter ( ) ,
210+ reporter,
211+ timer,
212+ count : 0 ,
213+ metrics_emitted : false ,
214+ } ) )
116215 }
117216 }
118217
@@ -127,6 +226,7 @@ impl<E: TaskExecutor> StorageHandler for ObjectStoreStorageHandler<E> {
127226 files : Vec < FileSlice > ,
128227 ) -> DeltaResult < Box < dyn Iterator < Item = DeltaResult < Bytes > > > > {
129228 let store = self . inner . clone ( ) ;
229+ let num_files = files. len ( ) as u64 ;
130230
131231 // This channel will become the output iterator.
132232 // Because there will already be buffering in the stream, we set the
@@ -173,19 +273,28 @@ impl<E: TaskExecutor> StorageHandler for ObjectStoreStorageHandler<E> {
173273 } ) ,
174274 ) ;
175275
176- Ok ( Box :: new ( receiver. into_iter ( ) ) )
276+ Ok ( Box :: new ( ReadMetricsIterator {
277+ inner : receiver. into_iter ( ) ,
278+ reporter : self . reporter . clone ( ) ,
279+ timer : Timer :: new ( ) ,
280+ num_files,
281+ bytes_read : 0 ,
282+ metrics_emitted : false ,
283+ } ) )
177284 }
178285
179286 fn copy_atomic ( & self , src : & Url , dest : & Url ) -> DeltaResult < ( ) > {
287+ let timer = Timer :: new ( ) ;
180288 let src_path = Path :: from_url_path ( src. path ( ) ) ?;
181289 let dest_path = Path :: from_url_path ( dest. path ( ) ) ?;
182290 let dest_path_str = dest_path. to_string ( ) ;
183291 let store = self . inner . clone ( ) ;
292+ let reporter = self . reporter . clone ( ) ;
184293
185294 // Read source file then write atomically with PutMode::Create. Note that a GET/PUT is not
186295 // necessarily atomic, but since the source file is immutable, we aren't exposed to the
187296 // possiblilty of source file changing while we do the PUT.
188- self . task_executor . block_on ( async move {
297+ let result = self . task_executor . block_on ( async move {
189298 let data = store. get ( & src_path) . await ?. bytes ( ) . await ?;
190299
191300 store
@@ -198,7 +307,17 @@ impl<E: TaskExecutor> StorageHandler for ObjectStoreStorageHandler<E> {
198307 e => e. into ( ) ,
199308 } ) ?;
200309 Ok ( ( ) )
201- } )
310+ } ) ;
311+
312+ if result. is_ok ( ) {
313+ if let Some ( ref r) = reporter {
314+ r. report ( MetricEvent :: StorageCopyCompleted {
315+ duration : timer. elapsed ( ) ,
316+ } ) ;
317+ }
318+ }
319+
320+ result
202321 }
203322}
204323
@@ -243,7 +362,7 @@ mod tests {
243362
244363 let store = Arc :: new ( LocalFileSystem :: new ( ) ) ;
245364 let executor = Arc :: new ( TokioBackgroundExecutor :: new ( ) ) ;
246- let storage = ObjectStoreStorageHandler :: new ( store, executor) ;
365+ let storage = ObjectStoreStorageHandler :: new ( store, executor, None ) ;
247366
248367 let mut slices: Vec < FileSlice > = Vec :: new ( ) ;
249368
@@ -274,7 +393,7 @@ mod tests {
274393 store. put ( & name, data. clone ( ) . into ( ) ) . await . unwrap ( ) ;
275394
276395 let table_root = Url :: parse ( "memory:///" ) . expect ( "valid url" ) ;
277- let engine = DefaultEngine :: new ( store, Arc :: new ( TokioBackgroundExecutor :: new ( ) ) ) ;
396+ let engine = DefaultEngine :: new ( store, Arc :: new ( TokioBackgroundExecutor :: new ( ) ) , None ) ;
278397 let files: Vec < _ > = engine
279398 . storage_handler ( )
280399 . list_from ( & table_root. join ( "_delta_log" ) . unwrap ( ) . join ( "0" ) . unwrap ( ) )
@@ -304,7 +423,7 @@ mod tests {
304423
305424 let url = Url :: from_directory_path ( tmp. path ( ) ) . unwrap ( ) ;
306425 let store = Arc :: new ( LocalFileSystem :: new ( ) ) ;
307- let engine = DefaultEngine :: new ( store, Arc :: new ( TokioBackgroundExecutor :: new ( ) ) ) ;
426+ let engine = DefaultEngine :: new ( store, Arc :: new ( TokioBackgroundExecutor :: new ( ) ) , None ) ;
308427 let files = engine
309428 . storage_handler ( )
310429 . list_from ( & url. join ( "_delta_log" ) . unwrap ( ) . join ( "0" ) . unwrap ( ) )
@@ -331,7 +450,7 @@ mod tests {
331450 let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
332451 let store = Arc :: new ( LocalFileSystem :: new ( ) ) ;
333452 let executor = Arc :: new ( TokioBackgroundExecutor :: new ( ) ) ;
334- let handler = ObjectStoreStorageHandler :: new ( store. clone ( ) , executor) ;
453+ let handler = ObjectStoreStorageHandler :: new ( store. clone ( ) , executor, None ) ;
335454
336455 // basic
337456 let data = Bytes :: from ( "test-data" ) ;
0 commit comments