Skip to content

Blog post on query cancellation #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 30, 2025
Merged

Blog post on query cancellation #75

merged 14 commits into from
Jun 30, 2025

Conversation

pepijnve
Copy link
Contributor

@pepijnve pepijnve commented Jun 15, 2025

Blog post describing the problem solved by and the changes made in 16398

@pepijnve
Copy link
Contributor Author

I've marked this as draft for now. I think I have the narrative arc I was going for in place, but the text probably still needs some editing work.

Copy link

@djanderson djanderson left a comment

Choose a reason for hiding this comment

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

I found this write-up very helpful and clear. Just a couple small typo suggestions and 1 clarifying question:

When calling `Receiver::recv` for one of these channels, a unit of Tokio task budget is consumed.
As a consequence, query plans that made use of exchange-like operators were already mostly cancelable.

### Depleting The Budget

Choose a reason for hiding this comment

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

I was looking for docs on poll_proceed and made_progress and I'm wondering if those functions have been renamed to has_budget_remaining and consume_budget recently? https://docs.rs/tokio/latest/tokio/task/coop/index.html#functions

Or, am I looking in the wrong place?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

poll_proceed/made_progress are the functions Tokio itself uses to consume budget in resources like Channel. consume_budget is implemented using those two as well.

At the moment they're still pub(crate) in tokio. I'm working on a PR to make them accessible at tokio-rs/tokio#7405. I wrote this post under the assumption that this will get merged. We shouldn't publish this post until that actually lands.

The DataFusion PR apache/datafusion#16398 currently contains three variants: one with the manual counter, one using has_budget_remaining/consume_budget as approximation, and one using poll_proceed/made_progress which doesn't compile just yet.

Choose a reason for hiding this comment

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

Thanks for the context!

@djanderson
Copy link

One other thing I'm curious about. This write-up discusses the change in terms of enabling long-running tasks to be cancelled, but would making CPU-intensive exec blocks more cooperative also help alleviate blocking IO on the main runtime if users don't set up a separate runtime ala apache/datafusion#16331? That could be a really nice benefit besides cancelability of this, if so. @alamb?

@pepijnve
Copy link
Contributor Author

but would making CPU-intensive exec blocks more cooperative also help alleviate blocking IO on the main runtime if users don't set up a separate runtime

It should make some more room for the IO indeed. Cancellation is just one facet of the more general problem of fairness in a cooperatively scheduled runtime. Yielding every now and then helps to ensure every concurrent task gets a fair share of time to run.

@pepijnve
Copy link
Contributor Author

pepijnve commented Jun 17, 2025

A colleague of mine suggested to add some more diagrams. I know UML is out of fashion, but here's a sketch of what's actually happening when you imagine Tokio's budget is initialized to 1. Would something like this be a useful addition, or trying to cram too much information in a small space?

Sequence diagram of one Tokio tick

@alamb
Copy link
Contributor

alamb commented Jun 17, 2025

A colleague of mine suggested to add some more diagrams. I know UML is out of fashion, but here's a sketch of what's actually happening when you imagine Tokio's budget is initialized to 1. Would something like this be a useful addition, or trying to cram too much information in a small space?

I think it is hugely helpful (we just shouldn't call it UML to avoid getting painted with the "old folks brush" 😆 )

@alamb
Copy link
Contributor

alamb commented Jun 17, 2025

FYI @ozankabak and @zhuqi-lucas

@@ -0,0 +1,353 @@
---
Copy link
Contributor

Choose a reason for hiding this comment

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

I took the liberty of pushing this "front matter" and ASF header to this post

I tried to render the blog locally to preview but found it wasn't in the blog list without this

Screenshot 2025-06-17 at 12 49 31 PM

Copy link
Contributor Author

@pepijnve pepijnve Jun 17, 2025

Choose a reason for hiding this comment

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

Since Datadobi is paying for my time while I work on this stuff, would it be possible/allowed to add a small note mentioning that?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes of course -- we have done this on other blogs as well

Something like "Thank you to Datadobi for supporting this work" would be very much following the same pattern

Copy link
Contributor

@alamb alamb left a comment

Choose a reason for hiding this comment

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

This looks great to me -- thank you @pepijnve

I am approving as I think the post could be posted as is, but of course we can also make it better.

I left a bunch of suggestions, but I also happy to help implement them if you prefer. Sorry I got carried away but I really like this post and thank you so much for writing it

## The Challenge of Cancelling Long-Running Queries

Have you ever tried to cancel a query that just wouldn't stop?
In this post, we'll take a look at why that can happen in DataFusion and what the community did to resolve the problem in depth.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would also help here in the intro to emphasize other learnings a potential reader might get (to convince them to read more):

  1. Review the async model / tokio tasks
  2. Learn how DataFusion uses that model for CPU bound work
  3. Learn how query engines in general, and DataFusion specifically implement cooperative scheduling and cancelling

So manybe something like

Suggested change
In this post, we'll take a look at why that can happen in DataFusion and what the community did to resolve the problem in depth.
In this post, we'll review how Rust's `async` model works, and how DataFusion uses that model for CPU intensive tasks and how they implement cancellation. Then we'll review some cases where queries could not be cancelled in DataFusion and what the community did to resolve the problem.


### Understanding Rust's Async Model

To really understand the cancellation problem you need to be somewhat familiar with Rust's asynchronous programming model.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
To really understand the cancellation problem you need to be somewhat familiar with Rust's asynchronous programming model.
DataFusion, somewhat unconventionally, [uses the Rust async system and the tokio task pool](https://docs.rs/datafusion/latest/datafusion/#thread-scheduling-cpu--io-thread-pools-and-tokio-runtimes) for CPU intensive processing.
To really understand the the cancellation problem you need to understand the DataFusion execution model which and thus with Rust's asynchronous programming model.


#### Futures Are Inert

Rust's asynchronous programming model is built around the `Future<T>` trait.
Copy link
Contributor

Choose a reason for hiding this comment

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

@alamb
Copy link
Contributor

alamb commented Jun 20, 2025

One other thing I'm curious about. This write-up discusses the change in terms of enabling long-running tasks to be cancelled, but would making CPU-intensive exec blocks more cooperative also help alleviate blocking IO on the main runtime if users don't set up a separate runtime ala apache/datafusion#16331? That could be a really nice benefit besides cancelability of this, if so. @alamb?

@djanderson it may help, but I think even if the CPU intensive tasks yielded more frequently we would still need a separate runtime to avoid increasing io latnecy

@alamb
Copy link
Contributor

alamb commented Jun 20, 2025

@pepijnve I pushed an Acknowedgement and "About DataFusion" sections

Screenshot 2025-06-20 at 7 41 54 AM

@alamb
Copy link
Contributor

alamb commented Jun 23, 2025

Hi @pepijnve -- I wonder if you plan to work on this post in the near term? If not I will try and find time to add some diagrams / etc to help it get ready to publish (as I think it is important content)

Thanks again for the work so far

@pepijnve
Copy link
Contributor Author

I was not planning on changing it substantially anymore. I was thinking of maybe rereading the text with a fresh pair of eyes and editing a sentence here or there, but that's it. Need to switch gears from open source contribution mode to building our own product for a bit.

@alamb
Copy link
Contributor

alamb commented Jun 23, 2025

I was not planning on changing it substantially anymore. I was thinking of maybe rereading the text with a fresh pair of eyes and editing a sentence here or there, but that's it. Need to switch gears from open source contribution mode to building our own product for a bit.

Makes sense -- thank you -- I'll do the same (with fresh eyes) and polish it up and get it ready to merge. Thank you

@alamb alamb marked this pull request as ready for review June 23, 2025 20:02
@alamb
Copy link
Contributor

alamb commented Jun 24, 2025

I pushed the images to this post, updated the publish date to June 30 (next Monday), and started doing some wordsmiting

Screenshot 2025-06-24 at 10 52 01 AM

I'll finish up my polishing today and then maybe try and highlight this draft in case others want a chance to review it. It is really neat. Thanks again @pepijnve

@alamb alamb assigned alamb and unassigned alamb Jun 24, 2025
Copy link
Contributor

@comphead comphead left a comment

Choose a reason for hiding this comment

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

Thanks @pepijnve it is very nice structured

@comphead
Copy link
Contributor

I was wondering if it makes sense to add challenges why async is challenging to cancel on low level but it probably would be noisy. But just in case this article shed the light on cancellation challenges including Rust ecosystem https://without.boats/blog/asynchronous-clean-up/

@alamb
Copy link
Contributor

alamb commented Jun 24, 2025

I was wondering if it makes sense to add challenges why async is challenging to cancel on low level but it probably would be noisy. But just in case this article shed the light on cancellation challenges including Rust ecosystem https://without.boats/blog/asynchronous-clean-up/

Man that post somewhat blew my mind (I only understood about 50% of it).

@alamb
Copy link
Contributor

alamb commented Jun 24, 2025

In my opinion this article does a pretty good job explaining the issues with cancellation, but it doesn't talk about async destructors which I agree are probably best left out of scope for this article

@pepijnve
Copy link
Contributor Author

https://without.boats/blog/asynchronous-clean-up/

Wait till you read their work on Pin/Unpin.

@pepijnve
Copy link
Contributor Author

I was wondering if it makes sense to add challenges why async is challenging to cancel on low level but it probably would be noisy. But just in case this article shed the light on cancellation challenges including Rust ecosystem https://without.boats/blog/asynchronous-clean-up/

Thanks for the pointer. I think this would be useful as hyperlink in the intro where we're trying to say "cancelling async ain't that simple". Another useful blog post to link to might be https://cybernetist.com/2024/04/19/rust-tokio-task-cancellation-patterns/

@pepijnve
Copy link
Contributor Author

In my opinion this article does a pretty good job explaining the issues with cancellation, but it doesn't talk about async destructors which I agree are probably best left out of scope for this article

If we do link to it I would link directly to https://without.boats/blog/asynchronous-clean-up/#cancellation rather than the top of the article. The idea of a 'spectrum of cooperativeness' when it comes to cancellation is a very elegant way of describing the problem. DataFusion tasks were a mix of implicitly cooperative (the Receiver users) and non-cooperative. The work that was done tries to move all tasks towards the cooperative end of the spectrum.

@pepijnve
Copy link
Contributor Author

@alamb I've done some work to make the diagrams a bit prettier using Excalidraw. Massaged the text a bit further. Tried to improve the flow from one section to the next.

Copy link
Contributor

@ozankabak ozankabak left a comment

Choose a reason for hiding this comment

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

LGTM, thank you @pepijnve

@alamb
Copy link
Contributor

alamb commented Jun 26, 2025

😍

Screenshot 2025-06-26 at 9 17 30 AM

@alamb
Copy link
Contributor

alamb commented Jun 26, 2025

I went through the draft again and I think it is looking REALLY nice -- something to be proud of I think

I pushed one more commit with some wordsmithing obsession and new links ab0094c

I can't want to publish this. It is going to be so good

@alamb alamb merged commit 8713024 into apache:main Jun 30, 2025
1 check passed
@alamb
Copy link
Contributor

alamb commented Jun 30, 2025

The blog is live!
https://datafusion.apache.org/blog/2025/06/30/cancellation/

@comphead
Copy link
Contributor

The blog is live! https://datafusion.apache.org/blog/2025/06/30/cancellation/

Published on Datafusion Linkedin resource

@kevinjqliu
Copy link
Contributor

kevinjqliu commented Jul 5, 2025

Just read the post. Coming back here to thank everyone for the contribution! I always learn so much from these.

I think it would be great to have a comment section for the blog posts. I opened #80 to discuss this idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Blog post about DataFusion Async / Stream execution model / cancellation
6 participants