Skip to content

Commit

Permalink
Add initial Runtime Graph Maintainability doc
Browse files Browse the repository at this point in the history
Contributes to dotnet/runtime#59803
  • Loading branch information
eerhardt committed Jan 14, 2022
1 parent f9d0060 commit 79735ac
Showing 1 changed file with 60 additions and 0 deletions.
60 changes: 60 additions & 0 deletions accepted/2022/runtime-graph-maintainability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Runtime graph maintainability

## Problem

New versions of Linux distros are constantly shipping. The Alpine distro ships a new minor version [every 6 months](https://alpinelinux.org/releases/). Fedora has also been shipping a new version [twice a year](https://en.wikipedia.org/wiki/Fedora_version_history#Version_history) for the past 10 years.

Each time one of these distros ships a new version, we need to update our RID graph. If we don't, applications running on these new operating systems can fail because the RID is unknown to .NET. And it doesn't use the correct native assets.

The underlying problem here is that RIDs were designed to be opaque strings. We intentionally didn't want people breaking these strings apart and inferring things about the RID. Instead, we build a [graph](https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.NETCore.Platforms/readme.md) that needs to be used in order to make sense of what RIDs are compatible with other RIDs.

Each new version of Alpine gets a new RID value. `alpine.3.14`, `alpine.3.15`. When version `3.16` ships, `alpine.3.16` doesn't show up in the graph. .NET doesn't infer any compatibility about `alpine.3.16` and instead falls all the way back to `linux-{arch}` for the RID. This means any native asset built for `alpine` or `linux-musl` are no longer loaded causing apps to fail to execute properly.

We spend a considerable amount of time just adding new versions to the RID graph in servicing. Here are some example PRs in the last 6 months:

Alpine
- 3.14
- [release/3.1](https://github.com/dotnet/corefx/pull/43099)
- [release/5.0](https://github.com/dotnet/runtime/pull/57226)
- [main / 6.0](https://github.com/dotnet/runtime/pull/55857)
- 3.15
- [main](https://github.com/dotnet/runtime/pull/62938) Merged Jan 2022
- servicing hasn't happened yet, but will be needed for at least 3.1, 6.0, and possibly 5.0

Fedora
- 35
- [release/2.1](https://github.com/dotnet/corefx/pull/43033)
- [release/3.1](https://github.com/dotnet/corefx/pull/43032)
- [release/5.0](https://github.com/dotnet/runtime/pull/48203)
- [main / 6.0](https://github.com/dotnet/runtime/pull/48200)

- 36
- [release/3.1](https://github.com/dotnet/corefx/pull/43101)
- [release/5.0](https://github.com/dotnet/runtime/pull/57703)
- [main / 6.0](https://github.com/dotnet/runtime/pull/57832)

## Proposal

Internally, we generate the full RID graph based on a higher-level set of data and use some rules for how to generate that data into the full RID graph. Here is the [documentation for that process](https://github.com/dotnet/runtime/tree/main/src/libraries/Microsoft.NETCore.Platforms#adding-a-new-os).

In order to avoid having to service the `runtime.json` file for every new version, an approach we can take is the following:

1. Add a new top-level section to the `runtime.json` JSON structure called `runtimeGroups`.
1. A runtime group will contain the following information:
1. `ID`: ex. `alpine`
2. `Parent`: ex. `linux-musl`
3. `TreatVersionsAsCompatible`: ex. `All`, `None`, `Major`, default is `All`
2. If a RID is found in the existing `runtimes` section of the `runtime.json`, the existing behavior will be taken and entries in the `runtimeGroups` section will be ignored.
3. When the RID is not found in the `runtimes` section, the `runtimeGroups` section will be inspected.
1. Using the first part of the `RID`, before the first `.`, look for a runtime group with matching `ID`. If it is not found, the existing fallback behavior is taken.
2. When probing for compatible assets, inspect the `TreatVersionsAsCompatible` setting.
1. For `All`, an asset with the same `ID` and the highest version that is less than or equal to the current RID's version will be selected.
2. For `None`, an asset with the same `ID` and a version that is equal to the current RID's version will be selected.
3. For `Major`, an asset with the same `ID` and the highest version that is equal to the current RID's version will be selected.
3. If no compatible asset is found using the same `ID`, get the `Parent` value and probe for assets using it.
4. Note: This design doesn't allow for mapping versions between `RID`s and their parents versions. For cases that require this, we will use the existing `runtimes` section.
5. TODO: handle architectures. Should we even consider architecture-less in this model? Is it used/necessary?

2. Update the logic in the `dotnet.exe` [host](https://github.com/dotnet/runtime/blob/635cfb3409fcce4257e35ba0cf3d385fdee3a6ad/src/native/corehost/deps_format.cpp#L141) to support the new `runtimeGroups` section.
3. Update the logic in [`NuGet.Packaging`](https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Packaging/RuntimeModel/RuntimeGraph.cs#L13) to support the new `runtimeGroups` section.
4. (Potentially) Update [DependencyContext](https://github.com/dotnet/runtime/blob/1ba80f698c85b525545f1a13c02eb405588b0461/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs#L64) to allow `runtimeGroups` to be flown through the `.deps.json` files.

0 comments on commit 79735ac

Please sign in to comment.