Support result buffers backed by caller-provided shared memory for zero-copy cross-process consumption
維護者通常 1 天內回覆
還沒有人認領這個 Issue。
評估
- 難度
- 5/5
- 預估耗時
- 一週以上
- 新手友好度
- 12/100
- Issue 類型
- 功能
- 描述清晰度
- 基本清楚
- 活躍度
- 活躍
- 技術堆疊
- rust
- 領域
- databases, performance
研究方向
Start with the proposed SessionConfig::with_buffer_allocator hook and trace how RuntimeEnv/TaskContext reach buffer creation sites, then inspect where arrow-rs MutableBuffer allocations occur. Read the Arrow C Data Interface ownership and release-callback rules before designing the descriptor export, since offsets rather than pointers are central. Done means an opt-in allocator travels with tasks and a batch can be published as region offsets that a consumer can reconstruct, with a fallback flag for non-region-backed buffers.
由索引模型根據 Issue 內容生成。
描述
Is your feature request related to a problem or challenge?
DataFusion is frequently embedded as an in-process engine that serves data to other processes: the engine runs in a dedicated "database" process, while consumers read results over the Arrow C Data Interface (directly, or through an ADBC driver or a custom FFI layer).
That path cannot be zero-copy today, and the failure mode is hard to diagnose.
The C Data Interface is pointer-based. ArrowSchema/ArrowArray handed to a consumer contain absolute addresses (format, children, buffers, …) and a release callback. DataFusion produces those buffers on its own heap, so:
- a consumer in another process cannot dereference buffers[i], children[i], format — the addresses are not mapped there, which results in an access violation or, worse, in silently dangling pointers that only fail when the first value is read;
- the release callback is a function pointer into the producer's code, so the consumer must not call it either.
As a result, the only correct way to move a result set across a process boundary is to copy every buffer, or to serialize the data (Arrow IPC). Both cost a full pass over the data, which is exactly what users of an embedded engine are trying to avoid.
DataFusion currently offers no way to influence where result buffers live:
- MemoryPool performs accounting and spilling only; it does not select an address space.
- RuntimeEnv/SessionConfig have no allocator hook.
The allocation itself happens in arrow-rs MutableBuffer, which goes through the process-global allocator and cannot be redirected afterwards.
So "run DataFusion in one process and read its results in another without copying" is not expressible with the current API, even though DataFusion's own execution model (partitioned, streaming, RecordBatch-based) would fit such a deployment well.
Describe the solution you'd like
An opt-in way to have a query's output buffers allocated in a caller-provided memory region, plus a descriptor that lets a consumer reconstruct the batches in its own address space.
Sketch of the API surface:
// Region is created by the application: named shared memory / memory-mapped file.
let region = SharedRegion::create("df_results", 1 << 30)?;
let config = SessionConfig::new()
.with_buffer_allocator(region.allocator()); // new
let df = ctx.sql("SELECT ... FROM parquet_scan(...)").await?;
let mut stream = df.execute_stream().await?;
while let Some(batch) = stream.next().await {
// Batch buffers live inside `region`; publish() returns only offsets/lengths,
// so the consumer never needs producer addresses.
let descriptor = region.publish(batch?)?;
send_descriptor_to_consumer(descriptor)?;
}
Two properties matter:
Allocator threading. A BufferAllocator handle carried by TaskContext/RuntimeEnv and used at buffer creation sites, defaulting to the current global allocator so behavior is unchanged unless opted in. Because operators run on multiple threads and also spawn work (repartition, joins, parquet prefetch), a thread-local set at the call site is not sufficient — the allocator has to travel with the task.
Descriptor-based export. The consumer should receive (region handle, buffer offsets, lengths, offsets/validity layout) rather than absolute pointers, then build its own ArrowArray with buffers[i] = local_base + offset. This avoids the requirement that the region be mapped at the same base address in both processes, which is fragile in practice (address-space collisions, ASLR, needing VirtualAlloc(MEM_RESERVE) / mmap(MAP_FIXED_NOREPLACE)).
With that, the data is written once into shared memory, the consumer maps the region and reads zero-copy, and release stays a local no-op on the consumer side.
Describe alternatives you've considered
- Deep copy on the consumer side. Walk the schema/array graph and copy every buffer into the consumer's memory (e.g. ReadProcessMemory on Windows, process_vm_readv on Linux). Works, but it is a full memcpy of the result set — the same cost as IPC, without the portability.
- Arrow IPC. Standard and supported, but serializes the data and adds framing/deserialization overhead.
- A custom driver/FFI layer that copies each batch once into a memory-mapped region. This is workable today and is essentially the feature requested here, just implemented outside DataFusion — it keeps DataFusion untouched but duplicates the logic in every embedder.
- Replacing the global allocator in the host process. A custom #[global_allocator] that routes allocations with Arrow's buffer alignment (ALIGNMENT = 128 on x86_64) into a shared arena. It affects the whole process (tokio, parsers, catalog), misses buffers created from Vec/bytes::Bytes, and depends on an implementation detail of arrow-rs rather than on a contract.
- Patching arrow-rs. Adding a per-buffer allocator to arrow-buffer and threading it through the execution context. Correct in principle, but it means maintaining a fork of arrow-rs and DataFusion and pinning versions.
- ADBC partitions / ConnectionReadPartition. Designed to be passable across processes, but the partition payload is serialized, so it is again a copy.
Additional context
- The allocation site is arrow-buffer's MutableBuffer → std::alloc with Layout { align: 128 }. A hook in DataFusion can only be effective if it reaches those allocation sites; that is why the "allocator travels with the task" part is the essential piece, not just a config flag.
- A partial solution is still useful: even if some buffers (e.g. those built from Vec by the parquet reader) end up outside the region, exposing the mechanism plus a per-batch "is this batch fully region-backed?" flag would let callers fall back to a copy only when necessary.
- Precedent for the descriptor approach: the Arrow C Device Data Interface separates the device representation from the arrays themselves (ArrowDeviceArray + ArrowMemoryManager). A host shared-memory device type there would be the natural long-term home for this, but DataFusion-side support for placing batches in a region is a prerequisite.
- This is an API/design request, not a crash report; the current behavior is expected given a private heap, but it is undocumented and easy to hit when embedding DataFusion as a separate service process.
Willingness to contribute
None
- 主要語言
- Rust
- 星號
- 9.6k
- 分支
- 2.6k
- 平均合併
- 3 天 4 小時
- 30 天內合併 PR
- 345
環境準備
在瀏覽器裡用你自己的 GitHub 帳號啟動這個專案的開發容器。
- 沒有 Dockerfile 或 Docker Compose 檔案
- 有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
apache/datafusion 的其他 Issue
-
難度 2/5 1-3 小時 新手友好度 72/100
apache/datafusion#27315 · 2 則留言 ·
維護者通常 1 天內回覆
-
bug
難度 2/5 1-3 小時 新手友好度 76/100
apache/datafusion#27094 · 1 則留言 ·
維護者通常 1 天內回覆
-
Substrait consumer doesn't recognise `shift_left` / `shift_right` from functions_arithmetic.yaml可能已有人在做 @jonasHanhan 於 9 天前認領。 未關閉bug
難度 2/5 1-3 小時 新手友好度 76/100
apache/datafusion#27059 ·
維護者通常 1 天內回覆
-
`ProjectionExprs::project_statistics` computes approximate `Statistics::total_byte_size` ignoring column stats可能已有人在做 @joseph-isaacs 於 10 天前認領。 未關閉bug
難度 2/5 1-3 小時 新手友好度 72/100
apache/datafusion#27050 ·
維護者通常 1 天內回覆
-
physical-plan: decoding an Unnest or Explain node with an unset field panics可能已有人在做 @geoHeil 於 8 天前認領。 未關閉
難度 2/5 1-3 小時 新手友好度 75/100
apache/datafusion#26999 · 1 則留言 ·
維護者通常 1 天內回覆
查看 apache/datafusion 的全部 Issue
相似的 Issue
-
P3
難度 2/5 1-3 小時 新手友好度 70/100
HerringtonDarkholme/hd-lang#1770 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 82/100
jonathanong/no-mistakes#3332 ·
維護者通常 1 天內回覆
-
bug
難度 2/5 1-3 小時 新手友好度 78/100
swc-project/swc-next-issues#310 ·
-
bug
難度 2/5 1-3 小時 新手友好度 72/100
swc-project/swc-ai-issues#653 ·
-
難度 2/5 1-3 小時 新手友好度 72/100
lbjlaq/Antigravity-Manager#3937 ·
維護者通常 1 天內回覆