Browse Source

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

Signed-off-by: Niels Thykier <>
Niels Thykier 11 months ago
No known key found for this signature in database GPG Key ID: A65B78DBE67C7AAC
2 changed files with 28 additions and 5 deletions
  1. +8
  2. +20

+ 8
- 4
britney2/installability/ View File

@@ -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]

+ 20
- 1
britney2/ View File

@@ -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]
return None

def write_nuninst(filename, nuninst):
"""Write the non-installable report