commit: 5f96ef64f9490de08f8b901c539d870d68d374fe Author: Zac Medico <zmedico <AT> gentoo <DOT> org> AuthorDate: Wed Oct 29 03:12:54 2025 +0000 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> CommitDate: Thu Oct 30 02:44:25 2025 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=5f96ef64
portdbapi: aux_get retry for unexpected process returncode Retry for an intermittent unexpected returncode which occurs in CI runs with forkserver (bug 965132). In CI the unexpected returncode tends to be 255 which indicates that the forkserver exited unexpectedly. Bug: https://bugs.gentoo.org/965132 Signed-off-by: Zac Medico <zmedico <AT> gentoo.org> lib/portage/dbapi/porttree.py | 121 +++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/lib/portage/dbapi/porttree.py b/lib/portage/dbapi/porttree.py index 3ca99381c1..93bd612507 100644 --- a/lib/portage/dbapi/porttree.py +++ b/lib/portage/dbapi/porttree.py @@ -1,4 +1,4 @@ -# Copyright 1998-2024 Gentoo Authors +# Copyright 1998-2025 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 __all__ = ["close_portdbapi_caches", "FetchlistDict", "portagetree", "portdbapi"] @@ -47,7 +47,6 @@ import threading import traceback import warnings import errno -import functools import shlex import collections @@ -752,25 +751,54 @@ class portdbapi(dbapi): mydata, ebuild_hash = self._pull_valid_cache(mycpv, myebuild, mylocation) - if mydata is not None: - future = loop.create_future() - self._aux_get_return( - future, - mycpv, - mylist, - myebuild, - ebuild_hash, - mydata, - mylocation, - cache_me, - None, - ) - return future.result() + proc = None + if mydata is None: + if myebuild in self._broken_ebuilds: + raise PortageKeyError(mycpv) + + # Retry for an intermittent unexpected returncode which + # occurs in CI runs with forkserver (bug 965132). In CI + # the unexpected returncode tends to be 255 which indicates + # that the forkserver exited unexpectedly. + tries = 3 + while tries > 0: + tries -= 1 + proc = await self._run_metadata_phase( + mycpv, mylocation, ebuild_hash, loop + ) - if myebuild in self._broken_ebuilds: - raise PortageKeyError(mycpv) + if proc.returncode != os.EX_OK: + if proc.returncode != 1: + writemsg( + _( + "!!! aux_get(): metadata phase for package '%(pkg)s' failed with unexpected returncode %(returncode)s\n" + ) + % {"pkg": mycpv, "returncode": proc.returncode}, + noiselevel=-1, + ) + # Only retry for an unexpected returncode. + if tries > 0: + continue + self._broken_ebuilds.add(myebuild) + raise PortageKeyError(mycpv) + + mydata = proc.metadata + break + + return self._aux_get_return( + mycpv, + mylist, + myebuild, + ebuild_hash, + mydata, + mylocation, + cache_me, + ) + + async def _run_metadata_phase( + self, mycpv, mylocation, ebuild_hash, loop + ) -> EbuildMetadataPhase: - proc = None deallocate_config = None async with contextlib.AsyncExitStack() as stack: try: @@ -800,21 +828,6 @@ class portdbapi(dbapi): deallocate_config=deallocate_config, ) - future = loop.create_future() - proc.addExitListener( - functools.partial( - self._aux_get_return, - future, - mycpv, - mylist, - myebuild, - ebuild_hash, - mydata, - mylocation, - cache_me, - ) - ) - future.add_done_callback(functools.partial(self._aux_get_cancel, proc)) proc.start() finally: @@ -824,21 +837,24 @@ class portdbapi(dbapi): if proc is None or not proc.isAlive(): deallocate_config.done() or deallocate_config.cancel() else: - await deallocate_config + try: + await deallocate_config + except asyncio.CancelledError: + proc.cancel() + raise # After deallocate_config is done, release self._doebuild_settings_lock - # by leaving the stack context, and wait for proc to finish and - # trigger a call to self._aux_get_return. - return await future - - @staticmethod - def _aux_get_cancel(proc, future): - if future.cancelled() and proc.returncode is None: + # by leaving the stack context, and wait for proc to finish. + try: + await proc.async_wait() + except asyncio.CancelledError: proc.cancel() + raise + + return proc def _aux_get_return( self, - future, mycpv, mylist, myebuild, @@ -846,24 +862,7 @@ class portdbapi(dbapi): mydata, mylocation, cache_me, - proc, ): - if future.cancelled(): - return - if proc is not None: - if proc.returncode != os.EX_OK: - if proc.returncode != 1: - writemsg( - _( - "!!! aux_get(): metadata phase for package '%(pkg)s' failed with unexpected returncode %(returncode)s\n" - ) - % {"pkg": mycpv, "returncode": proc.returncode}, - noiselevel=-1, - ) - self._broken_ebuilds.add(myebuild) - future.set_exception(PortageKeyError(mycpv)) - return - mydata = proc.metadata mydata["repository"] = self.repositories.get_name_for_location(mylocation) mydata["_mtime_"] = ebuild_hash.mtime eapi = mydata.get("EAPI") @@ -882,7 +881,7 @@ class portdbapi(dbapi): aux_cache[x] = mydata.get(x, "") self._aux_cache[mycpv] = aux_cache - future.set_result(returnme) + return returnme def getFetchMap(self, mypkg, useflags=None, mytree=None): """
