From 0d5ea5eb8c0276e48f1394f233e1441ab87dcfbc Mon Sep 17 00:00:00 2001 From: Niels Thykier Date: Sun, 10 Nov 2019 23:08:47 +0000 Subject: [PATCH] Improve cache invalidation of (pseudo-)essential set If a new pseudo-essential package was added to testing *and* it is mutually-exclusive with an existing member, britney would incorrectly flag it as uninstallable (unless another change triggered a cache invalidation of the pseudo-essential set). With this commit, the cache invalidation is now done based on whether we add something that *might* be in the (effective) pseudo-essential set. It is an overapproximation as there are cases where the invalidation is unnecessary, but at the moment it is not a performance concern. Closes: https://bugs.debian.org/944190 Signed-off-by: Niels Thykier --- britney2/installability/tester.py | 12 ++++++++---- britney2/utils.py | 21 ++++++++++++++++++++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/britney2/installability/tester.py b/britney2/installability/tester.py index 5ac6ef2..4687cc0 100644 --- a/britney2/installability/tester.py +++ b/britney2/installability/tester.py @@ -17,7 +17,7 @@ from functools import partial import logging from itertools import chain, filterfalse -from britney2.utils import iter_except +from britney2.utils import iter_except, add_transitive_dependencies_flatten class InstallabilityTester(object): @@ -52,12 +52,16 @@ class InstallabilityTester(object): # are essential and packages that will always follow. # # It may not be a complete essential set, since alternatives - # are not always resolved. Noticably cases like "awk" may be + # are not always resolved. Noticeably cases like "awk" may be # left out (since it could be either gawk, mawk or # original-awk) unless something in this sets depends strictly # on one of them self._cache_ess = {} + essential_w_transitive_deps = set(universe.essential_packages) + add_transitive_dependencies_flatten(universe, essential_w_transitive_deps) + self._cache_essential_transitive_dependencies = essential_w_transitive_deps + def compute_installability(self): """Computes the installability of all the packages in the suite @@ -137,8 +141,8 @@ class InstallabilityTester(object): # Re-add broken packages as some of them may now be installable self._suite_contents |= self._cache_broken self._cache_broken = set() - if pkg_id in self._universe.essential_packages and pkg_id.architecture in self._cache_ess: - # Adds new essential => "pseudo-essential" set needs to be + if pkg_id in self._cache_essential_transitive_dependencies and pkg_id.architecture in self._cache_ess: + # Adds new possibly pseudo-essential => "pseudo-essential" set needs to be # recomputed del self._cache_ess[pkg_id.architecture] diff --git a/britney2/utils.py b/britney2/utils.py index a72ff5f..10d352c 100644 --- a/britney2/utils.py +++ b/britney2/utils.py @@ -30,7 +30,7 @@ import time from collections import defaultdict from datetime import datetime from functools import partial -from itertools import filterfalse +from itertools import filterfalse, chain import yaml @@ -127,6 +127,25 @@ def compute_reverse_tree(pkg_universe, affected): return None +def add_transitive_dependencies_flatten(pkg_universe, initial_set): + """Find and include all transitive dependencies + + This method updates the initial_set parameter to include all transitive + dependencies. The first argument is an instance of the BinaryPackageUniverse + and the second argument are a set of BinaryPackageId. + + The set of initial packages will be updated in place and must + therefore be mutable. + """ + remain = list(initial_set) + while remain: + pkg_id = remain.pop() + new_pkg_ids = [x for x in chain.from_iterable(pkg_universe.dependencies_of(pkg_id)) if x not in initial_set] + initial_set.update(new_pkg_ids) + remain.extend(new_pkg_ids) + return None + + def write_nuninst(filename, nuninst): """Write the non-installable report