Skip to content

[EPIC] Better support / documentation / tests for Streaming SQL Engines #24265

Description

@alamb

Is your feature request related to a problem or challenge?

DataFusion has traditionally been developed using techniques from Batch processing systems and tries to maximize throughput by being as efficient as possible per tuple. This often involves techniques such as batching rows together to amortize the overhead of each row

However, it also seems increasingly common to try and use DataFusion for streaming systems, similar to Apache Flink, which typically have "unbounded queries" that run indefinitely and place a high value on values being produced quickly (to minimize latency) rather than simply raw per-row computation.

I think we have a bunch of features for streaming documented / tested in isolation already (see below) but the overall design and goal is not clearly documented or tested and thus the overall experience is regressing

The recent presentation on youtube from @sap1ens also mentioned this mismatch between streaming and batch oriented workflows for several built in operators:

Image

It turns out that unfortunately we are making it worse for streaming engines recently by making things better for batch execution (typically by moving buffering into the execution nodes)

There is some basic infrastructure today, but it isn't used / respected everywhere and there isn't an over arching strategy / description of its use

However, given that we are regressing behavior for streaming I think we should consider formalizing this functionality more

Example Streaming Systems

  • Synnada — streaming-first data products; authors of the original streaming roadmap #4285
  • Streamling (Goldsky, @sap1ens) — data streaming runtime on Rust/Arrow/DataFusion; powers Goldsky Turbo Pipelines. See Introducing Streamling and talk
  • StreamFusion (@jordepic) — Flink accelerator: swaps supported Flink SQL operators for native DataFusion execution over JNI
  • Arroyo — distributed stream processing engine; SQL engine built on Arrow + DataFusion since 0.10
  • Denormalized — embeddable "DuckDB for streaming"; Kafka, windowed aggregates, stream joins. See The future of DataFusion is Streaming
  • ArkFlow — Rust stream processing engine with a DataFusion-based SQL processor
  • LaminarDB — embedded streaming SQL database; integrates DataFusion in its SQL layer
  • Kamu — planet-scale streaming data pipeline

Describe the solution you'd like

Given how many systems seem to want to use DataFusion for streaming systems (rather than batch oriented ones) and the natural tension between batching (maximize throughput) vs streaming (minimize latency) I think if we should formalize / test / document the best way to configure DataFusion for streaming.

Describe alternatives you've considered

Better documentation / tests

Maybe part of this epic could be just to document what we have better

Config Flag

Maybe it would be worth some sort of "bounded configuration mode" setting to make implementing streaming systems easier

For example,

  1. A global config setting like streaming or optimize_for_latency (maybe repurpose datafusion.execution.coalesce_batches (source) -- since Remove coalesce batches rule and deprecate CoalesceBatchesExec #19622 I don't think anything reads it
  2. A flag on operators like FilterExec and RepartitionExec that controls their emission behavior (aka should they flush at the earliest opportunity)

Better propagation / policies

Several potential policies for when operators should flush partially-full batches came up in the context of #24044:

  1. Skip coalescing entirely when the input is unbounded (proposed by @goutamadwant in this comment, the approach of
    fix: avoid buffering unbounded repartition output indefinitely #24193 for RepartitionExec): simple and fixes the
    correctness issue, but @calvinchengx (comment) and @jayzhan211 (comment) note it may not be applicable to all cases
  2. Drain-on-pending (suggested by @ahirner in this comment): keep coalescing while input is Poll::Ready, but flush the
    residual whenever the input returns Pending instead of holding it.
  3. Size-or-deadline (suggested by @goutamadwant (comment) / @calvinchengx (comment)): flush at batch_size OR after a
    configurable maximum batch age, whichever comes first.
  4. Richer emission triggers (suggested by @jayzhan211 in this comment): time-based, idle-based (N empty polls), or
    watermark / checkpoint-barrier-based, if DataFusion later grows control-message propagation

Make more operators respect buffering

Additional context

We have discussed this in the past

Related Issues

Activity

  1. added theissue type on Aug 11, 2026
  2. added
    PROPOSAL EPICA proposal being discussed that is not yet fully underway
    on Aug 11, 2026
  3. changed the title [-][EPIC] Better support for Streaming SQL[/-] [+][EPIC] Better support / documentation / tests for Streaming SQL[/+] on Aug 11, 2026
  4. changed the title [-][EPIC] Better support / documentation / tests for Streaming SQL[/-] [+][EPIC] Better support / documentation / tests for Streaming SQL Engines[/+] on Aug 11, 2026
  5. marked Real-time streaming support #10895 as a duplicate of this issue on Aug 11, 2026
  6. sap1ens commented on Aug 11, 2026

    @sap1ens
    Contributor

    Thanks for creating this issue, @alamb!

    I think getting something like "Size-or-deadline" flush trigger into as many operators as possible (with values configured globally), can be the first step into better streaming semantics. You can avoid a global "streaming" flag by simply choosing how to flush batches, for example:

    • Batch system: size: 8k, deadline: disabled.
    • Streaming system: size: 1k, deadline: 100ms.

    Control-message propagation also would be a really cool building block: you could build high-level functionality like checkpointing, windowing, etc.

  7. jordepic commented on Aug 11, 2026

    @jordepic
    Contributor

    Thanks @alamb !

    In my personal experience building out StreamFusion, DataFusion could cover a few more streaming join operations if it supported a couple of things:

    1. Changelogs (need to be able to support incoming deleted rows, updated rows, etc)
    2. Window functions having an API to take in existing state and then aggregate an incoming batch into them

    @sap1ens - as StreamFusion and Streamling continue to both evolve a bit more, I'd love to hop on a call sometime and chat about them!

  8. rkrishn7 commented on Aug 11, 2026

    @rkrishn7
    Contributor

    Thanks for this @alamb.

    Massive also has an internal stream processing engine built on DataFusion. This talk from the Stockholm meetup earlier this year provides an overview.

    We ultimately opted out of DataFusion's physical layer and built our own for a few reasons:

    • Our implementation requires support for interleaving non-data payloads with data across operator edges. The core algorithm we use to provide exactly-once processing guarantees relies on these payloads to establish ordered cuts within the streaming graph.
    • Edges between operators can have different semantics, such as append-only and changelog streams.
    • Streaming engines also have specific requirements around watermark alignment, stateful node IDs, and stream keys.

    It would be great if DataFusion's physical execution/planning interfaces could accommodate some of these requirements, though a generic extension mechanism may be a better fit than baking stream-processing-specific semantics into DataFusion itself.

    Separately, we do a fair amount with aggregation and another pain point I've come across is the lack of a way to peek at aggregate state or evaluate only a subset of a group. Supporting this would make incremental emission from time-windowed aggregation operators more efficient. This issue describes a similar problem.

    I'm sure there is more that I'm not thinking of right now 😅 . Will update this thread as more comes to mind.

  9. kszlim commented on Aug 12, 2026

    @kszlim

    I think https://github.com/feldera/feldera is very interesting in this space

  10. 2010YOUY01 commented on Aug 23, 2026

    @2010YOUY01
    Contributor

    Here is a follows up discussion for

    My suggestion is to completely separate the streaming variant from the existing implementation—using different Streams/state machines—instead of adding internal control flags and reusing the same implementation.

    The reason is that adding a new flag for "streaming vs. batch" does not just add a small amount of complexity. It adds a new dimension to the operator's state space, and the complexity grows combinatorially as more such dimensions are introduced.

    This is somewhat counterintuitive. What #24353 is doing may look like a small, linear increase in implementation complexity, but it is actually adding another independent dimension to the operator, and the extra complexity will continue to grow on that newly added dimension.

    There are already other dimensions, such as ordered vs. unordered input, spilling vs. non-spilling, etc. 10 innocent-looking binary dimensions can already produce 1,024 combinations. We have suffered from the same problem before; the following write-up is a case study:

    The alternative I propose is to represent these as hard separated stream types, such as StreamingFilterStream and FilterStream, each with its own state machine.

    EDIT: I suspect some optimizer rules are not compatible with streaming variants for the existing ExecutionPlans, it might be necessary to even split the ExecutionPlan and maintain the dedicated optimizer rules for streaming input. Then a good long-term approach looks like a) only keep API changes in datafusion core b) move implementation to a separate project.

  11. jayzhan211 commented on Aug 23, 2026

    @jayzhan211
    Contributor

    I agree on separating into its own Stream type. If we find significant similarity between the implementations once the streaming feature is in place, we can revisit and refactor at that point.

  12. rkrishn7 commented on Aug 24, 2026

    @rkrishn7
    Contributor

    Separately, we do a fair amount with aggregation and another pain point I've come across is the lack of a way to peek at aggregate state or evaluate only a subset of a group. Supporting this would make incremental emission from time-windowed aggregation operators more efficient.

    I filed two related issues with concrete API proposals:

    The motivating use case is stateful stream processing, but both APIs are general and do not necessarily tie DataFusion to any sort of stream processing model.

  13. alamb commented on Aug 24, 2026

    @alamb
    ContributorAuthor

    Pulling some discussion out into #24353 (comment)

    If we agree on supporting more "streaming" execution in DataFusion, I think we should extend that support across all existing operators. +1 from me on the overall direction. How to design this while keeping maintenance complexity low could be a separate discussion — maybe in #24265

    In my mind DataFusion is in a half way state now -- it has some features / support for streaming (e.g. Boundedness), but as shown in #24044 since there is no clear design / tests / documentation implicit assumptions that streaming systems may be counting on can (and do) get broken

    So I think we should first agree on if we want to try and make DataFusion more useful for building streaming engines -- I think it is important and there are a bunch of people already doing so (and have done so for a while -- e.g. Arroyo and earlier versions of Synnada). Rewriting Apache Flink in rust seems to be all the rage now too (e.g. @jordepic 's StreamFusion, etc) , and many people are using DataFusion to try.

    it seems like that is the consensus of this ticket too

  14. removed theissue type on Aug 24, 2026
  15. alamb commented on Aug 24, 2026

    @alamb
    ContributorAuthor

    Given @2010YOUY01 's comments on #24265 (comment) and @jayzhan211 's comments

    I agree on separating into its own Stream type. If we find significant similarity between the implementations once the streaming feature is in place, we can revisit and refactor at that point.

    Where do we want to put this code?

    It feels to me like we should try and put it into its own crate (where we can document it, and allow downstream systems to opt in) -- BTW the symmetric hash join code is another example of something that I think is mostly for the streaming usecase and would be good to consolidate

    How about a new crate

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    PROPOSAL EPICA proposal being discussed that is not yet fully underwayenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions