@@ -71,19 +71,35 @@ impl TaskEstimator for DataSourceExecPartitionEstimator {
7171
7272 fn route_tasks ( & self , routing_ctx : & TaskRoutingContext < ' _ > ) -> DFResult < Option < Vec < Url > > > {
7373 if routing_ctx. available_urls . is_empty ( ) {
74+ log_routing_fallback ( routing_ctx, "no_available_workers" ) ;
7475 return Ok ( None ) ;
7576 }
76- let Some ( file_scan) = single_file_scan_config ( routing_ctx. plan ) else {
77+ let mut file_scans = Vec :: new ( ) ;
78+ collect_file_scan_configs ( routing_ctx. plan , & mut file_scans) ;
79+ let [ file_scan] = file_scans. as_slice ( ) else {
80+ let reason = if file_scans. is_empty ( ) {
81+ "no_file_scan_config"
82+ } else {
83+ "multiple_file_scan_configs"
84+ } ;
85+ log_routing_fallback ( routing_ctx, reason) ;
7786 return Ok ( None ) ;
7887 } ;
7988 let task_affinities = file_scan_task_affinities ( file_scan, routing_ctx. task_count ) ;
8089 if task_affinities. len ( ) != routing_ctx. task_count {
90+ tracing:: info!(
91+ strategy = "file_rendezvous" ,
92+ reason = "task_affinity_count_mismatch" ,
93+ task_count = routing_ctx. task_count,
94+ task_affinity_count = task_affinities. len( ) ,
95+ available_worker_count = routing_ctx. available_urls. len( ) ,
96+ "datafusion task routing fell back to default routing"
97+ ) ;
8198 return Ok ( None ) ;
8299 }
83- Ok ( Some ( route_by_rendezvous (
84- & task_affinities,
85- routing_ctx. available_urls ,
86- ) ) )
100+ let routes = route_by_rendezvous ( & task_affinities, routing_ctx. available_urls ) ;
101+ log_rendezvous_routes ( routing_ctx, & task_affinities, & routes) ;
102+ Ok ( Some ( routes) )
87103 }
88104}
89105
@@ -93,15 +109,6 @@ struct TaskAffinity {
93109 affinity_key : String ,
94110}
95111
96- fn single_file_scan_config ( plan : & Arc < dyn ExecutionPlan > ) -> Option < & FileScanConfig > {
97- let mut file_scans = Vec :: new ( ) ;
98- collect_file_scan_configs ( plan, & mut file_scans) ;
99- match file_scans. as_slice ( ) {
100- [ file_scan] => Some ( * file_scan) ,
101- _ => None ,
102- }
103- }
104-
105112fn collect_file_scan_configs < ' a > (
106113 plan : & ' a Arc < dyn ExecutionPlan > ,
107114 file_scans : & mut Vec < & ' a FileScanConfig > ,
@@ -211,6 +218,67 @@ fn rendezvous_affinity(url: &Url, key: &str) -> u64 {
211218 state. finish ( )
212219}
213220
221+ fn affinity_fingerprint ( key : & str ) -> u64 {
222+ let mut state = SipHasher :: new ( ) ;
223+ key. hash ( & mut state) ;
224+ state. finish ( )
225+ }
226+
227+ fn log_routing_fallback ( routing_ctx : & TaskRoutingContext < ' _ > , reason : & ' static str ) {
228+ tracing:: info!(
229+ strategy = "file_rendezvous" ,
230+ reason,
231+ task_count = routing_ctx. task_count,
232+ available_worker_count = routing_ctx. available_urls. len( ) ,
233+ "datafusion task routing fell back to default routing"
234+ ) ;
235+ }
236+
237+ fn log_rendezvous_routes (
238+ routing_ctx : & TaskRoutingContext < ' _ > ,
239+ task_affinities : & [ TaskAffinity ] ,
240+ routes : & [ Url ] ,
241+ ) {
242+ const ROUTE_SAMPLE_SIZE : usize = 16 ;
243+
244+ let mut route_distribution: Vec < ( & str , usize ) > = Vec :: new ( ) ;
245+ for route in routes {
246+ let route = route. as_str ( ) ;
247+ match route_distribution
248+ . iter_mut ( )
249+ . find ( |( worker_url, _) | * worker_url == route)
250+ {
251+ Some ( ( _, task_count) ) => * task_count += 1 ,
252+ None => route_distribution. push ( ( route, 1 ) ) ,
253+ }
254+ }
255+ route_distribution. sort_unstable_by ( |( left, _) , ( right, _) | left. cmp ( right) ) ;
256+
257+ let route_sample: Vec < ( usize , u64 , & str ) > = task_affinities
258+ . iter ( )
259+ . take ( ROUTE_SAMPLE_SIZE )
260+ . filter_map ( |task_affinity| {
261+ routes. get ( task_affinity. task_index ) . map ( |route| {
262+ (
263+ task_affinity. task_index ,
264+ affinity_fingerprint ( & task_affinity. affinity_key ) ,
265+ route. as_str ( ) ,
266+ )
267+ } )
268+ } )
269+ . collect ( ) ;
270+
271+ tracing:: info!(
272+ strategy = "file_rendezvous" ,
273+ task_count = routes. len( ) ,
274+ available_worker_count = routing_ctx. available_urls. len( ) ,
275+ route_sample_size = route_sample. len( ) ,
276+ route_distribution = ?route_distribution,
277+ route_sample = ?route_sample,
278+ "datafusion task routing selected workers"
279+ ) ;
280+ }
281+
214282#[ cfg( test) ]
215283mod tests {
216284 use super :: * ;
0 commit comments