Skip to content

feat: add extension to support DataFrame::checkpoint() - #1993

Open
sandugood wants to merge 9 commits into
apache:mainfrom
sandugood:feat/df-checkpoint
Open

feat: add extension to support DataFrame::checkpoint()#1993
sandugood wants to merge 9 commits into
apache:mainfrom
sandugood:feat/df-checkpoint

Conversation

@sandugood

@sandugood sandugood commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Closes #1937 .

Rationale for this change

Before this change, there was no checkpoint() method for the DataFrame in Ballista (where we break lineage of the plan and store intermediate results to specified location)

What changes are included in this PR?

Introducing new DataFrameExt trait in the client, also introducing both checkpoint() and checkpoint_lazy() there.

A new entry to the BallistaConfig was added as well. When trying to call checkpoint() or checkpoint_lazy() without the config entry set user will get an error.

Also added new logic to a separate file (checkpoint.rs) on the Scheduler's side, which is being used during planning in the submit_plan()

Are there any user-facing changes?

New methods introduced.

checkpoint() method is eager. It breaks lineage and doesn't wait for an action (i.e: show()) => so it executes immediately
checkpoint_lazy() method is lazy. It breaks lineage but does wait for an action

@sandugood sandugood changed the title feat: add extension for DataFrame::checkpoint() feat: add extension to support DataFrame::checkpoint() Jul 11, 2026
@sandugood

Copy link
Copy Markdown
Contributor Author

Is that OK that we'll hold the DataFrameExt in the client/src/extension.rs or should we move it to a separate file?

@milenkovicm milenkovicm 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.

sorry for very late review @sandugood I'm catching up with backlog after summer break.
the implementation does make sense, but it diverges from spark implementation as ballista checkpoint will materialise dag. perhaps if it is not too complicated we should consider implementing same behaviour like spark. wdyt?

Comment thread ballista/client/src/extension.rs
@sandugood
sandugood force-pushed the feat/df-checkpoint branch from bb83b59 to d88bb6c Compare August 7, 2026 12:18
@github-actions github-actions Bot removed the python label Aug 7, 2026
@sandugood

Copy link
Copy Markdown
Contributor Author

I think, same as .cache() this needs a physical operator (something like CheckpointExec), an extension planner, a physical codec entry
Right now it seems OK with logical planning. wdyt @milenkovicm

@milenkovicm

Copy link
Copy Markdown
Contributor

I believe you could get away without physical node if you split into two jobs at planning

@sandugood

sandugood commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

I believe you could get away without physical node if you split into two jobs at planning

Tried to introduce this concept and it worked out nicely
Added unit tests and an example as well. thanks @milenkovicm

For me it seemed to introduce new CheckpointMaterializer trait as a reasonable thing

@sandugood

sandugood commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Also, I think it is good to have an example with remote-checkpoint, so user could see the output of it in the ballista-tui

image

During this planning I have:

  1. Read .parquet and applied a simple filter. Then called .checkpoint_lazy(), which split the plan into two jobs
  2. Then reused the same DF, which didn't read from the original .parquet, but rather from the checkpointed part

Hence having 3 separated jobs in the TUI

@milenkovicm

Copy link
Copy Markdown
Contributor

thanks @sandugood will have a look

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Aug 12, 2026

@milenkovicm milenkovicm 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.

thanks @sandugood, my appologise for slow rewview. i have few comments, please have a look when you get chance.

I'm still not sure if we should provide checkpoint_lazy or checkopint(lazy) i guess second approach would give possibility to users to change from ine to other type withut method change, but lets think about it

}

#[tokio::test]
async fn should_insert_checkpoint_node_without_executing() -> datafusion::error::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.

should_insert_checkpoint_lazy as name?

.await?;
let schema_before = df.schema().clone();

let checkpointed = df.checkpoint_lazy()?;

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.

should we have test to check execution of checkpoint_lazy or ive missed it

let plan = match plan {
SubmitPlan::Logical(logical) if contains_checkpoint(logical) => {
resolved =
SubmitPlan::Logical(resolve_checkpoints(&ctx, logical, self).await?);

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.

if im not mistaken, this will block job submission (of reading part) until checkpoint creation finishes ? can we create two jobs and link them using job notification subscriber

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

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add suport for DataFrame.checkpoint()

2 participants