Skip to content

Put libjuju asyncio on a background thread#480

Merged
ChrisMacNaughton merged 10 commits into
openstack-charmers:masterfrom
ajkavanagh:threaded-libjuju
Jul 6, 2022
Merged

Put libjuju asyncio on a background thread#480
ChrisMacNaughton merged 10 commits into
openstack-charmers:masterfrom
ajkavanagh:threaded-libjuju

Conversation

@ajkavanagh
Copy link
Copy Markdown
Collaborator

This PR is a piece of work to:

  1. Put the asyncio libjuju API interface code on a background thread.
  2. Re work sync-wrapper to inject calls into the background thread's asyncio event loop, allow it to process, and retrieve the results.
  3. Re-work run_in_model and related functions so that a libjuju Model is maintained so that the libjuju objects attached to models remaining 'current' and can be used between sync calls (from the front thread) for a more consistent and functional sync code experience.

The background asyncio thread is kept running to allow libjuju data structures to be updated independently of the front thread. The GIL enables thread-safe data sharing between the threads so that libjuju objects can be read/updated from the sync code thread.

This should solve a bunch of errors including disconnects, slow operations (continuing to re-connect to the model multiple times during a run), memory issues, etc.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2022

Codecov Report

Merging #480 (23d061d) into master (1ec92a9) will decrease coverage by 0.15%.
The diff coverage is 80.44%.

@@            Coverage Diff             @@
##           master     #480      +/-   ##
==========================================
- Coverage   89.00%   88.85%   -0.16%     
==========================================
  Files          44       44              
  Lines        4429     4548     +119     
==========================================
+ Hits         3942     4041      +99     
- Misses        487      507      +20     
Impacted Files Coverage Δ
zaza/charm_lifecycle/before_deploy.py 78.37% <0.00%> (-4.48%) ⬇️
zaza/charm_lifecycle/configure.py 70.73% <9.09%> (-3.63%) ⬇️
zaza/charm_lifecycle/deploy.py 92.02% <16.66%> (-1.69%) ⬇️
zaza/charm_lifecycle/prepare.py 77.14% <16.66%> (-6.73%) ⬇️
zaza/charm_lifecycle/test.py 63.41% <16.66%> (-1.59%) ⬇️
zaza/charm_lifecycle/destroy.py 77.77% <20.00%> (-9.18%) ⬇️
zaza/charm_lifecycle/func_test_runner.py 72.94% <54.54%> (+0.10%) ⬆️
zaza/model.py 86.82% <87.62%> (+0.38%) ⬆️
zaza/__init__.py 86.59% <92.75%> (+9.32%) ⬆️
zaza/utilities/__init__.py 98.14% <100.00%> (+0.58%) ⬆️
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1ec92a9...23d061d. Read the comment docs.

@ajkavanagh ajkavanagh marked this pull request as ready for review May 4, 2022 20:07
@ajkavanagh
Copy link
Copy Markdown
Collaborator Author

Okay, I've got some extra tests to write/coverage to increase, but let's try to get the review ball going. The "main" change is in zaza/init.py to the sync_wrapper() and associated functions to run the async tasks in a separate thread to the main sync test thread. The rest of the changes to the library are effectively a knock-on effect of these. I'll also do some 'no code' changes to a few charms to test it out to make sure that it works. I'll post links here once I've got them set up.

Copy link
Copy Markdown
Collaborator

@ChrisMacNaughton ChrisMacNaughton left a comment

Choose a reason for hiding this comment

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

Looks generally good, have a nitpick and a question

Comment thread test.py
try:
print(zaza.model.get_units('ubuntu'))
finally:
zaza.clean_up_libjuju_thread()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this file still be here, or is it used for local validation of the changes?

Comment thread zaza/__init__.py Outdated
If the tasks spawns other future (tasks) then these are also cleaned up
after each step is performed.
# Timeout for loop to close. This is set to 30 seconds. If there is a non
# async all in the async thread then it could stall the thread for more than 30
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

small typo: s/async all/async/call

This patch updates the sync_wrapper() function to fully be able to run
async functions that interact with libjuju in a background thread to be
able to leave the async coroutines running that libjuju launches when
connecting to a controllers.

The rest of the patch is updating the library, and tests, to work with
the updated sync_wrapper().
It is necessary to call `zaza.clean_up_libjuju_thread()` in all of the
main functions that setup.py creates as otherwise the script can't exit
as the libjuju loop may be running in that thread.
1. async subprocess calls need a child watcher to be able to work.
2. if an error occurs that raises an uncaught exception, then
   the process will hang unless the event loop is properly
   terminated. Add in try: finally: handlers to ensure that the
   event loop is cleaned up for all the CLI calls.
@ajkavanagh
Copy link
Copy Markdown
Collaborator Author

The func test error is unrelated and should be fixed separately.

I've tested this on the master branches of charm-keystone and vault-charm which both use a good spectrum of features of the zaza library.

This needs to be landed with: zaza-openstack-tests#763 which changes that library into a proper namespaced library. This is because this change makes extensive changes to the zaza/__init__.py file.

Copy link
Copy Markdown
Collaborator

@ChrisMacNaughton ChrisMacNaughton 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 to me, just found one more typo, and there's a pep8 failure

Comment thread zaza/__init__.py Outdated
The thread that contains the runtime for libjuju asyncio futures.

zaza runs libjuju in a background thread so that it can make progress as
needed with the model(s) that are connect. The sync functions run in the
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

s/that are connect./that are connected./

@ajkavanagh
Copy link
Copy Markdown
Collaborator Author

Validated using charm-vault: https://review.opendev.org/c/openstack/charm-vault/+/841106 :)

@ChrisMacNaughton ChrisMacNaughton merged commit b393baa into openstack-charmers:master Jul 6, 2022
@ajkavanagh ajkavanagh deleted the threaded-libjuju branch July 8, 2022 10:19
coreycb pushed a commit to coreycb/zaza that referenced this pull request Oct 17, 2023
…aml_mellon-tests

Add multi-backend testing for Keystone SAML Mellon
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