-
Notifications
You must be signed in to change notification settings - Fork 787
Web 428 memory leaks in multiple components due to unmanaged subscriptions #2808
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
base: dev
Are you sure you want to change the base?
Conversation
…oliday components - Added OnDestroy lifecycle hook to both Create and Edit Holiday components - Imported Subject and takeUntil from rxjs - Created destroy$ Subject to manage subscription cleanup - Applied takeUntil operator to form valueChanges subscriptions - Properly unsubscribe on component destruction to prevent memory leaks - Fixes potential performance issues from accumulated subscriptions
|
Note
|
| Cohort / File(s) | Summary |
|---|---|
Holiday management components src/app/organization/holidays/create-holiday/create-holiday.component.ts, src/app/organization/holidays/edit-holiday/edit-holiday.component.ts |
Implements OnDestroy, adds private destroy$ = new Subject<void>(), pipes route/form/valueChanges and submit/update subscriptions through takeUntil(this.destroy$), and adds ngOnDestroy() to emit/complete destroy$. |
Product charge step components src/app/products/fixed-deposit-products/.../fixed-deposit-product-charges-step.component.ts, src/app/products/loan-products/.../loan-product-charges-step.component.ts, src/app/products/recurring-deposit-products/.../recurring-deposit-product-charges-step.component.ts, src/app/products/share-products/.../share-product-charges-step.component.ts |
Components now implement OnDestroy, introduce destroy$ Subject, pipe currencyCode.valueChanges (and other valueChanges like multiDisburseLoan) through takeUntil(this.destroy$), and add ngOnDestroy() to emit/complete destroy$. |
Shared notifications & shares account src/app/shared/notifications-tray/notifications-tray.component.ts, src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts |
Adds destroy$ Subject; wraps forkJoin, API calls, and valueChanges subscriptions with takeUntil(this.destroy$); implements OnDestroy and ngOnDestroy() to emit/complete destroy$. |
Sequence Diagram(s)
sequenceDiagram
participant C as Angular Component
participant O as Observable (forms, valueChanges, API)
participant D as destroy$ Subject
C->>O: subscribe(...).pipe(takeUntil(D))
O-->>C: emit values
C->>C: handle emissions
Note over C: Component destroyed (navigation/unmount)
C->>D: destroy$.next()
D->>O: takeUntil triggers unsubscription
C->>D: destroy$.complete()
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
- Pay special attention to NotificationsTrayComponent for multiple concurrent subscriptions and forkJoin usage.
- Confirm every subscription site in the changed files uses
takeUntil(this.destroy$)(including nested/valueChanges and API calls). - Verify
destroy$isprivateand is.next()then.complete()inngOnDestroy().
Suggested reviewers
- alberto-art3ch
Pre-merge checks and finishing touches
✅ Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly and specifically describes the main change: fixing memory leaks in multiple components by managing subscriptions, which directly aligns with all file modifications implementing OnDestroy and takeUntil patterns. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
✨ Finishing touches
- 📝 Generate docstrings
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/app/organization/holidays/edit-holiday/edit-holiday.component.ts (1)
2-3: Apply destroy$ pattern consistently toroute.dataas wellThe new
destroy$subject,OnDestroyimplementation, andtakeUntil(this.destroy$)onreschedulingType.valueChangesclean up the main reactive form subscription nicely. However, thethis.route.data.subscribe(...)in the constructor is still a long‑lived subscription that is not tied to the component lifecycle. To fully align with the leak‑prevention goal, consider piping it throughtakeUntil(this.destroy$)as well:- this.route.data.subscribe((data: { holiday: any; holidayTemplate: any }) => { + this.route.data + .pipe(takeUntil(this.destroy$)) + .subscribe((data: { holiday: any; holidayTemplate: any }) => { this.holidayData = data.holiday; this.holidayData.repaymentSchedulingTypes = data.holidayTemplate; this.reSchedulingType = this.holidayData.reschedulingType; if (this.holidayData.status.value === 'Active') { this.isActiveHoliday = true; } else { this.isActiveHoliday = false; } - }); + });This keeps all ongoing subscriptions in this component consistently tied to
destroy$. As per coding guidelines.Also applies to: 11-12, 31-45, 63-72, 119-137, 174-180
src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts (1)
83-84: valueChanges subscription now safely tied to component lifecycleWiring
this.currencyCode.valueChangesthrough.pipe(takeUntil(this.destroy$))and emitting/completingdestroy$inngOnDestroycorrectly prevents this form control subscription from leaking after the component is destroyed. Consider adding explicit return types (ngOnInit(): void,ngOnDestroy(): void) in a follow‑up pass to tighten type safety, but this is optional.Also applies to: 86-88
src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts (1)
84-85: Form control subscription is now properly cleaned up on destroyPiping
this.currencyCode.valueChangesthroughtakeUntil(this.destroy$)and triggeringnext()/complete()inngOnDestroyensures this subscription cannot leak beyond the component’s lifecycle. As an optional improvement, you might later tighten types (e.g., replaceany/{}[]with explicit interfaces) to better match the “strict type safety” guideline.Also applies to: 87-89
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/app/organization/holidays/create-holiday/create-holiday.component.ts(6 hunks)src/app/organization/holidays/edit-holiday/edit-holiday.component.ts(6 hunks)src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts(4 hunks)src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.ts(4 hunks)src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.ts(4 hunks)src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts(4 hunks)src/app/shared/notifications-tray/notifications-tray.component.ts(7 hunks)src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/app/**
⚙️ CodeRabbit configuration file
src/app/**: For Angular code: verify component separation, trackBy on *ngFor,
strict type safety, and clean observable patterns.
Files:
src/app/shared/notifications-tray/notifications-tray.component.tssrc/app/organization/holidays/create-holiday/create-holiday.component.tssrc/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.tssrc/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.tssrc/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.tssrc/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.tssrc/app/organization/holidays/edit-holiday/edit-holiday.component.tssrc/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run Lint, Build and Deploy
🔇 Additional comments (12)
src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts (1)
2-8: Solid destroy$ + takeUntil integration; subscriptions look well-scopedThe added
OnDestroyimplementation,destroy$subject, andtakeUntil(this.destroy$)oncurrencyCode.valueChangesare wired correctly and align with Angular/RxJS best practices for avoiding leaks. The remaining dialogafterClosed()subscriptions are short-lived and complete on close, so leaving them as-is is fine.Also applies to: 62-88, 97-105, 115-118
src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.ts (1)
1-8: Charges-step subscriptions correctly bound to component lifecycleUsing
destroy$withtakeUntilon bothcurrencyCode.valueChangesandmultiDisburseLoan.valueChanges, plus the newngOnDestroy, provides proper cleanup for the two long‑lived streams in this component. If there are code paths where either input control might be undefined atngOnInit, consider adding a simple null guard before subscribing; otherwise this implementation looks good.Also applies to: 58-77, 84-97, 99-101
src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.ts (1)
1-8: Recurring-deposit charges subscription cleanup looks correctThe addition of
destroy$,OnDestroy, andtakeUntil(this.destroy$)oncurrencyCode.valueChangescorrectly scopes the only ongoing subscription in this component to its lifecycle. This matches the cleanup pattern used elsewhere in the PR and should eliminate the previous leak.Also applies to: 52-67, 74-82, 84-86
src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts (2)
1-1: Subscription‑cleanup imports and OnDestroy integration look correctImporting
OnDestroy,Subject, andtakeUntil, and updating the class to implementOnInit, OnDestroycleanly supports the newtakeUntil(destroy$)pattern; no issues here.Also applies to: 5-7, 54-54
68-69: destroy$ subject is correctly scoped and typed
private destroy$ = new Subject<void>();is a good fit for centralizing subscription teardown and is properly kept private to the component.src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts (2)
1-1: Consistent lifecycle‑aware imports and interface usageBringing in
OnDestroy,Subject, andtakeUntiland updating the class to implementOnInit, OnDestroyaligns this component with the shared subscription‑cleanup pattern used elsewhere in the PR.Also applies to: 5-7, 54-54
69-71: destroy$ setup is appropriate for managing subscriptionsThe
private destroy$ = new Subject<void>();field is correctly declared for use as a teardown notifier and kept private to avoid external misuse.src/app/shared/notifications-tray/notifications-tray.component.ts (5)
5-6: RxJS imports and destroy$ field correctly prepared for teardownImporting
SubjectandtakeUntil, and addingprivate destroy$ = new Subject<void>();, is an appropriate foundation for centralizing subscription cleanup in this component.Also applies to: 50-51
73-83: Constructor subscription and ngOnDestroy are now lifecycle‑safeWrapping the initial
forkJoin([...getNotifications(...)])subscription with.pipe(takeUntil(this.destroy$))and then emitting/completingdestroy$inngOnDestroy, while still callingdestroy()to clear the timer, correctly prevents notification subscriptions from surviving past component destruction.Also applies to: 89-93
110-117: Polling subscription tied to destroy$ while timer is still clearedThe
fetchUnreadNotificationsHTTP call is now guarded bytakeUntil(this.destroy$), and with the existingclearTimeout(this.timer)indestroy(), both the polling subscription and its scheduling are safely bounded by the component lifecycle.
127-133: updateNotifications subscription no longer risks lingeringWrapping
updateNotifications()intakeUntil(this.destroy$)ensures this side‑effecting call cannot outlive the component if the menu closes near teardown; the local state updates remain unchanged.
142-149: Mock notifications subscription is also cleaned upGuarding
getMockUnreadNotification()withtakeUntil(this.destroy$)brings the test/mock path in line with the production paths, preventing accidental leaks when using the mock helper.
src/app/organization/holidays/create-holiday/create-holiday.component.ts
Show resolved
Hide resolved
…er subscription cleanup - Fixed memory leaks in NotificationsTrayComponent by adding takeUntil to all subscriptions - Fixed memory leaks in SharesAccountChargesStepComponent with proper unsubscribe pattern - Fixed memory leaks in RecurringDepositProductChargesStepComponent with takeUntil operator - Fixed memory leaks in FixedDepositProductChargesStepComponent with subscription cleanup - Fixed memory leaks in ShareProductChargesStepComponent with proper OnDestroy implementation - Fixed memory leaks in LoanProductChargesStepComponent by unsubscribing from valueChanges All components now implement OnDestroy interface and use Subject + takeUntil pattern to properly clean up subscriptions and prevent memory leaks.
3e32732 to
43854ce
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.ts (1)
1-1: Loan product charges: all long‑lived subscriptions now properly scoped to lifecycle
- Both
currencyCode.valueChangesandmultiDisburseLoan.valueChangesare now piped throughtakeUntil(this.destroy$), andngOnDestroycorrectly signals and completesdestroy$, so these form‑control streams will no longer outlive the component.- The pattern matches the other charges‑step components in this PR, giving you consistent subscription handling across the product steppers.
You might later consider a small shared base class or helper for the repeated
destroy$+ngOnDestroypattern across the various*ChargesStepComponents, but that’s purely a maintainability win, not required here.Also applies to: 5-7, 58-58, 76-78, 84-97, 99-101
src/app/organization/holidays/edit-holiday/edit-holiday.component.ts (1)
2-2: EditHoliday: lifecycle‑aware subscriptions implemented correctly
destroy$andOnDestroyare wired correctly, and the key subscriptions (route.data,reschedulingType.valueChanges, and theupdateHolidaycall insubmit()) are now all scoped withtakeUntil(this.destroy$), eliminating the previous risk of lingering subscriptions when the component is destroyed.- Given
getReschedulingType()is only called after thereschedulingTypecontrol is added for inactive holidays, accessing.get('reschedulingType')is safe in practice; if you ever change that flow, a small defensive check for a missing control would be the only refinement to consider.Also applies to: 11-12, 31-31, 44-45, 63-73, 123-136, 168-175, 177-183
src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts (1)
55-61: Optional: tighten typings for inputs and charge data
shareProductsTemplate: any,chargeData: any, andchargesDataSource: {}[]make this area loosely typed; defining an interface for the share product template and charge shape would help catch errors earlier and improve IDE support. This can be a follow-up and is not required for the subscription-leak fix.src/app/shared/notifications-tray/notifications-tray.component.ts (1)
110-121: Optional: consider RxJS-based polling instead ofsetTimeoutrecursionFor a future cleanup, you could replace the manual
setTimeoutrecursion infetchUnreadNotificationswith an RxJSinterval/timer+switchMapchain also governed bytakeUntil(this.destroy$). That would unify the polling logic with your observable-based teardown and further simplifydestroy(). Not required for this PR.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/app/organization/holidays/create-holiday/create-holiday.component.ts(8 hunks)src/app/organization/holidays/edit-holiday/edit-holiday.component.ts(7 hunks)src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts(4 hunks)src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.ts(4 hunks)src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.ts(4 hunks)src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts(4 hunks)src/app/shared/notifications-tray/notifications-tray.component.ts(7 hunks)src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/app/**
⚙️ CodeRabbit configuration file
src/app/**: For Angular code: verify component separation, trackBy on *ngFor,
strict type safety, and clean observable patterns.
Files:
src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.tssrc/app/organization/holidays/edit-holiday/edit-holiday.component.tssrc/app/organization/holidays/create-holiday/create-holiday.component.tssrc/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.tssrc/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.tssrc/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.tssrc/app/shared/notifications-tray/notifications-tray.component.tssrc/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts
🧬 Code graph analysis (1)
src/app/organization/holidays/create-holiday/create-holiday.component.ts (1)
src/app/organization/holidays/create-holiday/checklist-db.class.ts (1)
data(11-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run Lint, Build and Deploy
🔇 Additional comments (8)
src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts (1)
2-8: Subscription cleanup pattern is correctly applied here
- Importing
OnDestroy,Subject, andtakeUntil, addingdestroy$, and wiringcurrencyCode.valueChangesthroughtakeUntil(this.destroy$)with a matchingngOnDestroyis correct and will prevent the previous long‑lived subscription leak.- Dialog
afterClosed()subscriptions remain fine as those observables complete on close, so focusingtakeUntilon theFormControlstream is appropriate.Also applies to: 62-62, 86-88, 97-106, 115-118
src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.ts (1)
1-1: Recurring deposit charges: RxJS teardown looks goodThe newly added
destroy$subject,OnDestroyimplementation, and thecurrencyCode.valueChanges.pipe(takeUntil(this.destroy$))subscription form a clean teardown pattern and should address the leak in this component without changing behavior.Also applies to: 5-7, 52-52, 66-68, 74-82, 84-87
src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts (1)
1-1: Fixed deposit charges: correct use ofdestroy$for FormControl streamThe added
destroy$subject,OnDestroyimplementation, and wrapping ofcurrencyCode.valueChangeswithtakeUntil(this.destroy$)correctly bound the only long‑lived subscription in this component to its lifecycle, aligning it with the rest of the PR’s cleanup work.Also applies to: 5-7, 54-54, 68-70, 76-84, 86-88
src/app/organization/holidays/create-holiday/create-holiday.component.ts (3)
3-13: destroy$ lifecycle wiring and imports look correctImporting
OnDestroy,Subject, andtakeUntil, having the class implementOnDestroy, and declaringprivate destroy$ = new Subject<void>()is idiomatic and sets up a clean lifecycle boundary for all observable chains. No issues here.
As per coding guidelines, please just re-run the holiday-related unit/integration tests to confirm everything still compiles and behaves as expected.Also applies to: 57-69
118-133: All relevant subscriptions now consistently usetakeUntil(this.destroy$)The subscriptions on
route.data,this._database.dataChange,reschedulingType.valueChanges, andorganizationService.createHoliday(data)are all correctly piped throughtakeUntil(this.destroy$), which addresses the earlier unmanaged-subscription leaks and keeps the component’s observable usage consistent. This aligns well with the PR’s objective.
As per coding guidelines, please verify at runtime that holiday creation, office tree rendering, and rescheduling-type toggling still behave as before.Also applies to: 321-330, 360-371
374-379:ngOnDestroycorrectly tears downdestroy$Calling
this.destroy$.next()followed bythis.destroy$.complete()is the expected pattern to terminate alltakeUntil(this.destroy$)chains when the component is destroyed; no extra cleanup is needed here.
Consider adding/maintaining a simple component-destroy test (if feasible) to validate no subscriptions remain active after navigating away.src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts (1)
1-7: destroy$ pattern forcurrencyCode.valueChangesis implemented correctlyAdding
OnDestroy, importingSubject/takeUntil, introducingprivate destroy$ = new Subject<void>(), and pipingthis.currencyCode.valueChangesthroughtakeUntil(this.destroy$)withngOnDestroycompleting the subject gives this component a clean subscription lifecycle. The dialog’safterClosed()subscription remains a one-shot stream, so leaving it as-is is fine.
As per coding guidelines, please confirm manually that changing the currency still resetschargesDataSourceand that no console warnings about subscriptions appear on repeated navigation.Also applies to: 54-71, 84-89
src/app/shared/notifications-tray/notifications-tray.component.ts (1)
5-7: Subscription cleanup for notifications is comprehensive and consistentUsing
private destroy$ = new Subject<void>()plustakeUntil(this.destroy$)on the initialforkJoin,getNotifications(false, 9),updateNotifications(), andgetMockUnreadNotification()calls—combined withdestroy()clearing the timer andngOnDestroynexting/completingdestroy$—gives this component a clean teardown path for both HTTP streams and the polling loop. This should effectively address the previously leaking subscriptions.
As per coding guidelines, please verify manually (e.g., by opening/closing the notifications tray and navigating around) that notifications still load and update correctly with no lingering network activity after component destruction.Also applies to: 50-52, 73-83, 89-93, 111-117, 129-132, 143-149
Resolved memory leaks across multiple components by adding proper RxJS subscription cleanup. Updated each affected component to use the takeUntil(destroy$) pattern and implemented ngOnDestroy() to ensure all active subscriptions are terminated when the component is destroyed. This improves performance, prevents unnecessary background processing, and ensures consistent subscription handling throughout the application.
#Issue Number
WEB-428
Screenshots, if any
N/A
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.