[spec] Phased updates groups

Internal ID FO067
Title Phased updates group
Status Pending Review
Authors @juliank
Type Standard
Created 2022-06-23


Phased updates roll out updates in stages. Sometimes two updates depend on each other, like shim-signed and grub2-signed, or evolution and evolution-data-server. At the moment, a machine might be eligible for one of them and then try to remove the other. In general, if both updates are seeded at e.g. 10% the probability of installing both of them would be less than 10% which is not what we want.


Updates currently end up with the wrong probability if split across source packages, and might cause solver breakage, leading to less systems getting the phased update and in the worst case, broken systems.


Currently APT uses the template ${Source-Name}-${Source-Version}-${Machine-Id} to seed a random number generator to determine a percentage range. To address the issue at hand, we will make the first part configurable with the introduction of a Phased-Update-Group field in the Packages file alongside the Phased-Update-Percentage field.

This must be a list of source packages and their versions, such as

Phased-Update-Group: package (= ${package:Source-Version}),
                     other-package (= ${other-package:Source-Version})

However, clients must not parse the value, but only use it to replace the initial part of the random number generator seed.

All binaries produced by the listed source package versions must have the same value for Phased-Update-Percentage and Phased-Update-Group, optimally across all pockets if they share the same versions (or no percentage).

Further Information

The first iteration of the specification specified that APT would parse that string, and then calculate the minimum percentage of all packages listed in there. This is technically cleaner, but also requires complex slow code on the client side. For the sake of simplicity, shifting the burden of consistency to the server simplifies things.

One problematic bit is when users have -updates pockets enabled for two releases, which sometimes happen (but is not exactly supported), and the same version is available in multiple releases (e.g. shim is binary copied): The values for Phased-Update-Group and Phased-Update-Percentage may end up different. This can cause inconsistent phasing depending on the order of the sources.list entry as only the first entry for a version wins, so if you have focal-updates and jammy-updates and you have shim 1 and shim-signed 1~20.04 and 1~22.04, shim would phase using the focal values, whereas shim-signed would phase using the jammy values.

Mismatches like this would again cause a reduction in probability of both packages being phased at once and might cause the updates to be uninstallable. The behavior will be more similar to what it is now, without such grouping.

APT provides the guarantee in such a case that the package that’s uninstallable will not be removed, as it is marked “keep” and “protected” in the solver. It’s of course always possible the solver (which is greedy and organically grown over 25 years) gets confused or has a bug and breaks (e.g. LP: #1990684.

1 Like

My strawman for this was Phased-Update-Key because for the use cases we know about, there is always some “base” package that all other packages in the group should coordinate with.

If you have more than one source package listed in Phased-Update-Group, these will hash differently. What is the intended behavior if one hashes to say “yes, install this update” and one hashes to say “no, do not install this update”?

Where will the Phased-Update-Group group appear? In Packages? If so, then against which packages? One of them? All of them? What happens if they mismatch across the group?

No, we will hash using the group string instead of the individual ${Source-Name}-${Source-Version} string. So you’ll end up seeding the RNG with package (= ${package:Source-Version}), other-package (= ${other-package:Source-Version})-${Machine-ID} instead of ${Source-Name}-${Source-Version}-${Machine-ID} so that all packages are reflected. Well I guess spaces should be removed.

In theory we can support any random string in there, but this produces a uniform syntax with the rest of the archive and is readable.

Surprisingly perhaps this is not true. That is the usual case, but e.g. if you look at shim-signed history you’ll find there are uploads like 1.40.3 that do not have a corresponding shim upload. So we can’t statically declare in shim-signed packages that it should follow whatever shim does.

So this has to be determined at the server side when it looks at the updates pocket, it needs to check for each SRU if it depends on another SRU and cannot be satisfied by the release pocket, (given that launchpad does not preserve previous SRUs, you need to check that). ddand then group them together and assign the same percentage to both - it needs to track the SRUs as a group. So it might as well add the field to both packages listing both packages rather than declare that shim-signed has the same prefix for the RNG as shim.

Also another case may be that the new shim needs the new grub, then you end up needing to group grub2-unsigned, grub2-signed, shim, shim-signed into one group. Potentially grub2 itself too if the common bits need adjusting for new grub2-unsigned versions.

I don’t know if you have e.g. focal-updates and jammy-updates in your sources.list and you get a shim + shim-signed update it gets a bit weird, because focal-updates will want to phase differently from jammy-updates due to different shim-signed versions. Though in practice, as long as both packages are in both pockets, you’ll get consistent phasing values.

OK I added the field name to

All binaries produced by the listed source package versions must have the same value for Phased-Update-Percentage and Phased-Update-Group, optimally across all pockets (or no percentage).

Mismatch is unspecified behavior. If there’s dependencies between the two you end up reducing the probability because the probability to install two phased updates with independent probability 10% is 10%*10%=1%. You end up with the same levels of problems you have now.

I have added some more details to further information.

1 Like