Skip to content
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

Add support for blocking handlers #65

Merged
merged 5 commits into from
Jul 14, 2024
Merged

Conversation

rustworthy
Copy link
Collaborator

@rustworthy rustworthy commented May 20, 2024

Rationale:

We probably should not be discriminating those who have been using the crate for CPU intensive tasks running on the background, e.g. image processing.

We are not adding "fully-fledged" SyncJobRunner trait, just to keep things simple. WorkerBuilder::register_blocking_fn should be considered an escape hatch. Adding SyncJobRunner later on - should this be required - will not be a breaking change.


This change is Reviewable

Copy link

codecov bot commented May 20, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 68.3%. Comparing base (a4832db) to head (22b7d7e).
Report is 4 commits behind head on main.

Additional details and impacted files
Files Coverage Δ
src/worker/builder.rs 100.0% <100.0%> (ø)
src/worker/mod.rs 81.2% <100.0%> (+0.7%) ⬆️

... and 1 file with indirect coverage changes

Copy link
Owner

@jonhoo jonhoo left a comment

Choose a reason for hiding this comment

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

Mostly smaller bits!

src/worker/mod.rs Show resolved Hide resolved
Comment on lines 118 to 120
/// Note that it is not recommended to mix blocking and non-blocking tasks and so if many of your
/// handlers are blocking, you will want to launch a dedicated worker process (at least a separate
/// Tokio Runtime) where only blocking handlers will be used.
Copy link
Owner

Choose a reason for hiding this comment

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

I'm not sure I expect this to be necessary. As long as we're using spawn_blocking, it should be fine to have some async and some sync handlers.

Copy link
Collaborator Author

@rustworthy rustworthy May 26, 2024

Choose a reason for hiding this comment

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

This is my reading of this section and namely this passage:


If your code is CPU-bound and you wish to limit the number of threads used to run it, you should use a separate thread pool dedicated to CPU bound tasks. For example, you could consider using the rayon library for CPU-bound tasks. It is also possible to create an extra Tokio runtime dedicated to CPU-bound tasks, but if you do this, you should be careful that the extra runtime runs only CPU-bound tasks, as IO-bound tasks on that runtime will behave poorly.


So "if many handlers are blocking" they can employ one single runtime and mix blocking handlers with async ones, since we are using spawn_blocking (and so are protecting the cooperative scheduling for async tasks), but we are encouraging them to consider launching a dedicated runtime for their blocking tasks. Or you think this note is redundant?

Copy link
Owner

Choose a reason for hiding this comment

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

I think giving a nudge in this direction is fine, although I think the nudge is currently too strong. I wonder if we just want to say something like:

You can mix and match async and blocking handlers in a single Worker. However, note that there is no active management of the blocking tasks in tokio, and so if you end up with more CPU-intensive blocking handlers executing at the same time than you have cores, the asynchronous handler tasks (and indeed, all tasks) will suffer as a result. If you have a lot of blocking tasks, consider using the standard async job handler and add explicit code to manage the blocking tasks appropriately.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated

src/worker/mod.rs Show resolved Hide resolved
src/worker/mod.rs Outdated Show resolved Hide resolved
tests/real/community.rs Show resolved Hide resolved
let handler = self
.callbacks
.get(job.kind())
.ok_or(Failed::BadJobType(job.kind().to_string()))?;
handler.run(job).await.map_err(Failed::Application)
match handler {
Callback::Async(cb) => cb.run(job).await.map_err(Failed::Application),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am worried that this is not the case for async handlers, panicking in there is not intercepted in the way is now being done for register_blocking_fn. I wonder if might need to use futures::future::FutureExt::catch_unwind for those futures, not sure..

Copy link
Owner

Choose a reason for hiding this comment

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

Let's do that in a follow-up (maybe the tech debt one?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

pinned in there

@rustworthy rustworthy requested a review from jonhoo May 29, 2024 06:58
Copy link
Owner

@jonhoo jonhoo left a comment

Choose a reason for hiding this comment

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

Looks good subject to the one documentation change

@rustworthy rustworthy mentioned this pull request Jul 14, 2024
4 tasks
@rustworthy rustworthy merged commit 0df8659 into main Jul 14, 2024
19 checks passed
@rustworthy rustworthy deleted the chore/support_blocking_handlers branch July 14, 2024 12:38
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.

2 participants