This blog post has been written to summarize the problem, explain how we got here, offer potential solutions that would work right now, and to set a common ground for a discussion on the devel list about how to address this and similar problems properly.
Summary of what happened
This is about two modules, silver and bat, that depend on another module, libgit. The problem is that libgit changes its ABI (application binary interface) quite often. With every upstream release, in fact. So naturally there are new module streams for it appearing in Fedora. And the silver and bat modules need to consume the new versions in order to keep functioning properly. However, changing the stream of a module they depend on causes trouble.
Right now, DNF just can’t handle it and keeps erroring out. However, with the current design, changing streams should not happen as explained below in the Our intentions and how we got here section.
The real issue is how we solve this problem. Not just to stop the errors, but fix this situation in general. There is a dependency (libgit in this example) that needs an ABI-incompatible upgrade, and that dependency is required by different pieces of software (including silver and bat, but also others) of which only some need that upgrade. Not upgrading it breaks silver and bat. Upgrading it breaks the others. What to do?
In Fedora (and probably other distros as well) we have this concept of compatibility packages. These are packages with different names providing different version of the same library, potentially installable in parallel. So this problem is solvable with RPMs. But what about modules?
You can’t install two streams of a module on the same system at once. At first glance, that seems like the limiting factor here. And it is right now with the way libgit modules are structured. But there are existing approaches for fixing that.
Our intentions and how we got here
Principle 1: Only one stream of a given module can be installed on a system. That is because Modularity doesn’t modify the RPM packages in any way — they install files in standard paths so your system works as expected. The side effect of this is that installing multiple versions of one package is not possible, because the files would collide
In some cases the package is designed with parallel-installation in mind and those cases can be enabled by providing a different module name to each version. (This is one of the possible solutions offered later in this post.)
Principle 2: Updates respect your choices. Running dnf update on a system will not change any module stream. So when you choose a particular module stream, your system is upgraded to the newest versions of RPMs within that stream
But when changes are desired, there are explicit choices to be expressed. The user can explicitly change a module stream using a set of DNF commands when they are ready for the change.
This is quite clear and people have always liked this principle of stability.
But what about dependencies? Can a module change its dependencies (meaning its dependent module streams) during an update? Right now it can not, because that would break this very principle. But there are ways to solve this problem in the “Solutions right now section”, and a discussion about a potential tweak of this principle in the “Potential solutions for the future” section.
Solutions right now
There are existing ways to solve the libgit issue. Let’s have a look at each of them.
Multiple module names to parallel install
As already mentioned, we have the concept of compatibility packages in Fedora that enable us to provide multiple versions of the same library that are potentially parallel-installable. We can create the parallel-installable compatibility packages, and put them into modules having different names which would make them parallel-installable.
So instead of having libgit:0.26, libgit:0.27, and libgit:0.28, we could have something such as libgit-0.26:0.26, libgit-0.27:0.27, and libgit-0.28:0.28. It might not look ideal, but it would work today.
Module Stream Expansion — building one module against multiple streams of another one — would continue to work, because this mechanism allows to expand not just across multiple streams of the same module, but across multiple modules as well by specifying multiple entries under the dependencies field — see the Complex example in the Modularity docs / Defining modules in modulemd.
One stream with compatibility packages
Because libgit is changing so fast — reportedly changing its ABI with each upstream release — there could be just one libgit module containing compatibility packages for all versions. This could work if the RPMs define version-specific dependencies, however, it would prevent the packager from easily building against all versions using stream expansion as they could in the previous example.
Not modularize libgit
Libgit could also be built as a set of non-modular RPM compatibility packages, having basically the same effect as the previous example.
New streams of silver and bat
This would not solve the problem directly, but it is also a valid technical way of getting rid of the errors — so it’s listed here to make the list complete.
When requiring a new module stream of its dependencies, the silver and bat modules could add new streams that would have this new dependency. Users would need to switch to this stream to use the versions depending on the new libgit stream. However, if there was anything else installed on the system that requires the old stream of libgit, this approach wouldn’t work.
Bundle libgit into the silver and bat modules
This one is tricky, might have weird consequences, and is not recommended. But again, is listed here to make the list complete.
The silver and bat modules could bundle the libgit package in themselves and it would probably work.
What would happen, though, would be two instances of the libgit package in the repository. And when both silver and bat modules would get installed, only one of the two libgit packages would get installed based on their name-version-release (NVR). This is a general issue of modules overlapping.
Potential solutions for the future
One option is to allow switching streams in the background under some very specific conditions — such as only enable this for modules that have been enabled as a dependency (implicitly). Modules installed explicitly would never automatically switch stream. This approach would probably not solve the problem of different packages requiring different versions of a library at the same time, but might be worth looking into
Let’s discuss potential better ways to deal with this kind of problem in the future on the devel list.