OpenAPI document generation retains collectible endpoint contract assemblies
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
### Version
- .NET SDK 10.0.300
- .NET runtime/reference pack 10.0.8
- Microsoft.AspNetCore.OpenApi 10.0.10
- Microsoft.OpenApi 2.7.5
### Description
Generating an OpenAPI document for a `RouteEndpoint` whose `IAcceptsMetadata` / `IProducesResponseTypeMetadata` names types from a collectible `AssemblyLoadContext` keeps that load context, assembly, and contract type alive after the endpoint source and service provider have been released.
The endpoint's request delegate and description method are stable host-owned members. The only collectible artifacts are the request/response contract types in standard endpoint metadata. A control using identical host-owned contract types collects successfully.
### Minimal reproduction
The standalone reproduction and platform receipts are here:
https://github.com/elsa-workflows/elsa-foundation/tree/codex/1392-unload-safe-openapi-boundary/docs/reports/repros/openapi-collectible-contract-retention
Run:
```bash
dotnet run --project Repro.csproj
```
It performs two bounded cycles:
1. stable host-owned request/response metadata;
2. request/response metadata from a collectible contract assembly.
Observed on macOS arm64 and Linux amd64 (`mcr.microsoft.com/dotnet/sdk:10.0.300`):
```text
Stable metadata: CollectionResult { Collected = True, LoadContextAlive = False, AssemblyAlive = False, ContractTypeAlive = False, DelegateAlive = False, ProviderAlive = False }
Collectible metadata: CollectionResult { Collected = False, LoadContextAlive = True, AssemblyAlive = True, ContractTypeAlive = True, DelegateAlive = False, ProviderAlive = False }
```
### Expected behavior
After the endpoint source and OpenAPI service provider are released, no process-global or longer-lived OpenAPI state should strongly retain endpoint contract types or their collectible load context.
### Actual behavior
The provider and endpoint delegate collect, but the collectible load context, assembly, and contract type remain alive through repeated compacting collections.
Inspection of the installed implementation points to document-lifetime caches that retain API-description operation contexts and schema type identities. There does not appear to be a public generation/eviction seam that a dynamic endpoint host can use when a documented endpoint generation is retired.
### Requested direction
A supported generation/lifetime boundary would be preferable to application code clearing private caches. Possible shapes include:
- generation-aware or evictable operation-context and schema caches when endpoint descriptions change;
- avoiding strong retention of `Type`, `MethodInfo`, endpoint metadata, delegates, and service providers beyond the document generation that requires them;
- a public document-source/provider replacement seam used by mapped OpenAPI endpoints as well as direct consumers.
The reproduction intentionally does not mutate private framework caches, use GC delays as an eviction mechanism, or omit real OpenAPI generation.
Contributor guide
Research direction
Start by running the standalone reproduction with `dotnet run --project Repro.csproj` and compare its stable and collectible metadata cycles. Then inspect the installed OpenAPI implementation's document-lifetime caches, operation-description contexts, and schema type identities; done means a supported generation or lifetime boundary lets the collectible load context, assembly, and contract type collect after the endpoint source and provider are released.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, openapi
- Domain
- api, backend-api-design
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100