Thanks to visit codestin.com
Credit goes to github.com

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
@Platonn

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions