@@ -62,6 +62,7 @@ use arrow::compute::{SortOptions, interleave};
6262use arrow:: datatypes:: { Schema , SchemaRef } ;
6363use datafusion_common:: config:: ConfigOptions ;
6464use datafusion_common:: stats:: Precision ;
65+ use datafusion_common:: utils:: memory:: RecordBatchMemoryCounter ;
6566use datafusion_common:: utils:: {
6667 compare_rows, get_row_at_idx, normalize_float_zero_scalar,
6768} ;
@@ -93,7 +94,6 @@ use crate::metrics::{
9394 BaselineMetrics , Count , ExecutionPlanMetricsSet , Gauge , MetricBuilder ,
9495 MetricCategory , MetricsSet , RecordOutput , Time ,
9596} ;
96- use crate :: spill:: get_record_batch_memory_size;
9797use crate :: statistics:: { ChildStats , StatisticsArgs } ;
9898use crate :: stream:: RecordBatchStreamAdapter ;
9999use crate :: {
@@ -609,9 +609,10 @@ async fn collect_right_input(
609609 metrics : AsOfJoinMetrics ,
610610) -> Result < BroadcastRightInput > {
611611 let schema = input. schema ( ) ;
612+ let mut memory_counter = RecordBatchMemoryCounter :: new ( ) ;
612613 let batches = input
613614 . try_fold ( Vec :: new ( ) , |mut batches, batch| {
614- let batch_size = get_record_batch_memory_size ( & batch) ;
615+ let batch_size = memory_counter . count_batch ( & batch) ;
615616 futures:: future:: ready ( reservation. try_grow ( batch_size) . map ( |_| {
616617 metrics. build_mem_used . add ( batch_size) ;
617618 metrics. build_input_batches . add ( 1 ) ;
@@ -1070,6 +1071,7 @@ mod tests {
10701071 } ;
10711072 use arrow:: datatypes:: { DataType , Field , Int8Type } ;
10721073 use datafusion_execution:: config:: SessionConfig ;
1074+ use datafusion_execution:: runtime_env:: RuntimeEnvBuilder ;
10731075 use datafusion_expr:: ColumnarValue ;
10741076 use datafusion_physical_expr_common:: metrics:: MetricValue ;
10751077 use datafusion_physical_expr_common:: physical_expr:: PhysicalExpr ;
@@ -1401,6 +1403,90 @@ mod tests {
14011403 Ok ( ( ) )
14021404 }
14031405
1406+ #[ tokio:: test]
1407+ async fn shared_right_buffers_are_reserved_once ( ) -> Result < ( ) > {
1408+ let left_schema = Arc :: new ( Schema :: new ( vec ! [
1409+ Field :: new( "key" , DataType :: Utf8 , false ) ,
1410+ Field :: new( "ts" , DataType :: Int64 , false ) ,
1411+ Field :: new( "id" , DataType :: Int32 , false ) ,
1412+ ] ) ) ;
1413+ let left = TestMemoryExec :: try_new_exec (
1414+ & [ vec ! [ make_batch(
1415+ & left_schema,
1416+ vec![ Some ( "A" ) ] ,
1417+ vec![ Some ( 4095 ) ] ,
1418+ vec![ 0 ] ,
1419+ ) ?] ] ,
1420+ Arc :: clone ( & left_schema) ,
1421+ None ,
1422+ ) ?;
1423+
1424+ let right_schema = Arc :: new ( Schema :: new ( vec ! [
1425+ Field :: new( "key" , DataType :: Utf8 , false ) ,
1426+ Field :: new( "ts" , DataType :: Int64 , false ) ,
1427+ Field :: new( "price" , DataType :: Int32 , false ) ,
1428+ ] ) ) ;
1429+ let row_count = 4096 ;
1430+ let parent = make_batch (
1431+ & right_schema,
1432+ vec ! [ Some ( "A" ) ; row_count] ,
1433+ ( 0 ..row_count) . map ( |value| Some ( value as i64 ) ) . collect ( ) ,
1434+ ( 0 ..row_count as i32 ) . collect ( ) ,
1435+ ) ?;
1436+ let mut memory_counter = RecordBatchMemoryCounter :: new ( ) ;
1437+ let retained_size = memory_counter. count_batch ( & parent) ;
1438+ let right_batches = ( 0 ..16 )
1439+ . map ( |index| parent. slice ( index * 256 , 256 ) )
1440+ . collect ( ) ;
1441+ let right = TestMemoryExec :: try_new_exec (
1442+ & [ right_batches] ,
1443+ Arc :: clone ( & right_schema) ,
1444+ None ,
1445+ ) ?;
1446+
1447+ let exec = Arc :: new ( AsOfJoinExec :: try_new (
1448+ left,
1449+ right,
1450+ vec ! [ (
1451+ Arc :: new( PhysicalColumn :: new( "key" , 0 ) ) ,
1452+ Arc :: new( PhysicalColumn :: new( "key" , 0 ) ) ,
1453+ ) ] ,
1454+ AsOfMatchExpr :: new (
1455+ Arc :: new ( PhysicalColumn :: new ( "ts" , 1 ) ) ,
1456+ Operator :: GtEq ,
1457+ Arc :: new ( PhysicalColumn :: new ( "ts" , 1 ) ) ,
1458+ ) ,
1459+ vec ! [ 2 ] ,
1460+ ) ?) ;
1461+ let runtime = RuntimeEnvBuilder :: new ( )
1462+ . with_memory_limit ( retained_size, 1.0 )
1463+ . build_arc ( ) ?;
1464+ let context = Arc :: new ( TaskContext :: default ( ) . with_runtime ( runtime) ) ;
1465+
1466+ let batches = collect ( Arc :: clone ( & exec) as _ , context) . await ?;
1467+ let prices = batches
1468+ . iter ( )
1469+ . flat_map ( |batch| {
1470+ batch
1471+ . column ( 3 )
1472+ . as_any ( )
1473+ . downcast_ref :: < Int32Array > ( )
1474+ . unwrap ( )
1475+ . iter ( )
1476+ } )
1477+ . collect :: < Vec < _ > > ( ) ;
1478+ assert_eq ! ( prices, vec![ Some ( 4095 ) ] ) ;
1479+
1480+ let metrics = exec. metrics ( ) . expect ( "ASOF metrics must be present" ) ;
1481+ assert_eq ! (
1482+ metrics
1483+ . sum_by_name( "build_mem_used" )
1484+ . map( |value| value. as_usize( ) ) ,
1485+ Some ( retained_size)
1486+ ) ;
1487+ Ok ( ( ) )
1488+ }
1489+
14041490 #[ tokio:: test]
14051491 async fn preserves_dictionary_outputs_across_large_flush ( ) -> Result < ( ) > {
14061492 let dictionary_type =
0 commit comments