blob: 26e0f0dea0d04e579bdf3ad92bef83c93ca61d70 [file]
<!DOCTYPE html>
<html lang="en" data-content_root="../" data-theme="auto">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Distributing work &#8212; Apache DataFusion in Python documentation</title>
<script data-cfasync="false">
document.documentElement.dataset.mode = localStorage.getItem("mode") || "auto";
document.documentElement.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<!--
this give us a css class that will be invisible only if js is disabled
-->
<noscript>
<style>
.pst-js-only { display: none !important; }
</style>
</noscript>
<!-- Loaded before other Sphinx assets -->
<link href="../_static/styles/theme.css?digest=8878045cc6db502f8baf" rel="stylesheet" />
<link href="../_static/styles/pydata-sphinx-theme.css?digest=8878045cc6db502f8baf" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=8f2a1f02" />
<link rel="stylesheet" type="text/css" href="../_static/mystnb.11b39860a7a0cbfd473a3ad8a317855267ff0bd372690045ca344a6b62be495e.css" />
<link rel="stylesheet" type="text/css" href="../_static/graphviz.css?v=4ae1632d" />
<link rel="stylesheet" type="text/css" href="../_static/theme_overrides.css?v=4af573bc" />
<!-- So that users can add custom icons -->
<script src="../_static/scripts/fontawesome.js?digest=8878045cc6db502f8baf"></script>
<!-- Pre-loaded scripts that we'll load fully later -->
<link rel="preload" as="script" href="../_static/scripts/bootstrap.js?digest=8878045cc6db502f8baf" />
<link rel="preload" as="script" href="../_static/scripts/pydata-sphinx-theme.js?digest=8878045cc6db502f8baf" />
<script src="../_static/documentation_options.js?v=5929fcd5"></script>
<script src="../_static/doctools.js?v=9bcbadda"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
<script>DOCUMENTATION_OPTIONS.pagename = 'user-guide/distributing-work';</script>
<script src="../_static/toc-toggle.js?v=36375f01"></script>
<link rel="icon" href="../_static/favicon.svg"/>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="SQL" href="sql.html" />
<link rel="prev" title="Configuration" href="configuration.html" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="docsearch:language" content="en"/>
<meta name="docsearch:version" content="" />
</head>
<body data-bs-spy="scroll" data-bs-target=".bd-toc-nav" data-offset="180" data-bs-root-margin="0px 0px -60%" data-default-mode="auto">
<div id="pst-skip-link" class="skip-link d-print-none"><a href="#main-content">Skip to main content</a></div>
<div id="pst-scroll-pixel-helper"></div>
<button type="button" class="btn rounded-pill" id="pst-back-to-top">
<i class="fa-solid fa-arrow-up"></i>Back to top</button>
<dialog id="pst-search-dialog">
<form class="bd-search d-flex align-items-center"
action="../search.html"
method="get">
<i class="fa-solid fa-magnifying-glass"></i>
<input type="search"
class="form-control"
name="q"
placeholder="Search the docs ..."
aria-label="Search the docs ..."
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"/>
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd>K</kbd></span>
</form>
</dialog>
<div class="pst-async-banner-revealer d-none">
<aside id="bd-header-version-warning" class="d-none d-print-none" aria-label="Version warning"></aside>
</div>
<header class="bd-header navbar navbar-expand-lg bd-navbar d-print-none">
<div class="bd-header__inner bd-page-width">
<button class="pst-navbar-icon sidebar-toggle primary-toggle" aria-label="Site navigation">
<span class="fa-solid fa-bars"></span>
</button>
<div class="col-lg-3 navbar-header-items__start">
<div class="navbar-item">
<a class="navbar-brand logo" href="../index.html">
<img src="../_static/original.svg" class="logo__image only-light" alt="Apache DataFusion in Python"/>
<img src="../_static/original_dark.svg" class="logo__image only-dark pst-js-only" alt="Apache DataFusion in Python"/>
</a></div>
</div>
<div class="col-lg-9 navbar-header-items">
<div class="me-auto navbar-header-items__center">
<div class="navbar-item">
<nav>
<ul class="bd-navbar-elements navbar-nav">
<li class="nav-item current active">
<a class="nav-link nav-internal" href="index.html">
User Guide
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-internal" href="../contributor-guide/index.html">
Contributor Guide
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-internal" href="../autoapi/index.html">
API Reference
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-internal" href="../links.html">
Links
</a>
</li>
</ul>
</nav></div>
</div>
<div class="navbar-header-items__end">
<div class="navbar-item navbar-persistent--container">
<button class="btn search-button-field search-button__button pst-js-only" title="Search" aria-label="Search" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="fa-solid fa-magnifying-glass"></i>
<span class="search-button__default-text">Search</span>
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd class="kbd-shortcut__modifier">K</kbd></span>
</button>
</div>
<div class="navbar-item"><ul class="navbar-icon-links"
aria-label="Icon Links">
<li class="nav-item">
<a href="https://github.com/apache/datafusion-python" title="GitHub" class="nav-link pst-navbar-icon" rel="noopener" target="_blank" data-bs-toggle="tooltip" data-bs-placement="bottom"><i class="fa-brands fa-github fa-lg" aria-hidden="true"></i>
<span class="sr-only">GitHub</span></a>
</li>
<li class="nav-item">
<a href="https://docs.rs/datafusion/latest/datafusion/" title="Rust API docs (docs.rs)" class="nav-link pst-navbar-icon" rel="noopener" target="_blank" data-bs-toggle="tooltip" data-bs-placement="bottom"><i class="fa-brands fa-rust fa-lg" aria-hidden="true"></i>
<span class="sr-only">Rust API docs (docs.rs)</span></a>
</li>
</ul></div>
<div class="navbar-item">
<button class="btn btn-sm nav-link pst-navbar-icon theme-switch-button pst-js-only" aria-label="Color mode" data-bs-title="Color mode" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="theme-switch fa-solid fa-sun fa-lg" data-mode="light" title="Light"></i>
<i class="theme-switch fa-solid fa-moon fa-lg" data-mode="dark" title="Dark"></i>
<i class="theme-switch fa-solid fa-circle-half-stroke fa-lg" data-mode="auto" title="System Settings"></i>
</button></div>
</div>
</div>
<div class="navbar-persistent--mobile">
<button class="btn search-button-field search-button__button pst-js-only" title="Search" aria-label="Search" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="fa-solid fa-magnifying-glass"></i>
<span class="search-button__default-text">Search</span>
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd class="kbd-shortcut__modifier">K</kbd></span>
</button>
</div>
<button class="pst-navbar-icon sidebar-toggle secondary-toggle" aria-label="On this page">
<span class="fa-solid fa-outdent"></span>
</button>
</div>
</header>
<div class="bd-container">
<div class="bd-container__inner bd-page-width">
<dialog id="pst-primary-sidebar-modal"></dialog>
<div id="pst-primary-sidebar" class="bd-sidebar-primary bd-sidebar">
<div class="sidebar-header-items sidebar-primary__section">
<div class="sidebar-header-items__center">
<div class="navbar-item">
<nav>
<ul class="bd-navbar-elements navbar-nav">
<li class="nav-item current active">
<a class="nav-link nav-internal" href="index.html">
User Guide
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-internal" href="../contributor-guide/index.html">
Contributor Guide
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-internal" href="../autoapi/index.html">
API Reference
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-internal" href="../links.html">
Links
</a>
</li>
</ul>
</nav></div>
</div>
<div class="sidebar-header-items__end">
<div class="navbar-item"><ul class="navbar-icon-links"
aria-label="Icon Links">
<li class="nav-item">
<a href="https://github.com/apache/datafusion-python" title="GitHub" class="nav-link pst-navbar-icon" rel="noopener" target="_blank" data-bs-toggle="tooltip" data-bs-placement="bottom"><i class="fa-brands fa-github fa-lg" aria-hidden="true"></i>
<span class="sr-only">GitHub</span></a>
</li>
<li class="nav-item">
<a href="https://docs.rs/datafusion/latest/datafusion/" title="Rust API docs (docs.rs)" class="nav-link pst-navbar-icon" rel="noopener" target="_blank" data-bs-toggle="tooltip" data-bs-placement="bottom"><i class="fa-brands fa-rust fa-lg" aria-hidden="true"></i>
<span class="sr-only">Rust API docs (docs.rs)</span></a>
</li>
</ul></div>
<div class="navbar-item">
<button class="btn btn-sm nav-link pst-navbar-icon theme-switch-button pst-js-only" aria-label="Color mode" data-bs-title="Color mode" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="theme-switch fa-solid fa-sun fa-lg" data-mode="light" title="Light"></i>
<i class="theme-switch fa-solid fa-moon fa-lg" data-mode="dark" title="Dark"></i>
<i class="theme-switch fa-solid fa-circle-half-stroke fa-lg" data-mode="auto" title="System Settings"></i>
</button></div>
</div>
</div>
<div class="sidebar-primary-items__start sidebar-primary__section">
<div class="sidebar-primary-item">
<nav class="bd-docs-nav bd-links" aria-label="Section Navigation">
<p class="bd-links__title" role="heading" aria-level="1">Section Navigation</p>
<div class="bd-toc-item navbar-nav">
<ul class="current nav bd-sidenav">
<li class="toctree-l1 current active has-children"><a class="reference internal" href="index.html">User Guide</a><details open="open"><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="introduction.html">Introduction</a></li>
<li class="toctree-l2"><a class="reference internal" href="basics.html">Concepts</a></li>
<li class="toctree-l2"><a class="reference internal" href="data-sources.html">Data Sources</a></li>
<li class="toctree-l2 has-children"><a class="reference internal" href="dataframe/index.html">DataFrames</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
<li class="toctree-l3"><a class="reference internal" href="dataframe/rendering.html">DataFrame Rendering</a></li>
<li class="toctree-l3"><a class="reference internal" href="dataframe/execution-metrics.html">Execution Metrics</a></li>
</ul>
</details></li>
<li class="toctree-l2 has-children"><a class="reference internal" href="common-operations/index.html">Common Operations</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
<li class="toctree-l3"><a class="reference internal" href="common-operations/views.html">Registering Views</a></li>
<li class="toctree-l3"><a class="reference internal" href="common-operations/basic-info.html">Basic Operations</a></li>
<li class="toctree-l3"><a class="reference internal" href="common-operations/select-and-filter.html">Column Selections</a></li>
<li class="toctree-l3"><a class="reference internal" href="common-operations/expressions.html">Expressions</a></li>
<li class="toctree-l3"><a class="reference internal" href="common-operations/joins.html">Joins</a></li>
<li class="toctree-l3"><a class="reference internal" href="common-operations/functions.html">Functions</a></li>
<li class="toctree-l3"><a class="reference internal" href="common-operations/spark-functions.html">Spark-Compatible Functions</a></li>
<li class="toctree-l3"><a class="reference internal" href="common-operations/aggregations.html">Aggregation</a></li>
<li class="toctree-l3"><a class="reference internal" href="common-operations/windows.html">Window Functions</a></li>
<li class="toctree-l3"><a class="reference internal" href="common-operations/udf-and-udfa.html">User-Defined Functions</a></li>
</ul>
</details></li>
<li class="toctree-l2 has-children"><a class="reference internal" href="io/index.html">IO</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
<li class="toctree-l3"><a class="reference internal" href="io/arrow.html">Arrow</a></li>
<li class="toctree-l3"><a class="reference internal" href="io/avro.html">Avro</a></li>
<li class="toctree-l3"><a class="reference internal" href="io/csv.html">CSV</a></li>
<li class="toctree-l3"><a class="reference internal" href="io/json.html">JSON</a></li>
<li class="toctree-l3"><a class="reference internal" href="io/parquet.html">Parquet</a></li>
<li class="toctree-l3"><a class="reference internal" href="io/table_provider.html">Custom Table Provider</a></li>
</ul>
</details></li>
<li class="toctree-l2"><a class="reference internal" href="configuration.html">Configuration</a></li>
<li class="toctree-l2 current active"><a class="current reference internal" href="#">Distributing work</a></li>
<li class="toctree-l2"><a class="reference internal" href="sql.html">SQL</a></li>
<li class="toctree-l2"><a class="reference internal" href="upgrade-guides.html">Upgrade Guides</a></li>
<li class="toctree-l2"><a class="reference internal" href="ai-coding-assistants.html">Using AI Coding Assistants</a></li>
</ul>
</details></li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../contributor-guide/index.html">Contributor Guide</a><details open="open"><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
<li class="toctree-l2"><a class="reference internal" href="../contributor-guide/introduction.html">Introduction</a></li>
<li class="toctree-l2"><a class="reference internal" href="../contributor-guide/ffi.html">Python Extensions</a></li>
</ul>
</details></li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../autoapi/index.html">API Reference</a><details open="open"><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
<li class="toctree-l2 has-children"><a class="reference internal" href="../autoapi/datafusion/index.html">datafusion</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/catalog/index.html">datafusion.catalog</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/context/index.html">datafusion.context</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/dataframe/index.html">datafusion.dataframe</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/dataframe_formatter/index.html">datafusion.dataframe_formatter</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/expr/index.html">datafusion.expr</a></li>
<li class="toctree-l3 has-children"><a class="reference internal" href="../autoapi/datafusion/functions/index.html">datafusion.functions</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
<li class="toctree-l4"><a class="reference internal" href="../autoapi/datafusion/functions/spark/index.html">datafusion.functions.spark</a></li>
</ul>
</details></li>
<li class="toctree-l3 has-children"><a class="reference internal" href="../autoapi/datafusion/input/index.html">datafusion.input</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
<li class="toctree-l4"><a class="reference internal" href="../autoapi/datafusion/input/base/index.html">datafusion.input.base</a></li>
<li class="toctree-l4"><a class="reference internal" href="../autoapi/datafusion/input/location/index.html">datafusion.input.location</a></li>
</ul>
</details></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/io/index.html">datafusion.io</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/ipc/index.html">datafusion.ipc</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/object_store/index.html">datafusion.object_store</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/options/index.html">datafusion.options</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/plan/index.html">datafusion.plan</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/record_batch/index.html">datafusion.record_batch</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/substrait/index.html">datafusion.substrait</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/unparser/index.html">datafusion.unparser</a></li>
<li class="toctree-l3"><a class="reference internal" href="../autoapi/datafusion/user_defined/index.html">datafusion.user_defined</a></li>
</ul>
</details></li>
</ul>
</details></li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../links.html">Links</a><details open="open"><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
<li class="toctree-l2"><a class="reference external" href="https://github.com/apache/datafusion-python">GitHub and Issue Tracker</a></li>
<li class="toctree-l2"><a class="reference external" href="https://docs.rs/datafusion/latest/datafusion/">Rust API Docs</a></li>
<li class="toctree-l2"><a class="reference external" href="https://github.com/apache/datafusion/blob/main/CODE_OF_CONDUCT.md">Code of Conduct</a></li>
<li class="toctree-l2"><a class="reference external" href="https://github.com/apache/datafusion-python/tree/main/examples">Examples</a></li>
</ul>
</details></li>
</ul>
</div>
</nav></div>
</div>
<div class="sidebar-primary-items__end sidebar-primary__section">
<div class="sidebar-primary-item">
<div id="ethical-ad-placement"
class="flat"
data-ea-publisher="readthedocs"
data-ea-type="readthedocs-sidebar"
data-ea-manual="true">
</div></div>
</div>
</div>
<main id="main-content" class="bd-main" role="main">
<div class="bd-content">
<div class="bd-article-container">
<div class="bd-header-article d-print-none">
<div class="header-article-items header-article__inner">
<div class="header-article-items__start">
<div class="header-article-item">
<nav aria-label="Breadcrumb" class="d-print-none">
<ul class="bd-breadcrumbs">
<li class="breadcrumb-item breadcrumb-home">
<a href="../index.html" class="nav-link" aria-label="Home">
<i class="fa-solid fa-home"></i>
</a>
</li>
<li class="breadcrumb-item"><a href="index.html" class="nav-link">User Guide</a></li>
<li class="breadcrumb-item active" aria-current="page"><span class="ellipsis">Distributing work</span></li>
</ul>
</nav>
</div>
</div>
</div>
</div>
<div id="searchbox"></div>
<article class="bd-article">
<!---
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.
-->
<section id="distributing-work">
<h1>Distributing work<a class="headerlink" href="#distributing-work" title="Link to this heading">#</a></h1>
<p>DataFusion supports splitting work across processes by shipping
serialized expressions to workers: the driver builds an
<a class="reference internal" href="../autoapi/datafusion/index.html#datafusion.Expr" title="datafusion.Expr"><code class="xref py py-class docutils literal notranslate"><span class="pre">Expr</span></code></a>, each worker evaluates it against its
own slice of data. This pattern suits embarrassingly-parallel
workloads where the driver decides partitioning up front.</p>
<p>Query-level distribution — where the runtime partitions a single
logical or physical plan across worker nodes — is in progress
upstream via <a class="reference external" href="https://github.com/apache/datafusion-distributed">datafusion-distributed</a> and <a class="reference external" href="https://github.com/apache/datafusion-ballista">Apache
Ballista</a>. Both
have short sections at the end of this page; integration details
will land as those projects become usable from datafusion-python.</p>
<section id="expression-level-distribution">
<h2>Expression-level distribution<a class="headerlink" href="#expression-level-distribution" title="Link to this heading">#</a></h2>
<p>DataFusion expressions support distribution directly: pass one to a
worker process and Python’s standard
<a class="reference external" href="https://docs.python.org/3/library/pickle.html">pickle</a> machinery
serializes it transparently — the same machinery
<code class="xref py py-meth docutils literal notranslate"><span class="pre">multiprocessing.pool.Pool.map()</span></code>, Ray’s <code class="docutils literal notranslate"><span class="pre">&#64;ray.remote</span></code>, and
similar libraries already use to ship function arguments. Python UDFs
— scalar, aggregate, and window — travel inside the serialized
expression; the receiver does not need to pre-register them.</p>
<section id="basic-worker-pool-example">
<h3>Basic worker-pool example<a class="headerlink" href="#basic-worker-pool-example" title="Link to this heading">#</a></h3>
<p>Define a worker function that takes the expression plus a batch and
returns the evaluated result:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">pyarrow</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">pa</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">datafusion</span><span class="w"> </span><span class="kn">import</span> <span class="n">SessionContext</span>
<span class="k">def</span><span class="w"> </span><span class="nf">evaluate</span><span class="p">(</span><span class="n">expr</span><span class="p">,</span> <span class="n">batch</span><span class="p">):</span>
<span class="c1"># `expr` arrived here via the pool&#39;s automatic pickling —</span>
<span class="c1"># no manual serialization needed in user code.</span>
<span class="n">ctx</span> <span class="o">=</span> <span class="n">SessionContext</span><span class="p">()</span>
<span class="n">df</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">from_pydict</span><span class="p">({</span><span class="s2">&quot;a&quot;</span><span class="p">:</span> <span class="n">batch</span><span class="p">})</span>
<span class="k">return</span> <span class="n">df</span><span class="o">.</span><span class="n">with_column</span><span class="p">(</span><span class="s2">&quot;result&quot;</span><span class="p">,</span> <span class="n">expr</span><span class="p">)</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="s2">&quot;result&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">to_pydict</span><span class="p">()[</span><span class="s2">&quot;result&quot;</span><span class="p">]</span>
</pre></div>
</div>
<p>Then build the expression in the driver and fan it out:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">multiprocessing</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">mp</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">datafusion</span><span class="w"> </span><span class="kn">import</span> <span class="n">col</span><span class="p">,</span> <span class="n">udf</span>
<span class="n">double</span> <span class="o">=</span> <span class="n">udf</span><span class="p">(</span>
<span class="k">lambda</span> <span class="n">arr</span><span class="p">:</span> <span class="n">pa</span><span class="o">.</span><span class="n">array</span><span class="p">([(</span><span class="n">v</span><span class="o">.</span><span class="n">as_py</span><span class="p">()</span> <span class="ow">or</span> <span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">arr</span><span class="p">]),</span>
<span class="p">[</span><span class="n">pa</span><span class="o">.</span><span class="n">int64</span><span class="p">()],</span> <span class="n">pa</span><span class="o">.</span><span class="n">int64</span><span class="p">(),</span> <span class="n">volatility</span><span class="o">=</span><span class="s2">&quot;immutable&quot;</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;double&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">expr</span> <span class="o">=</span> <span class="n">double</span><span class="p">(</span><span class="n">col</span><span class="p">(</span><span class="s2">&quot;a&quot;</span><span class="p">))</span>
<span class="n">mp_ctx</span> <span class="o">=</span> <span class="n">mp</span><span class="o">.</span><span class="n">get_context</span><span class="p">(</span><span class="s2">&quot;forkserver&quot;</span><span class="p">)</span>
<span class="k">with</span> <span class="n">mp_ctx</span><span class="o">.</span><span class="n">Pool</span><span class="p">(</span><span class="n">processes</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span> <span class="k">as</span> <span class="n">pool</span><span class="p">:</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">pool</span><span class="o">.</span><span class="n">starmap</span><span class="p">(</span>
<span class="n">evaluate</span><span class="p">,</span>
<span class="p">[(</span><span class="n">expr</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]),</span> <span class="p">(</span><span class="n">expr</span><span class="p">,</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">])],</span>
<span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">results</span><span class="p">)</span> <span class="c1"># [[2, 4, 6], [20, 40, 60]]</span>
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>When saved to a <code class="docutils literal notranslate"><span class="pre">.py</span></code> file and executed with the <code class="docutils literal notranslate"><span class="pre">spawn</span></code> or
<code class="docutils literal notranslate"><span class="pre">forkserver</span></code> start method, wrap the driver block in
<code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">&quot;__main__&quot;:</span></code> so worker processes can re-import
the module without re-running it. This is a standard Python
<code class="xref py py-mod docutils literal notranslate"><span class="pre">multiprocessing</span></code> requirement, not DataFusion-specific —
see <a class="reference external" href="https://docs.python.org/3/library/multiprocessing.html#the-spawn-and-forkserver-start-methods">Safe importing of main module</a>
in the Python docs.</p>
</div>
</section>
<section id="what-travels-with-the-expression">
<h3>What travels with the expression<a class="headerlink" href="#what-travels-with-the-expression" title="Link to this heading">#</a></h3>
<ul class="simple">
<li><p><strong>Built-in functions</strong> (<code class="docutils literal notranslate"><span class="pre">abs</span></code>, <code class="docutils literal notranslate"><span class="pre">length</span></code>, arithmetic, comparisons,
etc.) — fully portable. Worker needs nothing pre-registered.</p></li>
<li><p><strong>Python UDFs</strong> — travel inline (subject to the two portability
requirements below). The callable, its signature, and any state
captured in closures travel inside the serialized expression and are
reconstructed on the worker automatically. Applies equally to:</p>
<ul>
<li><p><strong>scalar UDFs</strong> (<a class="reference internal" href="../autoapi/datafusion/index.html#datafusion.udf" title="datafusion.udf"><code class="xref py py-func docutils literal notranslate"><span class="pre">datafusion.udf()</span></code></a>)</p></li>
<li><p><strong>aggregate UDFs</strong> (<a class="reference internal" href="../autoapi/datafusion/index.html#datafusion.udaf" title="datafusion.udaf"><code class="xref py py-func docutils literal notranslate"><span class="pre">datafusion.udaf()</span></code></a>)</p></li>
<li><p><strong>window UDFs</strong> (<a class="reference internal" href="../autoapi/datafusion/index.html#datafusion.udwf" title="datafusion.udwf"><code class="xref py py-func docutils literal notranslate"><span class="pre">datafusion.udwf()</span></code></a>)</p></li>
</ul>
</li>
<li><p><strong>UDFs imported via the FFI capsule protocol</strong> — travel <strong>by name
only</strong>. The worker must already have a matching registration on its
<code class="xref py py-class docutils literal notranslate"><span class="pre">SessionContext</span></code>. Without that registration, evaluation
raises an error.</p></li>
</ul>
</section>
<section id="portability-requirements-for-inline-python-udfs">
<span id="distributed-udf-portability"></span><h3>Portability requirements for inline Python UDFs<a class="headerlink" href="#portability-requirements-for-inline-python-udfs" title="Link to this heading">#</a></h3>
<p>Inline Python UDFs ride on <a class="reference external" href="https://github.com/cloudpipe/cloudpickle">cloudpickle</a>, which imposes two
requirements on the worker environment:</p>
<ul class="simple">
<li><p><strong>Matching Python minor version.</strong> cloudpickle serializes Python
bytecode, which is not stable across minor versions. A UDF pickled
on 3.12 cannot be reconstructed on 3.11 or 3.13. The wire format
stamps the sender’s <code class="docutils literal notranslate"><span class="pre">(major,</span> <span class="pre">minor)</span></code>; mismatches raise a clear
error naming both versions. Align the Python version on driver and
workers.</p></li>
<li><p><strong>Imported modules must be importable on the worker.</strong> cloudpickle
captures the callable <em>by value</em> (bytecode and closure cells travel
whole), but names resolved through <code class="docutils literal notranslate"><span class="pre">import</span></code> are captured <em>by
reference</em> — module path only. A UDF doing
<code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">mylib</span> <span class="pre">import</span> <span class="pre">transform</span></code> requires <code class="docutils literal notranslate"><span class="pre">mylib</span></code> installed on the
worker. Same applies to bound methods of imported classes.
Self-contained UDFs (no imports beyond what the worker already has,
e.g. <code class="docutils literal notranslate"><span class="pre">pyarrow</span></code>) avoid this entirely.</p></li>
</ul>
</section>
<section id="registering-shared-udfs-on-workers">
<h3>Registering shared UDFs on workers<a class="headerlink" href="#registering-shared-udfs-on-workers" title="Link to this heading">#</a></h3>
<p>When an expression references an FFI capsule UDF (or any UDF the
worker must resolve from its registered functions), set up the
worker’s <code class="xref py py-class docutils literal notranslate"><span class="pre">SessionContext</span></code> once per process and install it
as the <em>worker context</em>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">datafusion</span><span class="w"> </span><span class="kn">import</span> <span class="n">SessionContext</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">datafusion.ipc</span><span class="w"> </span><span class="kn">import</span> <span class="n">set_worker_ctx</span>
<span class="k">def</span><span class="w"> </span><span class="nf">init_worker</span><span class="p">():</span>
<span class="n">ctx</span> <span class="o">=</span> <span class="n">SessionContext</span><span class="p">()</span>
<span class="n">ctx</span><span class="o">.</span><span class="n">register_udaf</span><span class="p">(</span><span class="n">my_ffi_aggregate</span><span class="p">)</span>
<span class="n">set_worker_ctx</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
<span class="k">with</span> <span class="n">mp</span><span class="o">.</span><span class="n">get_context</span><span class="p">(</span><span class="s2">&quot;forkserver&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">Pool</span><span class="p">(</span>
<span class="n">processes</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">initializer</span><span class="o">=</span><span class="n">init_worker</span>
<span class="p">)</span> <span class="k">as</span> <span class="n">pool</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</div>
<p>Inside a worker, expressions arriving from the driver resolve their
by-name references against the installed worker context. If no worker
context is installed, the global <code class="xref py py-class docutils literal notranslate"><span class="pre">SessionContext</span></code> is used —
fine for expressions that only reference built-ins and Python UDFs,
but FFI-capsule-backed registrations must be installed on the global
context to resolve.</p>
</section>
<section id="python-3-14-default-change">
<h3>Python 3.14 default change<a class="headerlink" href="#python-3-14-default-change" title="Link to this heading">#</a></h3>
<p>Python 3.14 changed the Linux default start method for
<code class="xref py py-mod docutils literal notranslate"><span class="pre">multiprocessing</span></code> from <code class="docutils literal notranslate"><span class="pre">fork</span></code> to <code class="docutils literal notranslate"><span class="pre">forkserver</span></code> (macOS has
defaulted to <code class="docutils literal notranslate"><span class="pre">spawn</span></code> since Python 3.8; Windows has always used
<code class="docutils literal notranslate"><span class="pre">spawn</span></code>). With <code class="docutils literal notranslate"><span class="pre">fork</span></code>, any state set in the parent was visible in
workers via copy-on-write; with <code class="docutils literal notranslate"><span class="pre">forkserver</span></code> and <code class="docutils literal notranslate"><span class="pre">spawn</span></code> it is
not. The <a class="reference internal" href="../autoapi/datafusion/ipc/index.html#datafusion.ipc.set_worker_ctx" title="datafusion.ipc.set_worker_ctx"><code class="xref py py-func docutils literal notranslate"><span class="pre">set_worker_ctx()</span></code></a> pattern works on
every start method — prefer it over relying on inherited state.</p>
</section>
<section id="practical-considerations">
<h3>Practical considerations<a class="headerlink" href="#practical-considerations" title="Link to this heading">#</a></h3>
<ul class="simple">
<li><p><strong>Serialized size scales with what travels inline.</strong> A serialized
expression of just built-ins is small (tens of bytes). An
expression carrying a Python UDF is hundreds of bytes (the callable
and its signature). When the same UDF is shipped many times,
registering an equivalent FFI-capsule UDF on each worker via
<a class="reference internal" href="../autoapi/datafusion/ipc/index.html#datafusion.ipc.set_worker_ctx" title="datafusion.ipc.set_worker_ctx"><code class="xref py py-func docutils literal notranslate"><span class="pre">set_worker_ctx()</span></code></a> and referring to it by
name cuts the per-trip overhead.</p></li>
<li><p><strong>Closure capture.</strong> When a Python UDF closes over surrounding
state — local variables, module-level objects, file paths — that
state is captured at serialization time. Surprises are possible if
the captured state is large, mutable, or not portable to the
worker’s environment. See <a class="reference internal" href="#distributed-udf-portability"><span class="std std-ref">Portability requirements for inline Python UDFs</span></a> for the Python-version and imported-module rules.</p></li>
</ul>
</section>
<section id="disabling-python-udf-inlining">
<h3>Disabling Python UDF inlining<a class="headerlink" href="#disabling-python-udf-inlining" title="Link to this heading">#</a></h3>
<p>For a stricter wire format, call
<code class="xref py py-meth docutils literal notranslate"><span class="pre">SessionContext.with_python_udf_inlining(enabled=False)</span></code> on the session
producing or consuming the bytes. With inlining disabled, Python
UDFs travel by name only — the same way FFI-capsule UDFs do — and
the receiver must have a matching registration.</p>
<p>Two use cases:</p>
<ul class="simple">
<li><p><strong>Cross-language portability.</strong> A non-Python decoder cannot
reconstruct a cloudpickled payload. Senders aimed at Java, C++,
or another Rust binary disable inlining and rely on the receiver
having compatible UDF registrations.</p></li>
<li><p><strong>Untrusted-source decode.</strong> With inlining disabled,
<code class="xref py py-meth docutils literal notranslate"><span class="pre">Expr.from_bytes()</span></code> never calls <code class="docutils literal notranslate"><span class="pre">cloudpickle.loads</span></code> on
the incoming bytes — an inline payload from a misbehaving sender
raises a clear error instead of executing arbitrary Python code.</p></li>
</ul>
<p>Mismatched configurations raise a descriptive error: an inline blob
fed to a strict receiver fails fast rather than silently dropping
into <code class="docutils literal notranslate"><span class="pre">cloudpickle.loads</span></code>.</p>
<p>To make the toggle apply through <code class="xref py py-func docutils literal notranslate"><span class="pre">pickle.dumps()</span></code> (which
calls <code class="xref py py-meth docutils literal notranslate"><span class="pre">Expr.to_bytes()</span></code> with no context), install the strict
session as the driver’s <em>sender context</em>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">datafusion</span><span class="w"> </span><span class="kn">import</span> <span class="n">SessionContext</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">datafusion.ipc</span><span class="w"> </span><span class="kn">import</span> <span class="n">set_sender_ctx</span>
<span class="n">set_sender_ctx</span><span class="p">(</span><span class="n">SessionContext</span><span class="p">()</span><span class="o">.</span><span class="n">with_python_udf_inlining</span><span class="p">(</span><span class="n">enabled</span><span class="o">=</span><span class="kc">False</span><span class="p">))</span>
<span class="c1"># Every subsequent pickle.dumps(expr) on this thread encodes</span>
<span class="c1"># without inlining the Python callable.</span>
</pre></div>
</div>
<p>Pair with a matching strict worker context
(<a class="reference internal" href="../autoapi/datafusion/ipc/index.html#datafusion.ipc.set_worker_ctx" title="datafusion.ipc.set_worker_ctx"><code class="xref py py-func docutils literal notranslate"><span class="pre">set_worker_ctx()</span></code></a>) so the <code class="docutils literal notranslate"><span class="pre">pickle.loads</span></code>
side also refuses inline payloads. Explicit
<code class="xref py py-meth docutils literal notranslate"><span class="pre">Expr.to_bytes(ctx)</span></code> and
<code class="xref py py-meth docutils literal notranslate"><span class="pre">Expr.from_bytes(blob,</span> <span class="pre">ctx=ctx)</span></code> calls
honor the supplied <code class="docutils literal notranslate"><span class="pre">ctx</span></code> directly and ignore the sender / worker
contexts.</p>
<p>The toggle only narrows the <code class="xref py py-meth docutils literal notranslate"><span class="pre">Expr.from_bytes()</span></code> surface;
<code class="xref py py-func docutils literal notranslate"><span class="pre">pickle.loads()</span></code> on untrusted bytes remains unsafe regardless
of this setting. See the <a class="reference internal" href="#distributed-expr-security"><span class="std std-ref">Security</span></a> section below for the full
threat model.</p>
</section>
<section id="security">
<span id="distributed-expr-security"></span><h3>Security<a class="headerlink" href="#security" title="Link to this heading">#</a></h3>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>Reconstructing an expression containing a Python UDF executes
arbitrary Python code on the receiver — pickle is doing the work
under the hood and pickle is unsafe on untrusted input (see the
<a class="reference external" href="https://docs.python.org/3/library/pickle.html#module-pickle">pickle module security warning</a>
in the Python standard library docs). Only accept expressions
from trusted sources. For untrusted-source workflows, disable
Python UDF inlining (see above), restrict senders to built-in
functions and pre-registered Rust-side UDFs, and avoid
<code class="xref py py-func docutils literal notranslate"><span class="pre">pickle.loads()</span></code> on externally supplied bytes entirely.</p>
</div>
</section>
<section id="reference-session-context-slots">
<h3>Reference: session context slots<a class="headerlink" href="#reference-session-context-slots" title="Link to this heading">#</a></h3>
<p>There is only one type — <code class="xref py py-class docutils literal notranslate"><span class="pre">SessionContext</span></code>. It can occupy
up to four <em>slots</em> in a running program:</p>
<div class="pst-scrollable-table-container"><table class="table">
<thead>
<tr class="row-odd"><th class="head"><p>Slot</p></th>
<th class="head"><p>Lifetime</p></th>
<th class="head"><p>Purpose</p></th>
<th class="head"><p>Set how</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>User-held</p></td>
<td><p>Local variable / attribute</p></td>
<td><p>Build and run queries</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">ctx</span> <span class="pre">=</span> <span class="pre">SessionContext(...)</span></code></p></td>
</tr>
<tr class="row-odd"><td><p>Global</p></td>
<td><p>Process singleton (lazy-init)</p></td>
<td><p>Backs module-level <a class="reference internal" href="../autoapi/datafusion/io/index.html#datafusion.io.read_parquet" title="datafusion.io.read_parquet"><code class="xref py py-func docutils literal notranslate"><span class="pre">read_parquet()</span></code></a>, <a class="reference internal" href="../autoapi/datafusion/io/index.html#datafusion.io.read_csv" title="datafusion.io.read_csv"><code class="xref py py-func docutils literal notranslate"><span class="pre">read_csv()</span></code></a>, <a class="reference internal" href="../autoapi/datafusion/io/index.html#datafusion.io.read_json" title="datafusion.io.read_json"><code class="xref py py-func docutils literal notranslate"><span class="pre">read_json()</span></code></a>, <a class="reference internal" href="../autoapi/datafusion/io/index.html#datafusion.io.read_avro" title="datafusion.io.read_avro"><code class="xref py py-func docutils literal notranslate"><span class="pre">read_avro()</span></code></a>; final fallback for <code class="xref py py-meth docutils literal notranslate"><span class="pre">Expr.from_bytes()</span></code></p></td>
<td><p>Implicit; access via <code class="xref py py-meth docutils literal notranslate"><span class="pre">SessionContext.global_ctx()</span></code></p></td>
</tr>
<tr class="row-even"><td><p>Sender</p></td>
<td><p>Thread-local on the driver</p></td>
<td><p>Codec settings for outbound <code class="xref py py-func docutils literal notranslate"><span class="pre">pickle.dumps()</span></code> / <code class="xref py py-meth docutils literal notranslate"><span class="pre">Expr.to_bytes()</span></code> without <code class="docutils literal notranslate"><span class="pre">ctx</span></code></p></td>
<td><p><a class="reference internal" href="../autoapi/datafusion/ipc/index.html#datafusion.ipc.set_sender_ctx" title="datafusion.ipc.set_sender_ctx"><code class="xref py py-func docutils literal notranslate"><span class="pre">set_sender_ctx()</span></code></a></p></td>
</tr>
<tr class="row-odd"><td><p>Worker</p></td>
<td><p>Thread-local on the worker</p></td>
<td><p>Function registry for inbound <code class="xref py py-func docutils literal notranslate"><span class="pre">pickle.loads()</span></code> / <code class="xref py py-meth docutils literal notranslate"><span class="pre">Expr.from_bytes()</span></code> without <code class="docutils literal notranslate"><span class="pre">ctx</span></code></p></td>
<td><p><a class="reference internal" href="../autoapi/datafusion/ipc/index.html#datafusion.ipc.set_worker_ctx" title="datafusion.ipc.set_worker_ctx"><code class="xref py py-func docutils literal notranslate"><span class="pre">set_worker_ctx()</span></code></a></p></td>
</tr>
</tbody>
</table>
</div>
<p>The same <code class="xref py py-class docutils literal notranslate"><span class="pre">SessionContext</span></code> object may occupy more than one
slot simultaneously — installing it into a slot is a reference, not
a copy. A non-distributed program only ever uses the user-held slot;
the global slot is invisible unless you call top-level <code class="docutils literal notranslate"><span class="pre">read_*</span></code>
helpers.</p>
<p>Resolution order on the worker side is <em>explicit argument →
worker context → global context.</em> Explicit <code class="docutils literal notranslate"><span class="pre">ctx=</span></code> on
<code class="xref py py-meth docutils literal notranslate"><span class="pre">Expr.from_bytes()</span></code> always wins; the sender slot is ignored
on decode and the worker slot is ignored on encode.</p>
<p>Sharp edges:</p>
<ul class="simple">
<li><p>Sender and worker slots are <strong>thread-local</strong>. Background threads
on either side see <code class="docutils literal notranslate"><span class="pre">None</span></code> until they install their own.</p></li>
<li><p>Under the <code class="docutils literal notranslate"><span class="pre">fork</span></code> start method, the parent’s <code class="docutils literal notranslate"><span class="pre">threading.local()</span></code>
values are copied into the child by copy-on-write — a forked
worker initially observes whatever sender / worker slot the parent
had set, until the worker writes its own value (or calls the
matching <code class="docutils literal notranslate"><span class="pre">clear_*_ctx</span></code>). <code class="docutils literal notranslate"><span class="pre">spawn</span></code> and <code class="docutils literal notranslate"><span class="pre">forkserver</span></code> workers
start with empty thread-local slots. Treat the slot as
uninitialized on worker entry and install (or clear) it explicitly
in the worker initializer; do not rely on inherited state.</p></li>
<li><p>The global slot persists across <code class="docutils literal notranslate"><span class="pre">fork</span></code> workers (copy-on-write
memory inherit) but not across <code class="docutils literal notranslate"><span class="pre">spawn</span></code> / <code class="docutils literal notranslate"><span class="pre">forkserver</span></code> workers
(fresh process — register or install a worker context on
start-up).</p></li>
<li><p>The inlining toggle is per-context state, not a global switch.
Two contexts with different toggles can coexist in one process.</p></li>
</ul>
</section>
</section>
<section id="query-level-distribution-via-datafusion-distributed">
<h2>Query-level distribution via datafusion-distributed<a class="headerlink" href="#query-level-distribution-via-datafusion-distributed" title="Link to this heading">#</a></h2>
<p>🚧 <em>Work in progress upstream — not yet usable from datafusion-python.</em></p>
<p><a class="reference external" href="https://github.com/apache/datafusion-distributed">datafusion-distributed</a>
splits a single physical plan into stages and runs each stage on a
different worker node. The driver writes a SQL or DataFrame query
once; the runtime handles partitioning, shuffles, and reassembly.</p>
<p>A datafusion-python integration is in development. This section will
document the integration once it lands. In the meantime, the
expression-level approach above covers most use cases that do not
require automatic plan partitioning.</p>
</section>
<section id="query-level-distribution-via-apache-ballista">
<h2>Query-level distribution via Apache Ballista<a class="headerlink" href="#query-level-distribution-via-apache-ballista" title="Link to this heading">#</a></h2>
<p>🚧 <em>Work in progress upstream — not yet usable from datafusion-python.</em></p>
<p><a class="reference external" href="https://github.com/apache/datafusion-ballista">Apache Ballista</a>
provides distributed query execution on top of DataFusion with a
scheduler / executor model better suited to long-lived cluster
deployments. A datafusion-python integration is on the roadmap; this
section will fill in once the integration is usable.</p>
</section>
<section id="see-also">
<h2>See also<a class="headerlink" href="#see-also" title="Link to this heading">#</a></h2>
<ul class="simple">
<li><p><a class="reference internal" href="../autoapi/datafusion/ipc/index.html#module-datafusion.ipc" title="datafusion.ipc"><code class="xref py py-mod docutils literal notranslate"><span class="pre">datafusion.ipc</span></code></a> — worker context API.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">examples/multiprocessing_pickle_expr.py</span></code> — runnable
<code class="docutils literal notranslate"><span class="pre">multiprocessing.Pool</span></code> example that ships a different parametric
expression to each worker and collects results back.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">examples/ray_pickle_expr.py</span></code> — runnable Ray actor example.</p></li>
</ul>
</section>
</section>
</article>
<footer class="prev-next-footer d-print-none">
<div class="prev-next-area">
<a class="left-prev"
href="configuration.html"
title="previous page">
<i class="fa-solid fa-angle-left"></i>
<div class="prev-next-info">
<p class="prev-next-subtitle">previous</p>
<p class="prev-next-title">Configuration</p>
</div>
</a>
<a class="right-next"
href="sql.html"
title="next page">
<div class="prev-next-info">
<p class="prev-next-subtitle">next</p>
<p class="prev-next-title">SQL</p>
</div>
<i class="fa-solid fa-angle-right"></i>
</a>
</div>
</footer>
</div>
<dialog id="pst-secondary-sidebar-modal"></dialog>
<div id="pst-secondary-sidebar" class="bd-sidebar-secondary bd-toc"><div class="sidebar-secondary-items sidebar-secondary__inner">
<div class="sidebar-secondary-item">
<div
id="pst-page-navigation-heading-2"
class="page-toc tocsection onthispage">
<i class="fa-solid fa-list"></i> On this page
</div>
<nav class="bd-toc-nav page-toc" aria-labelledby="pst-page-navigation-heading-2">
<ul class="visible nav section-nav flex-column">
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#expression-level-distribution">Expression-level distribution</a><ul class="visible nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#basic-worker-pool-example">Basic worker-pool example</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#what-travels-with-the-expression">What travels with the expression</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#portability-requirements-for-inline-python-udfs">Portability requirements for inline Python UDFs</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#registering-shared-udfs-on-workers">Registering shared UDFs on workers</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#python-3-14-default-change">Python 3.14 default change</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#practical-considerations">Practical considerations</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#disabling-python-udf-inlining">Disabling Python UDF inlining</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#security">Security</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#reference-session-context-slots">Reference: session context slots</a></li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#query-level-distribution-via-datafusion-distributed">Query-level distribution via datafusion-distributed</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#query-level-distribution-via-apache-ballista">Query-level distribution via Apache Ballista</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#see-also">See also</a></li>
</ul>
</nav></div>
</div></div>
</div>
<footer class="bd-footer-content">
</footer>
</main>
</div>
</div>
<!-- Scripts loaded after <body> so the DOM is not blocked -->
<script defer src="../_static/scripts/bootstrap.js?digest=8878045cc6db502f8baf"></script>
<script defer src="../_static/scripts/pydata-sphinx-theme.js?digest=8878045cc6db502f8baf"></script>
<!-- Based on pydata_sphinx_theme/footer.html -->
<footer class="footer mt-5 mt-md-0">
<div class="container">
<div class="footer-item">
<p>Apache Arrow DataFusion, Arrow DataFusion, Apache, the Apache feather logo, and the Apache Arrow DataFusion project logo</p>
<p>are either registered trademarks or trademarks of The Apache Software Foundation in the United States and other countries.</p>
</div>
</div>
</footer>
</body>
</html>