Skip to content

feat: use datafusion python FFI for integration (native datafusion python support) - #2588

Open
milenkovicm wants to merge 13 commits into
apache:mainfrom
milenkovicm:feat_python_query_planner
Open

milenkovicm wants to merge 13 commits into
apache:mainfrom
milenkovicm:feat_python_query_planner

Conversation

@milenkovicm

@milenkovicm milenkovicm commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

import ballista

from ballista import BallistaSessionContext

import datafusion
# BallistaSessionContext replaces SessionContext
# from datafusion import SessionContext
from datafusion import DataFrame

# replace datafusion SessionContext
# ctx = SessionContext()
# with BallistaSessionContext
ctx = BallistaSessionContext(address=f"df://localhost:50050")

# to run query distributed ob ballista
ctx.sql("SELECT 1 as r").show()

Closes #.

Rationale for this change

  • update to datafusion python 55
  • use FFI query planner released with datafusion python 55, to make ballista integration just a tin wrapper on top of datfusion-puthon

What changes are included in this PR?

  • removed DistributedDataFrame and BallistaContext
  • removed redefining meta classes for redefinition of DistributedDataFrame and BallistaContext
  • removed methods which are tied to DistributedDataFrame and BallistaContext, i would argue they should not be there in the first place
  • use FFI datafusion python query planner

Are there any user-facing changes?

yes api changed, some methods and classes disappeared, as python integration was experimental users should have expected

this PR builds on @timsaucer's PR #2252

Follow up

  • support for distributed python UDFs

@milenkovicm
milenkovicm marked this pull request as draft October 12, 2026 18:18
@milenkovicm
milenkovicm force-pushed the feat_python_query_planner branch 4 times, most recently from e57d4d3 to 32b0078 Compare October 12, 2026 19:42
@milenkovicm

Copy link
Copy Markdown
Contributor Author

hey @timsaucer this is follow up on your #2252 after datafusion python 55 released,

uv run --no-project pytest

FAILED python/tests/test_context.py::test_write_csv - Exception: DataFusion error: FFI error: This feature is not implemented: FFI does not support decode_file_format
FAILED python/tests/test_context.py::test_write_parquet - Exception: DataFusion error: FFI error: This feature is not implemented: FFI does not support decode_file_format
FAILED python/tests/test_context.py::test_write_json - Exception: DataFusion error: FFI error: This feature is not implemented: FFI does not support decode_file_format

I have removed good part of ballista specific code, happy day scenario works, it connects to ballista and runs the query, but write_ methods are failing, and i'm not sure if there is something else needed.

any ideas what could be done ?

cc @andygrove another pair of eyes would help (thanks)

@milenkovicm
milenkovicm force-pushed the feat_python_query_planner branch from 32b0078 to d568260 Compare October 12, 2026 19:46
@milenkovicm

Copy link
Copy Markdown
Contributor Author

there is comment apache/datafusion-python#1679 (comment) but it makes no sense to me, perhaps @timsaucer may have better idea what claude wants to say :D

Comment thread ballista/core/src/serde/mod.rs Outdated
@milenkovicm milenkovicm changed the title feat: update to datafusion 55 and remove distributed dataframe feat: use datafusion python FFI for integration with ballista Oct 14, 2026
@milenkovicm milenkovicm changed the title feat: use datafusion python FFI for integration with ballista feat: use datafusion python FFI for integration (native datafusion python support) Oct 14, 2026
@milenkovicm
milenkovicm marked this pull request as ready for review October 14, 2026 18:59

@andygrove andygrove left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pushing this through @milenkovicm, and @timsaucer for the groundwork in #2252! Moving to a real DataFusion SessionContext is a big improvement. Every DataFrame method now runs on the cluster, where before only the few the metaclass wrapped did.

I built this branch and main locally and ran a few probes against setup_test_cluster(). I left comments on a config regression, EXPLAIN ANALYZE and some API cleanup. I'm requesting changes mainly for the config issue, since it makes queries fail that work on main today.

Since this removes DistributedDataFrame, turns BallistaSessionContext into a function and changes what config does, could we also add the api-change label and a Python client section to docs/source/upgrading/56.0.0.md?

Comment thread python/python/ballista/extension.py Outdated
Comment thread python/src/query_planner.rs Outdated
input.clone(),
)?))
}
PhysicalPlanType::DistributedQuery(_) => Err(DataFusionError::Internal(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving the serde into PyBallistaPhysicalExtensionCodec makes sense given your earlier note. Would it make sense to move the proto message there too? Right now core's wire format has a node that its own codec rejects, and a library user who hits this arm gets an internal error with no hint about where the node comes from. If it needs to stay in core's oneof so the Python codec can tell it apart from Ballista nodes, a short comment on the message and on this arm saying only the Python client produces it would help. Keeping it out of core would also avoid adding a PhysicalPlanType variant, though tags 13 to 15 went in the same way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest keeping this here, with the changed exception saying this is intended to be used in python only. I agree its not ideal but alternative is to have tonic infrastructure in python project. wdyt ?

Comment thread python/src/config.rs
Ok(Self { config })
}

fn __datafusion_extension_options__<'py>(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ballista.client.use_tls and ballista.client.pull stop working in this path. DistributedQueryExec::execute reads both from a native BallistaConfig on the task context. datafusion-ffi rebuilds that context on the Ballista side with only a generic FFI_ExtensionOptions, so both fall back to their defaults. The docs tell clients to set ballista.client.use_tls for TLS result fetches, and on main it works from cluster_config. I haven't tried this against a TLS cluster, so please double check. Could execute() fall back to self.config when the task context has no BallistaConfig? Or could the Python side rebuild one from the datafusion_ffi options?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i can't reproduce this issue

ctx.sql("SET ballista.job.name='super job'").show()
ctx.sql("SET ballista.shuffle.force_remote_read=true").show()

ctx.sql("select id, count(*) from t group by id").show()

manually checking scheduler i see them set

same for

ctx = BallistaSessionContext(address=f"df://{host}:{port}", cluster_config={
    "ballista.job.name": "super job",
    "ballista.shuffle.force_remote_read": True
})

can you please re-check when u get chance

Comment thread python/python/ballista/__init__.py
Comment thread python/python/ballista/jupyter.py Outdated
@milenkovicm
milenkovicm marked this pull request as draft October 16, 2026 13:43
@milenkovicm

Copy link
Copy Markdown
Contributor Author

thanks @andygrove comments make sense, will follow up over the weekend, converting to PR draft until then

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Oct 17, 2026
Co-authored-by: Tim Saucer <timsaucer@gmail.com>
timsaucer and others added 11 commits October 17, 2026 13:01
datafusion-python replaces the codecs on an installed FFI_QueryPlanner
with its own, so Ballista decodes logical plans through a
ForeignLogicalExtensionCodec. In datafusion-ffi 55.2.0 that codec's
try_decode_file_format is a hard-coded not_impl_err!, so any plan with a
FileFormatFactory (LogicalPlan::Copy, i.e. DataFrame.write_*) fails.

Replace the planner handle's create_physical_plan and clone entries.
The replacement forwards to datafusion-ffi's own entry on a temporary
handle whose logical codec is local to this library and falls back to
DefaultLogicalExtensionCodec for file formats.

This mirrors private datafusion-ffi layouts and is only valid for the
pinned datafusion-ffi =55.2.0. Remove once datafusion-ffi handles file
formats in ForeignLogicalExtensionCodec.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@milenkovicm
milenkovicm force-pushed the feat_python_query_planner branch from 3f8c511 to b1ad4ea Compare October 17, 2026 12:05
@milenkovicm
milenkovicm marked this pull request as ready for review October 17, 2026 12:06
@milenkovicm
milenkovicm requested a review from andygrove October 17, 2026 12:06
@milenkovicm

Copy link
Copy Markdown
Contributor Author

please have a look when you get chance @andygrove

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-change documentation Improvements or additions to documentation python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants