viirya commented on code in PR #25491:
URL: https://github.com/apache/datafusion/pull/25491#discussion_r4090230160


##########
datafusion/physical-plan/src/joins/hash_join/exec.rs:
##########
@@ -3104,18 +3211,28 @@ async fn collect_left_input(
         && !left_values.is_empty()
         && left_values[0].logical_null_count() > 0;
 
+    if prepared {
+        drop(batches);
+        let retained = RecordBatchMemoryCounter::new().count_batch(&batch);
+        let allowance = input_bytes + copy_bytes;
+        debug_assert!(retained <= allowance);

Review Comment:
   Confirmed on `cab63e4`, thanks. The check happens before `shrink`, the error 
path leaves the reservation untouched for the drop in `collect_left_input` to 
release, and `prepared_concat_rejects_under_admission_without_shrinking` pins 
that behaviour. Resolving from my side.



##########
datafusion/physical-plan/src/joins/hash_join/exec/prepared.rs:
##########
@@ -0,0 +1,367 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Explicit immutable build reuse for embedding executors.
+
+use super::*;
+use crate::spill::spill_manager::GetSlicedSize;
+use datafusion_common::exec_datafusion_err;
+use datafusion_execution::memory_pool::MemoryPool;
+
+/// An immutable, fully prepared broadcast build, independent of any probe 
task.
+///
+/// Created by [`HashJoinExec::prepare_build`]. The embedding executor owns 
cache
+/// identity, admission, single-flight coordination, cancellation and eviction.
+/// This object retains its input buffers and memory reservation until its last
+/// lease is dropped; it never retains an input stream or task context. 
Prepared
+/// builds support fixed-width and UTF-8 build columns, with direct-column keys
+/// and non-spilling INNER joins. Residual conditions belong to each consuming
+/// join; null-aware joins remain unsupported.
+///
+/// Hash-join gathers copy supported build columns into output buffers,
+/// including contiguous selections. Output batches can therefore outlive this
+/// object without retaining unaccounted cached payload. View, dictionary and
+/// nested build columns remain unsupported. UTF-8 and fixed-size binary keys
+/// use hash-table membership filters instead of copying range or IN-list 
values.
+pub struct PreparedHashJoinBuild {
+    build: Arc<JoinBuildData>,
+    keys: Vec<usize>,
+    null_equality: NullEquality,
+}
+
+impl fmt::Debug for PreparedHashJoinBuild {
+    /// Describe immutable metadata without dumping table contents.
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("PreparedHashJoinBuild")
+            .field("schema", &self.build.batch.schema())
+            .field("keys", &self.keys)
+            .field("rows", &self.num_rows())
+            .field("reserved_bytes", &self.reserved_bytes())
+            .finish()
+    }
+}
+
+impl PreparedHashJoinBuild {
+    /// Return the retained build reservation, excluding all per-probe state.
+    pub fn reserved_bytes(&self) -> usize {
+        self.build.reservation.size()
+    }
+
+    /// Return the complete build row count, including duplicate and null keys.
+    pub fn num_rows(&self) -> usize {
+        self.build.batch.num_rows()
+    }
+
+    /// Create independent mutable state for one consuming join.
+    pub(super) fn probe_data(&self, probe_threads: usize) -> JoinLeftData {
+        JoinLeftData {
+            build: Arc::clone(&self.build),
+            null_aware_mark_scope_map: None,

Review Comment:
   Heads-up for the rebase: this file doesn't exist on `main`, so `git 
merge-tree` merges it without a textual conflict. But these two initializers 
still use the pre-#25339 field names (`null_aware_mark_scope_map`, 
`null_value_scope_map`). On current `main` they are `null_aware_scope_map` and 
`null_value_build_rows: Option<NullValueBuildRows>`. The compiler will catch 
it. I'm mentioning it only because it won't show up in the conflict list.



##########
datafusion/physical-plan/src/joins/hash_join/exec.rs:
##########
@@ -2944,7 +3037,21 @@ async fn collect_left_input(
             // Arc is used instead of Box to allow sharing with 
SharedBuildAccumulator for hash map pushdown
             let mut hashmap = new_join_hashmap(num_rows, &mut reservation, 
&metrics)?;
 
-            let mut hashes_buffer = Vec::new();
+            let scratch_reservation = reservation.new_empty();
+            if prepared {
+                // Allow one logical null mask per key plus the combined mask.
+                let masks = if null_equality == 
NullEquality::NullEqualsNothing {
+                    on_left.len() + 1
+                } else {
+                    0
+                };
+                scratch_reservation.try_grow(
+                    max_batch_rows * size_of::<u64>()
+                        + (max_batch_rows.div_ceil(8) + 64) * masks,

Review Comment:
   Thanks. The checked arithmetic is back, and the simpler mask formula stayed. 
For completeness: the unchecked `num_rows * size_of::<u64>()` that #25508 added 
to `new_join_hashmap` is fine, because `estimate_memory_size` has already 
checked `num_rows * 8` when that line runs. Resolving.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to