Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design for solving transient disposables on Blazor Server #26676

Open
SteveSandersonMS opened this issue Oct 7, 2020 · 32 comments
Open

Design for solving transient disposables on Blazor Server #26676

SteveSandersonMS opened this issue Oct 7, 2020 · 32 comments
Assignees
Labels
affected-few This issue impacts only small number of customers area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-blazor-component-model Any feature that affects the component model for Blazor (Parameters, Rendering, Lifecycle, etc) feature-blazor-server feature-blazor-server-experience Issues that make Blazor server experience different of Web assembly or Desktop Needs: Design This issue requires design work before implementating. Pillar: Technical Debt Priority:1 Work that is critical for the release, but we could probably ship without severity-major This label is used by an internal tool
Milestone

Comments

@SteveSandersonMS
Copy link
Member

This is something we've discussed in the past, and previously decided that we needed a notion of nested DI scopes to solve truly. If this is possibly in scope for .NET 6, we need to begin an actual design process.

cc @javiercn @davidfowl

@SteveSandersonMS SteveSandersonMS added the area-blazor Includes: Blazor, Razor Components label Oct 7, 2020
@mkArtakMSFT mkArtakMSFT added enhancement This issue represents an ask for new feature or an enhancement to an existing one Needs: Design This issue requires design work before implementating. labels Oct 8, 2020
@mkArtakMSFT mkArtakMSFT added this to the Next sprint planning milestone Oct 8, 2020
@ghost
Copy link

ghost commented Oct 8, 2020

Thanks for contacting us.
We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@SteveSandersonMS SteveSandersonMS added affected-few This issue impacts only small number of customers severity-major This label is used by an internal tool labels Oct 9, 2020 — with ASP.NET Core Issue Ranking
@ghost
Copy link

ghost commented Oct 9, 2020

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@davidfowl
Copy link
Member

Can I assume backlog is another way to say, we're going to do it for 6.0 😬

@javiercn javiercn added feature-blazor-component-model Any feature that affects the component model for Blazor (Parameters, Rendering, Lifecycle, etc) feature-blazor-server feature-blazor-server-experience Issues that make Blazor server experience different of Web assembly or Desktop labels Apr 20, 2021
@mkArtakMSFT mkArtakMSFT added Priority:1 Work that is critical for the release, but we could probably ship without triaged labels Nov 2, 2021
@mrpmorris
Copy link

Have there been more discussions about this since?

Could someone share the current thoughts so we can talk through it?

@TanayParikh
Copy link
Contributor

Design Proposal: Transient disposables on Blazor Server

Summary

Allow for more granular control over the lifetime of transient services implementing IDisposable within Blazor Server.

Motivation

Transient services which implement IDisposable currently leak memory. The behavior is as follows:

Transient Service which do NOT implement IDisposable Transient services which implement IDisposable
Are garbage collected when the component in which the service was injected is disposed. A reference to the service is maintained by the DI container so it can handle service disposal. This prevents garbage collection of the service upon component disposal. The service is kept in memory till the DI container itself can be garbage collected.

The service is kept in memory till the DI container itself can be garbage collected.

The DI container for Blazor Server cannot be garbage collected until the circuit is terminated. Consequently, transient services implementing IDisposable are kept in memory till circuit termination.

Given the lifetime of the circuit may be much longer than the lifetime of the component, we're unduly delaying the disposal of transient services implementing IDisposable, thus leaking memory.

In-scope

  • Facilitate a safe and reliable way to dispose of transient services implementing IDisposable within Blazor Server.

We want to ensure that transient services implementing IDisposable can be disposed when the component in which the service was injected, is disposed.

Out of scope

Depending on the solution chosen, it may be possible for users to purposely not dispose of the transient service upon component disposal. This would be discouraged, but it may not be possible to entirely prevent.

Proposed Solutions

Implicitly Manage Service Lifetime

Have the Blazor framework handle the lifetime of IDisposable transient services.

The Blazor framework requests the DI container (Service Provider) for a tracker. A tracker is maintained for each component created. All transient services created for a specific component are associated with the tracker. When the component is disposed, the tracker is disposed, thereby disposing the IDisposable transient services created using the tracker.

Framework code:

IServiceProvider provider;

// Create a tracker for the component.
var tracker = provider.CreateTracker(); // [NEW]

// `IDisposable` transient services injected into the component are created
// using the tracker.
tracker.GetRequireService<ITransientDisposableService>();

// The tracker is disposed when the component is disposed.
// The `IDisposable` transient services created using the tracker are disposed via the tracker disposal.
await tracker.DisposeAsync();

Advantages

  • No need to explicitly dispose of transient services implementing IDisposable when the component is disposed.
  • Automatically resolve memory leaks in existing Blazor Server applications using IDisposable transient services.
    • No changes needed in existing Blazor Server applications.
  • Does not impact the learning curve for Blazor as we are not introducing a new concept.

Disadvantages

  • Increased overhead from the maintainance of a tracker for each component.
  • Less control over the lifetime of transient services implementing IDisposable within Blazor Server.
  • Breaking change in the event developers were relying on long-living IDisposable transient services.
  • Will require additional public APIs to the Dependency Injection Container's Service Provider.

Explicitly Manage Service Lifetime

Allow users to manage the lifetime of IDisposable transient services.

Users wrap the IDisposable transient service with an additional generic interface which facilitates user disposal of the IDisposable transient service.

User code:

@inject ITracked<ITransientDisposableService> trackedTransientDisposableService

@{
    var service = trackedTransientDisposableService.Value;
    trackedTransientDisposableService.Dispose();
}

Framework code:

IServiceProvider provider;

return provider.GetRequiredService<ITracked<ITransientDisposableService>>();

Definition

interface ITracked<T> : IDisposable
{
    T Value { get; }
}

Advantages / Disadvantages are essentially the inverse of the explicit approach.

Advantages

  • More control over the lifetime of transient services implementing IDisposable within Blazor Server.
  • Existing behavior is maintained, so no breaking changes.
  • No changes required to the existing public APIs offered by the Dependency Injection Container's Service Provider. The (new) ITracked wrapper would handle much of the functionality.

Disadvantages

  • Increased overhead from the ITracked wrapper.
  • Requires explicit disposal of transient services implementing IDisposable when the component is disposed.
  • Does not automatically resolve memory leaks in existing Blazor Server applications using IDisposable transient services.
  • Increases the "concept count" for the DI Container and Blazor, and (slightly) increases the learning curve.

Hybrid Approach

Allow both implicit and explicit management. Implicitly manage IDisposable transient services by default, however allow explicit management of transient disposable services wrapped with (ITracked). Thus we get the advantages of implicit handling without losing the ability to granularly control the lifetime of IDisposable transient services should that be desired.

Advantages

  • Automatically resolves memory leaks in existing Blazor Server applications using IDisposable transient services.
  • Still allows for granular control should that be desired.

Disadvantages

  • Increased implementation complexity and learning curve.
  • Overhead from maintaining a tracker for each component & the overhead from the ITracked wrapper.
  • Could still be considered a breaking change for existing applications.
  • Requires more extensive changes to the Dependency Injection Container.

Leverage OwningComponentBase

By leveraging OwningComponentBase we can manage the lifetime of IDisposable transient services within Blazor Server presently.

By having a component extend OwningComponentBase, we have access to a ScopedServices provider.

/// <summary>
/// Gets the scoped <see cref="IServiceProvider"/> that is associated with this component.
/// </summary>
protected IServiceProvider ScopedServices

User code:

@implements OwningComponentBase

@{
    var service = ScopedServices.GetService<ITransientDisposableService>();
}

The disposal of the component results in the disposal of the OwningComponentBase. Thus, the ScopedServices provider is garbage collected, and the transient services implementing IDisposable are disposed.

Advantages

  • Already implemented & available for use. No changes required to the Dependency Injection Container.
  • No potentially breaking changes.

Disadvantages

  • Learning curve to understand how to implement the pattern.
  • Requires users to work manually with the Dependency Injection Container (can't use Blazor's @inject).
  • Doesn't automatically resolve the issue for existing apps not using the pattern.

Risks / Unknowns

Varies based on the approach chosen. The disadvantages sections cover the risks and unknowns of the approach.

Requires updating the Dependency Injection Container implementation which is used broadly. Different stakeholders may have differing concerns and requirements.

Examples

See above.

@mkArtakMSFT mkArtakMSFT added Priority:1 Work that is critical for the release, but we could probably ship without and removed Priority:1 Work that is critical for the release, but we could probably ship without labels Nov 10, 2022
@mkArtakMSFT mkArtakMSFT removed the ss-p1 label Nov 22, 2022
@mkArtakMSFT mkArtakMSFT modified the milestones: .NET 8 Planning, Backlog Jun 29, 2023
@ghost
Copy link

ghost commented Jun 29, 2023

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@ghost
Copy link

ghost commented Dec 12, 2023

Thanks for contacting us.

We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
affected-few This issue impacts only small number of customers area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-blazor-component-model Any feature that affects the component model for Blazor (Parameters, Rendering, Lifecycle, etc) feature-blazor-server feature-blazor-server-experience Issues that make Blazor server experience different of Web assembly or Desktop Needs: Design This issue requires design work before implementating. Pillar: Technical Debt Priority:1 Work that is critical for the release, but we could probably ship without severity-major This label is used by an internal tool
Projects
None yet
Development

No branches or pull requests