Skip to content

refactor: Centralize SignalR initialization and fix subscription leaks#2581

Merged
pkuehnel merged 2 commits intofix/chorefrom
fix-signalr-subscription-leak-1030221631243904585
Feb 27, 2026
Merged

refactor: Centralize SignalR initialization and fix subscription leaks#2581
pkuehnel merged 2 commits intofix/chorefrom
fix-signalr-subscription-leak-1030221631243904585

Conversation

@pkuehnel
Copy link
Owner

Fixes a bug where components (CarDetailsComponent, ChargingConnectorDetailsComponent) were redundantly subscribing to SignalRStateService on every OnParametersSetAsync, causing duplicate UI updates and memory leaks. The service now handles its own initialization gracefully and returns IDisposable from subscription methods to ensure components can properly unregister their callbacks.


PR created automatically by Jules for task 1030221631243904585 started by @pkuehnel

- Added internal `await InitializeAsync()` calls to public methods in `SignalRStateService`.
- Changed `Subscribe` and `SubscribeToTrigger` to return `Task<IDisposable>` for proper cleanup.
- Removed redundant `InitializeAsync()` calls from Blazor components.
- Added proper `IDisposable` implementation in components using SignalR subscriptions to prevent duplicate updates and memory leaks during re-renders.

Co-authored-by: pkuehnel <[email protected]>
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the SignalR integration across the application to prevent subscription leaks and redundant initializations. By centralizing the SignalR initialization within the SignalRStateService and implementing proper disposal mechanisms for subscriptions, the changes ensure a more robust and efficient handling of real-time data updates in Blazor components. This resolves issues of duplicate UI updates and memory consumption previously caused by components repeatedly subscribing to the SignalR service.

Highlights

  • Subscription Leak Fixes: Implemented IDisposable in several Blazor components and refactored SignalR subscription management to prevent memory leaks caused by redundant subscriptions.
  • Centralized SignalR Initialization: Moved SignalRStateService.InitializeAsync() calls from individual components into the service's core methods (GetStateAsync, Subscribe, SubscribeToTrigger), ensuring initialization occurs only when needed.
  • IDisposable for Subscriptions: Modified ISignalRStateService and SignalRStateService to return IDisposable objects from subscription methods, allowing components to properly unregister callbacks.
  • Optimized Component Subscriptions: Added logic in CarDetailsComponent and ChargingConnectorDetailsComponent to re-subscribe to SignalR only when their respective IDs change, preventing duplicate subscriptions on OnParametersSetAsync.
  • New Subscription Management Class: Introduced a SubscriptionDisposable class to encapsulate the unsubscription logic, simplifying resource management within the SignalRStateService.
Changelog
  • TeslaSolarCharger/Client/Components/StartPage/CarDetailsComponent.razor
    • Implemented IDisposable interface.
    • Added _subscription and _lastSubscribedCarId fields for managing subscriptions.
    • Refactored OnParametersSetAsync to dispose of existing subscriptions and re-subscribe only if the CarId changes.
    • Added a Dispose() method to unregister the SignalR subscription.
  • TeslaSolarCharger/Client/Components/StartPage/ChargingConnectorDetailsComponent.razor
    • Implemented IDisposable interface.
    • Added _subscription and _lastSubscribedChargingConnectorId fields for managing subscriptions.
    • Refactored OnParametersSetAsync to dispose of existing subscriptions and re-subscribe only if the ChargingConnectorId changes.
    • Added a Dispose() method to unregister the SignalR subscription.
  • TeslaSolarCharger/Client/Components/StartPage/ChargingSchedulesComponent.razor
    • Implemented IDisposable interface.
    • Added a _subscription field to hold the SignalR subscription.
    • Removed the explicit SignalRStateService.InitializeAsync() call from OnInitializedAsync.
    • Assigned the result of SubscribeToTrigger to _subscription.
    • Added a Dispose() method to unregister the SignalR subscription.
  • TeslaSolarCharger/Client/Components/StartPage/LoadpointComponent.razor
    • Implemented IDisposable interface.
    • Added a _subscription field to hold the SignalR subscription.
    • Removed the explicit SignalRStateService.InitializeAsync() call from OnInitializedAsync.
    • Assigned the result of Subscribe to _subscription.
    • Added a Dispose() method to unregister the SignalR subscription.
  • TeslaSolarCharger/Client/Components/StartPage/NotChargingAtExpectedPowerReasonsComponent.razor
    • Added a _subscription field to hold the SignalR subscription.
    • Removed the explicit SignalRStateService.InitializeAsync() call from OnParametersSetAsync.
    • Assigned the result of SubscribeToTrigger to _subscription.
    • Added _subscription?.Dispose() to the existing Dispose() method.
  • TeslaSolarCharger/Client/Components/StartPage/PowerFlowComponent.razor
    • Implemented IDisposable interface.
    • Added a _subscription field to hold the SignalR subscription.
    • Removed the explicit SignalRStateService.InitializeAsync() call from OnInitializedAsync.
    • Assigned the result of Subscribe to _subscription.
    • Added a Dispose() method to unregister the SignalR subscription.
  • TeslaSolarCharger/Client/Pages/Home.razor
    • Implemented IDisposable interface.
    • Added a _subscription field to hold the SignalR subscription.
    • Removed the explicit SignalRStateService.InitializeAsync() call from OnInitializedAsync.
    • Assigned the result of SubscribeToTrigger to _subscription.
    • Added a Dispose() method to unregister the SignalR subscription.
  • TeslaSolarCharger/Client/Services/Contracts/ISignalRStateService.cs
    • Updated the Subscribe and SubscribeToTrigger method signatures to return Task<IDisposable>.
  • TeslaSolarCharger/Client/Services/SignalRStateService.cs
    • Added await InitializeAsync() calls to GetStateAsync, Subscribe, and SubscribeToTrigger for centralized initialization.
    • Modified Subscribe and SubscribeToTrigger methods to return IDisposable instances.
    • Implemented logic within the returned IDisposable to remove the corresponding callback from internal subscriber lists upon disposal.
  • TeslaSolarCharger/Client/Services/SubscriptionDisposable.cs
    • Added a new class SubscriptionDisposable that implements IDisposable and executes a provided action upon disposal.
  • get_diff.sh
    • Added a new shell script get_diff.sh to execute git diff.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively refactors the SignalR subscription management to prevent memory leaks by introducing a disposable pattern. The centralization of the SignalR connection initialization in SignalRStateService is a great improvement. The changes across the components correctly adopt the new IDisposable subscriptions.

I've identified one component (NotChargingAtExpectedPowerReasonsComponent) where the subscription leak is not fully resolved and have suggested a fix. I also found a minor issue with a misleading log message in another component. Overall, this is a solid improvement for the application's stability and maintainability.

@pkuehnel pkuehnel merged commit 69fca0a into fix/chore Feb 27, 2026
1 check passed
@pkuehnel pkuehnel deleted the fix-signalr-subscription-leak-1030221631243904585 branch February 27, 2026 23:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant