@@ -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 :: {
@@ -752,9 +752,10 @@ async fn collect_right_input(
752752 metrics : AsOfJoinMetrics ,
753753) -> Result < BroadcastRightInput > {
754754 let schema = input. schema ( ) ;
755+ let mut memory_counter = RecordBatchMemoryCounter :: new ( ) ;
755756 let batches = input
756757 . try_fold ( Vec :: new ( ) , |mut batches, batch| {
757- let batch_size = get_record_batch_memory_size ( & batch) ;
758+ let batch_size = memory_counter . count_batch ( & batch) ;
758759 futures:: future:: ready ( reservation. try_grow ( batch_size) . map ( |_| {
759760 metrics. build_mem_used . add ( batch_size) ;
760761 metrics. build_input_batches . add ( 1 ) ;
@@ -1213,6 +1214,7 @@ mod tests {
12131214 } ;
12141215 use arrow:: datatypes:: { DataType , Field , Int8Type } ;
12151216 use datafusion_execution:: config:: SessionConfig ;
1217+ use datafusion_execution:: runtime_env:: RuntimeEnvBuilder ;
12161218 use datafusion_expr:: ColumnarValue ;
12171219 use datafusion_physical_expr_common:: metrics:: MetricValue ;
12181220 use datafusion_physical_expr_common:: physical_expr:: PhysicalExpr ;
@@ -1544,6 +1546,90 @@ mod tests {
15441546 Ok ( ( ) )
15451547 }
15461548
1549+ #[ tokio:: test]
1550+ async fn shared_right_buffers_are_reserved_once ( ) -> Result < ( ) > {
1551+ let left_schema = Arc :: new ( Schema :: new ( vec ! [
1552+ Field :: new( "key" , DataType :: Utf8 , false ) ,
1553+ Field :: new( "ts" , DataType :: Int64 , false ) ,
1554+ Field :: new( "id" , DataType :: Int32 , false ) ,
1555+ ] ) ) ;
1556+ let left = TestMemoryExec :: try_new_exec (
1557+ & [ vec ! [ make_batch(
1558+ & left_schema,
1559+ vec![ Some ( "A" ) ] ,
1560+ vec![ Some ( 4095 ) ] ,
1561+ vec![ 0 ] ,
1562+ ) ?] ] ,
1563+ Arc :: clone ( & left_schema) ,
1564+ None ,
1565+ ) ?;
1566+
1567+ let right_schema = Arc :: new ( Schema :: new ( vec ! [
1568+ Field :: new( "key" , DataType :: Utf8 , false ) ,
1569+ Field :: new( "ts" , DataType :: Int64 , false ) ,
1570+ Field :: new( "price" , DataType :: Int32 , false ) ,
1571+ ] ) ) ;
1572+ let row_count = 4096 ;
1573+ let parent = make_batch (
1574+ & right_schema,
1575+ vec ! [ Some ( "A" ) ; row_count] ,
1576+ ( 0 ..row_count) . map ( |value| Some ( value as i64 ) ) . collect ( ) ,
1577+ ( 0 ..row_count as i32 ) . collect ( ) ,
1578+ ) ?;
1579+ let mut memory_counter = RecordBatchMemoryCounter :: new ( ) ;
1580+ let retained_size = memory_counter. count_batch ( & parent) ;
1581+ let right_batches = ( 0 ..16 )
1582+ . map ( |index| parent. slice ( index * 256 , 256 ) )
1583+ . collect ( ) ;
1584+ let right = TestMemoryExec :: try_new_exec (
1585+ & [ right_batches] ,
1586+ Arc :: clone ( & right_schema) ,
1587+ None ,
1588+ ) ?;
1589+
1590+ let exec = Arc :: new ( AsOfJoinExec :: try_new (
1591+ left,
1592+ right,
1593+ vec ! [ (
1594+ Arc :: new( PhysicalColumn :: new( "key" , 0 ) ) ,
1595+ Arc :: new( PhysicalColumn :: new( "key" , 0 ) ) ,
1596+ ) ] ,
1597+ AsOfMatchExpr :: new (
1598+ Arc :: new ( PhysicalColumn :: new ( "ts" , 1 ) ) ,
1599+ Operator :: GtEq ,
1600+ Arc :: new ( PhysicalColumn :: new ( "ts" , 1 ) ) ,
1601+ ) ,
1602+ vec ! [ 2 ] ,
1603+ ) ?) ;
1604+ let runtime = RuntimeEnvBuilder :: new ( )
1605+ . with_memory_limit ( retained_size, 1.0 )
1606+ . build_arc ( ) ?;
1607+ let context = Arc :: new ( TaskContext :: default ( ) . with_runtime ( runtime) ) ;
1608+
1609+ let batches = collect ( Arc :: clone ( & exec) as _ , context) . await ?;
1610+ let prices = batches
1611+ . iter ( )
1612+ . flat_map ( |batch| {
1613+ batch
1614+ . column ( 3 )
1615+ . as_any ( )
1616+ . downcast_ref :: < Int32Array > ( )
1617+ . unwrap ( )
1618+ . iter ( )
1619+ } )
1620+ . collect :: < Vec < _ > > ( ) ;
1621+ assert_eq ! ( prices, vec![ Some ( 4095 ) ] ) ;
1622+
1623+ let metrics = exec. metrics ( ) . expect ( "ASOF metrics must be present" ) ;
1624+ assert_eq ! (
1625+ metrics
1626+ . sum_by_name( "build_mem_used" )
1627+ . map( |value| value. as_usize( ) ) ,
1628+ Some ( retained_size)
1629+ ) ;
1630+ Ok ( ( ) )
1631+ }
1632+
15471633 #[ tokio:: test]
15481634 async fn preserves_dictionary_outputs_across_large_flush ( ) -> Result < ( ) > {
15491635 let dictionary_type =
0 commit comments