Skip to content

feat: serialize LazyMemoryExec through generator hooks - #25765

Open
QinXi-ai wants to merge 4 commits into
apache:mainfrom
QinXi-ai:codex/25395-generator-proto
Open

QinXi-ai wants to merge 4 commits into
apache:mainfrom
QinXi-ai:codex/25395-generator-proto

Conversation

@QinXi-ai

@QinXi-ai QinXi-ai commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Part of #25395. This is the wire-compatible generator-hook step; multiple-generator serialization remains separate work.

Rationale for this change

Serializing a built-in LazyMemoryExec currently requires the central protobuf implementation to recognize each concrete generator type. Let generators supply the existing generate-series representation, with decoding owned by the series implementation.

What changes are included in this PR?

  • Add the defaulted, feature-gated LazyBatchGenerator::try_to_proto hook and dispatch to it from LazyMemoryExec.
  • Implement encoding for empty, integer and timestamp/date series, and move built-in decoding into datafusion-functions-table.
  • Preserve the single-generator wire format, extension fallback for unsupported or multiple generators, and propagation of actual encoding errors.
  • Retain the four existing PhysicalPlanNodeExt generate-series helpers as deprecated compatibility methods. The proto README documents replacements.
  • Document that a custom hook's payload must reproduce its reset-state rows, batch boundaries and schema. Only Ok(None) permits extension fallback; Err stops serialization.
  • Exercise a custom generator that delegates execution/reset to a real series, including a partially consumed cursor and repeated execution after round-trip serialization.

The fixed built-in decoder still requires datafusion-functions-table; removing that dependency would require a separate decoder registration design.

What is the testing strategy for this PR?

Tests build legacy wire messages independently of the hooks and check exact re-encoding, execution, batching and repeatability. They also cover custom hook dispatch, deprecated helper compatibility, unsupported generators, malformed payloads and batch-size overflow.

Local validation of the review follow-up on Windows x64:

  • Rust 1.98.1: cargo test --locked -p datafusion-proto --test proto_integration lazy_memory: 6 passed, 0 failed.
  • Rust 1.99.0 (repository toolchain): cargo fmt --all -- --check: passed.
  • Repository Markdown formatter and git diff --check: passed.
  • Rust 1.99.0: cargo clippy --offline --all-targets --all-features -- -D warnings failed with three clippy::mutable_key_type errors in the untouched datafusion/optimizer/src/push_down_filter.rs at lines 1177, 1181 and 1665. The diagnostics follow Expr through Arrow buffers to memory-accounting interior mutability. That file has no changes in this follow-up; fetching current upstream main did not reveal a fix for these locations. Remaining build jobs were stopped after the errors. Full-workspace Clippy is not claimed as passing.

The extended workspace test suite and performance benchmarks were not rerun. This follow-up adds compatibility forwarding methods, documentation and regression coverage; it does not change the aggregation/execution hot path.

Are there any user-facing changes?

New defaulted Rust trait methods and an opt-in proto feature. Existing downstream generators need no changes. Direct users of the four old generate-series helpers retain source compatibility, with deprecation notices and a documented migration path. The built-in single-generator wire format is unchanged.

This contribution is AI-assisted.

@github-actions github-actions Bot added development-process Related to development process of DataFusion common Related to common crate proto Related to proto crate functions Changes to functions implementation physical-plan Changes to the physical-plan crate labels Sep 26, 2026
@github-actions github-actions Bot removed the common Related to common crate label Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Thank you for opening this pull request!

Reviewer note: cargo-semver-checks reported the current version number is not SemVer-compatible with the changes in this pull request (compared against the base branch).

Details
     Cloning apache/main
    Building datafusion-functions-table v55.1.0 (current)
       Built [  47.512s] (current)
     Parsing datafusion-functions-table v55.1.0 (current)
      Parsed [   0.008s] (current)
    Building datafusion-functions-table v55.1.0 (baseline)
       Built [  41.038s] (baseline)
     Parsing datafusion-functions-table v55.1.0 (baseline)
      Parsed [   0.008s] (baseline)
    Checking datafusion-functions-table v55.1.0 -> v55.1.0 (no change; assume patch)
     Checked [   0.092s] 229 checks: 229 pass, 31 skip
     Summary no semver update required
    Finished [  90.047s] datafusion-functions-table
    Building datafusion-physical-plan v55.1.0 (current)
       Built [  36.351s] (current)
     Parsing datafusion-physical-plan v55.1.0 (current)
      Parsed [   0.194s] (current)
    Building datafusion-physical-plan v55.1.0 (baseline)
       Built [  35.959s] (baseline)
     Parsing datafusion-physical-plan v55.1.0 (baseline)
      Parsed [   0.193s] (baseline)
    Checking datafusion-physical-plan v55.1.0 -> v55.1.0 (no change; assume patch)
     Checked [   0.655s] 229 checks: 229 pass, 31 skip
     Summary no semver update required
    Finished [  74.460s] datafusion-physical-plan
    Building datafusion-proto v55.1.0 (current)
       Built [  52.257s] (current)
     Parsing datafusion-proto v55.1.0 (current)
      Parsed [   0.013s] (current)
    Building datafusion-proto v55.1.0 (baseline)
       Built [  51.895s] (baseline)
     Parsing datafusion-proto v55.1.0 (baseline)
      Parsed [   0.014s] (baseline)
    Checking datafusion-proto v55.1.0 -> v55.1.0 (no change; assume patch)
     Checked [   0.117s] 229 checks: 228 pass, 1 fail, 0 warn, 31 skip

--- failure trait_method_marked_deprecated: trait method #[deprecated] added ---

Description:
A trait method is now #[deprecated]. Downstream crates will get a compiler warning when using this method.
        ref: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.51.0/src/lints/trait_method_marked_deprecated.ron

Failed in:
  method generate_series_name_to_str in trait datafusion_proto::physical_plan::PhysicalPlanNodeExt in /home/runner/work/datafusion/datafusion/datafusion/proto/src/physical_plan/mod.rs:1147
  method try_into_generate_series_physical_plan in trait datafusion_proto::physical_plan::PhysicalPlanNodeExt in /home/runner/work/datafusion/datafusion/datafusion/proto/src/physical_plan/mod.rs:1147
  method str_to_generate_series_name in trait datafusion_proto::physical_plan::PhysicalPlanNodeExt in /home/runner/work/datafusion/datafusion/datafusion/proto/src/physical_plan/mod.rs:1147
  method try_from_lazy_memory_exec in trait datafusion_proto::physical_plan::PhysicalPlanNodeExt in /home/runner/work/datafusion/datafusion/datafusion/proto/src/physical_plan/mod.rs:1147

     Summary semver requires new minor version: 0 major and 1 minor checks failed
    Finished [ 105.702s] datafusion-proto

@github-actions github-actions Bot added the auto detected api change Auto detected API change label Oct 2, 2026
@codecov-commenter

codecov-commenter commented Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.55224% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.91%. Comparing base (cf4869c) to head (3a91993).
⚠️ Report is 28 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/functions-table/src/generate_series.rs 82.71% 11 Missing and 3 partials ⚠️
...usion/functions-table/src/generate_series/proto.rs 91.78% 2 Missing and 4 partials ⚠️
datafusion/physical-plan/src/memory.rs 95.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #25765      +/-   ##
==========================================
+ Coverage   82.84%   82.91%   +0.06%     
==========================================
  Files        1151     1155       +4     
  Lines      455961   457653    +1692     
  Branches   455961   457653    +1692     
==========================================
+ Hits       377760   379460    +1700     
+ Misses      55245    55186      -59     
- Partials    22956    23007      +51     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@QinXi-ai
QinXi-ai marked this pull request as ready for review October 2, 2026 08:58

@kosiew kosiew left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@QinXi-ai,

Thanks for this refactor. Moving generate-series serialization into the generator hooks simplifies the central protobuf code while preserving the existing wire format, execution behavior, and extension fallback.

I found no blocking correctness issues, so I'm approving with two optional suggestions. One compatibility concern worth highlighting is that removing four methods from PhysicalPlanNodeExt breaks downstream Rust callers. I'd prefer deprecated forwarding methods Please document the migration path.

/// Returns a new instance with the state reset.
fn reset_state(&self) -> Arc<RwLock<dyn LazyBatchGenerator>>;

/// Encode this generator using the built-in generate-series wire format.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could we clarify the contract for this hook? Returning Some(node) means the plan will be reconstructed as a built-in series generator, so its rows, batch boundaries, and schema should match the original generator after reset. It would also help to mention that Err stops serialization, while only Ok(None) allows extension fallback.

}

#[test]
fn lazy_memory_dispatches_to_generator_hook() -> Result<()> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could we optionally add a custom generator that delegates execution and reset to a real series, then compare its rows, batch sizes, and schema before and after serialization? The current HookGenerator returns no rows even though its payload represents [3, 2, 1], so this would help demonstrate the execution contract for custom generators.

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

Labels

auto detected api change Auto detected API change development-process Related to development process of DataFusion functions Changes to functions implementation physical-plan Changes to the physical-plan crate proto Related to proto crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants