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

Remove asyncio.run_in_executor() calls and make scheduler cancel() async #9664

Merged
merged 1 commit into from
Jan 13, 2025

Conversation

jonathan-eq
Copy link
Contributor

@jonathan-eq jonathan-eq commented Jan 6, 2025

Issue
Resolves #9307

Approach
The commit in this PR removes the asyncio.run_in_executor() calls, and runs the methods directly instead.
It also makes the signal_cancel, and cancel methods async/await instead of asyncio.run_coroutine_threadsafe().

(Screenshot of new behavior in GUI if applicable)

  • PR title captures the intent of the changes, and is fitting for release notes.
  • Added appropriate release note label
  • Commit history is consistent and clean, in line with the contribution guidelines.
  • Make sure unit tests pass locally after every commit (git rebase -i main --exec 'pytest tests/ert/unit_tests -n logical -m "not integration_test"')

When applicable

  • When there are user facing changes: Updated documentation
  • New behavior or changes to existing untested code: Ensured that unit tests are added (See Ground Rules).
  • Large PR: Prepare changes in small commits for more convenient review
  • Bug fix: Add regression test for the bug
  • Bug fix: Create Backport PR to latest release

@jonathan-eq jonathan-eq added the release-notes:bug-fix Automatically categorise as bug fix in release notes label Jan 6, 2025
@jonathan-eq jonathan-eq marked this pull request as ready for review January 6, 2025 13:14
@codecov-commenter
Copy link

codecov-commenter commented Jan 6, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 91.72%. Comparing base (7127528) to head (4c5af60).
Report is 10 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9664      +/-   ##
==========================================
- Coverage   91.73%   91.72%   -0.01%     
==========================================
  Files         426      426              
  Lines       26518    26526       +8     
==========================================
+ Hits        24326    24331       +5     
- Misses       2192     2195       +3     
Flag Coverage Δ
cli-tests 39.73% <72.72%> (+0.02%) ⬆️
everest-models-test 34.09% <54.54%> (-0.13%) ⬇️
gui-tests 74.27% <54.54%> (+2.32%) ⬆️
integration-test 37.92% <54.54%> (-0.13%) ⬇️
performance-tests 51.52% <54.54%> (-0.13%) ⬇️
test 39.46% <54.54%> (-0.13%) ⬇️
unit-tests 74.21% <100.00%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

Copy link

codspeed-hq bot commented Jan 6, 2025

CodSpeed Performance Report

Merging #9664 will not alter performance

Comparing jonathan-eq:fix-bugs2 (4c5af60) with main (7b2bedf)

Summary

✅ 24 untouched benchmarks

Copy link
Contributor

@eivindjahren eivindjahren left a comment

Choose a reason for hiding this comment

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

This seems to put the application in a bad state in case the condition is ever false.

@jonathan-eq
Copy link
Contributor Author

I was unable to reproduce this with asyncio, but it was possible when using lower level ThreadPoolExecutor. https://superfastpython.com/threadpoolexecutor-shutdown/

event,
iteration,
)
if asyncio.get_running_loop().is_running():
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, this I think is redundant as this should be always true as this runs inside asyncio.run(...)
I would recommend instead:

loop = asyncio.get_running_loop()
if loop.is_closed():
   self.send_snapshot_event(event, iteration)
else:
   await asyncio.get_running_loop().run_in_executor(
    None,
    self.send_snapshot_event,
    event,
    iteration,
)

@xjules
Copy link
Contributor

xjules commented Jan 9, 2025

Maybe it helps if this evaluator function will become async:

    def _signal_cancel(self) -> None:

Currently we call it directly from async context.

@eivindjahren
Copy link
Contributor

Maybe it helps if this evaluator function will become async:

    def _signal_cancel(self) -> None:

Currently we call it directly from async context.

So I think we need to reproduce before anything else, as it is, I am not sure whether this is worse or better.

@jonathan-eq jonathan-eq force-pushed the fix-bugs2 branch 2 times, most recently from c330a34 to 86787bd Compare January 9, 2025 13:25
@@ -333,8 +333,7 @@ def _signal_cancel(self) -> None:
"""
if self._ensemble.cancellable:
logger.debug("Cancelling current ensemble")
assert self._loop is not None
self._loop.run_in_executor(None, self._ensemble.cancel)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we remove self._loop attribute as well?

iteration,
)

self.send_snapshot_event(event, iteration)
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's test this first on bigpoly with 400 realizations

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tested with 200 realizations, and there was no difference. I think it should be sufficient

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tested with 400 realization, and it was the same

@jonathan-eq jonathan-eq changed the title Make sure event loop is running before running coroutines in executor Remove asyncio.run_in_executor() calls Jan 10, 2025
This commit makes the methods async, so we can await them instead
fire-and-forgetting them through asyncio.run_coroutine_threadsafe.
Copy link
Contributor

@xjules xjules left a comment

Choose a reason for hiding this comment

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

Ok. Let's merge it then! Good job! 🚀

@jonathan-eq jonathan-eq changed the title Remove asyncio.run_in_executor() calls Remove asyncio.run_in_executor() calls and make scheduler cancel() async Jan 13, 2025
@jonathan-eq jonathan-eq enabled auto-merge (rebase) January 13, 2025 08:45
@jonathan-eq jonathan-eq merged commit 94439f5 into equinor:main Jan 13, 2025
43 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-notes:bug-fix Automatically categorise as bug fix in release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cancelling jobs could result in "unexpected error: cannot schedule new futures after shutdown"
4 participants