Skip to content

PlatformRef is not destroyed in SSR if error happens during the bootstrap() phase (e.g. when an APP_INITIALIZER rejects) #58111

Closed

Description

@Platonn

Which @angular/* package(s) are the source of the bug?

platform-server

Is this a regression?

No

Description

Problem

In SSR in renderApplication() (or analogically in renderModule()) when an error happens in the bootstrap() (or respectively in platformRef.bootstrapModule()) - for instance when one of the APP_INITIALIZER rejects - the PlatformRef is not destroyed. Then ngOnDestroy() hooks of singleton services are not called at the end of the SSR, which can lead to possible memory leaks in SSR.

Reason

The logic for destroying the PlatformRef is lying only at the end of the _redner() function body. So if an error happens earlier during the bootstrap() phase, the rest of the _render() function (including its ending which destroys the PlatformRef) is skipped.

Since the PlatformRef is not destroyed, the EnvironmentInjector (or the ModuleRef.injector) is not destroyed in cascade. Therefore the ngOnDestroy() hooks of singleton services provided in such an injector are not invoked. This can can lead to memory leaks in custom apps which have important teardown logics (e.g. unsubscribing from RxJs observables) in their singleton services' ngOnDestroy hooks).

Additional flaw in case of renderModule()

In other words, to fix the bug:

  • in case of renderApplication, it suffices to destroy PlatformRef even on failed bootstrap() function, e.g. wrap it with a try{} block and call the PlatformRef.destroy() in the finally{} block:

    export async function renderApplication<T>(/*...*/) {
      /* ... */
       const platformRef = createServerPlatform(options);
       try {
         const applicationRef = await bootstrap();
         return await _render(platformRef, applicationRef);
       } finally {
         platformRef.destroy();
       }
  • in case of renderModule, we need to do the analogical fix as above (i.e. destroy PlatformRef in the try-finally block ) AND moreover I believe (but please correct me if I'm wrong!) we should setup the PLATFORM_DESTROY_LISTENER there, similarly to how we already do it for the renderApplication flow.

Please provide a link to a minimal reproduction of the bug

https://github.com/Platonn/ng-ssr-memory-leak-bug

Please provide the exception or error you saw

No response

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 18.2.5
Node: 20.14.0
Package Manager: npm 10.7.0
OS: darwin arm64

Angular: 18.2.5
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router, ssr

Package Version

@angular-devkit/architect 0.1802.5
@angular-devkit/build-angular 18.2.5
@angular-devkit/core 18.2.5
@angular-devkit/schematics 18.2.5
@schematics/angular 18.2.5
rxjs 7.8.1
typescript 5.5.4
zone.js 0.14.10

Anything else?

I've provided PR with a proposed fix #58112

Activity

  1. added this to the needsTriage milestone on Oct 7, 2024
  2. added a commit that references this issue on Oct 9, 2024
    9e82559
  3. angular-automatic-lock-bot commented on Nov 9, 2024

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  4. locked and limited conversation to collaborators on Nov 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions