I have an Angular 9 (v9.0.6) app working correctly with Universal (SSR) in it and I was performing the last set of tests prior moving to PROD when I noticed the application hanging with 100% CPU consumption. I analyzed the error and turned out to be a problem when the Angular app loads directly a route which is a lazy loaded module which performs a HTTP call.
If I load the angular via Home (or another route with no HTTP - but even the home, another lazy loaded module has the same component which makes the HTTP call) eveything works fine. I can navigate to all routes without any issue and the APP works like a charm. If, however I go, to, lets say www.mywebsite/lazy-loaded-module straight in a new tab, I believe there is something in the bootstrap process which is preventing for all tregistrations or at least the HTTPClientModule to be registered correctly and then I get the failure.
To start with, I have the HTTPClientModule
registered only once in the AppModule
. The error I get is:
(node:17624) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
The CPU goes to 100% and the application hangs. Again, that does not happen if I go to www.my-website.com
then I navigate through the app to /my-lazy-loaded-module
I tried something else then: Including (I know I shouldn't) the HTTPClientModule inside my lazy loaded module and it worked as in the CPU issue went away however I got a different error:
ReferenceError: XMLHttpRequest is not defined
at BrowserXhr.build
Which has an impact in the content I would retrieve using SSR and rendering it because there is none. As a workaround that could do it but I'd like to know how this should be addressed. The node
version I have executing is: v10.16.2
UPDATE
To give more visibility around the HTTP call I'm making is just a standard call:
public getNext = (page: number, pageSize: number): Promise<IEventsPaged> => {
return this.http.get<IEventsPaged>(`${environment.apiBaseUrl}/events?page=${page}&pageSize=${pageSize})
.toPromise()
.then(r => r)
.catch((error: Response | any) => {
return Promise.reject(error);
});
}
UPDATE 2
I have updated to Angular 9.1.1 which fixes the buffer warning. The application still hangs, and it's only happening on one module. Other modules are also making a http call (same standard get, but with a different service) without issues.
** UPDATE 3 **
I have found the root cause of the issue. It had nothing to do with HTTP eventually. What was happening was that in the page I'm referring I have an angular animation very simple. It's a triangle which gets bigger or smaller applying an infinite animation via standard angular animation. To achieve the infinite effect I was hooking into the animation.done
event to change the state to either big or small. Well, if you hard refresh the page on that route where that animation is placed, you end up on an infinite loop like this:
{ element:
HTMLDivElement {
parentNode:
HTMLUnknownElement {
parentNode: [HTMLDivElement],
_previousSibling: [HTMLDivElement],
_nextSibling: [HTMLImageElement],
_index: undefined,
_childNodes: null,
_firstChild: [Circular],
nodeType: 1,
ownerDocument: [Object],
localName: 'app-bottom-angle',
namespaceURI: 'http://www.w3.org/1999/xhtml',
prefix: null,
_tagName: undefined,
_attrsByQName: [Object],
_attrsByLName: [Object],
_attrKeys: [Array],
__ngContext__: [LComponentView_CalendarIntroductionComponent],
_classList: [DOMTokenList],
_nid: 79 },
_previousSibling: [Circular],
_nextSibling: [Circular],
_index: undefined,
_childNodes: null,
_firstChild: null,
nodeType: 1,
ownerDocument:
{ parentNode: null,
_previousSibling: [Circular],
_nextSibling: [Circular],
_index: undefined,
_childNodes: null,
_firstChild: [Object],
nodeType: 9,
isHTML: true,
_address: 'http://localhost:54818/en/calendar',
readyState: 'loading',
implementation: [Object],
ownerDocument: null,
_contentType: 'text/html',
doctype: [Object],
documentElement: [HTMLHtmlElement],
_templateDocCache: null,
_nodeIterators: null,
_nid: 1,
_nextnid: 152,
_nodes: [Array],
byId: [Object],
modclock: 23,
_scripting_enabled: true,
defaultView: [Object],
_lastModTime: 1 },
localName: 'div',
namespaceURI: 'http://www.w3.org/1999/xhtml',
prefix: null,
_tagName: undefined,
_attrsByQName:
[Object: null prototype] { '_ngcontent-sc33': [Object], class: [Object], style: [Object] },
_attrsByLName:
[Object: null prototype] {
'|_ngcontent-sc33': [Object],
'|class': [Object],
'|style': [Object] },
_attrKeys: [ '|_ngcontent-sc33', '|class', '|style' ],
_classList:
DOMTokenList {
'0': 'position-absolute',
'1': 'z-index-plus-1',
'2': 'bottom-0',
'3': 'right-0',
'4': 'left-0',
'5': 'mb-4',
'6': 'ng-tns-c33-1',
'7': 'ng-trigger',
'8': 'ng-trigger-scale',
'9': undefined,
'10': undefined,
_getString: [Function],
_setString: [Function],
_length: 9 },
__ngContext__:
LComponentView_BottomAngleComponent [
[HTMLUnknownElement],
[TView],
211,
[LComponentView_CalendarIntroductionComponent],
null,
null,
[TNode$1],
[LCleanup],
[BottomAngleComponent],
[Object],
[AnimationRendererFactory],
[AnimationRenderer],
null,
null,
null,
[LComponentView_CalendarIntroductionComponent],
[Circular],
null,
0,
[Circular],
'big' ],
_nid: 80,
_style:
{ _element: [Circular],
_parsedStyles: [Object],
_lastParsedText: 'transform: scale(1); transform-style: preserve-3d;',
_names: [Array] } },
triggerName: 'scale',
fromState: 'small',
toState: 'big',
phaseName: 'done',
totalTime: 1200,
disabled: false,
_data: 1006 }
{ element:
HTMLDivElement {
parentNode:
HTMLUnknownElement {
parentNode: [HTMLDivElement],
_previousSibling: [HTMLDivElement],
_nextSibling: [HTMLImageElement],
_index: undefined,
_childNodes: null,
_firstChild: [Circular],
nodeType: 1,
ownerDocument: [Object],
localName: 'app-bottom-angle',
namespaceURI: 'http://www.w3.org/1999/xhtml',
prefix: null,
_tagName: undefined,
_attrsByQName: [Object],
_attrsByLName: [Object],
_attrKeys: [Array],
__ngContext__: [LComponentView_CalendarIntroductionComponent],
_classList: [DOMTokenList],
_nid: 79 },
_previousSibling: [Circular],
_nextSibling: [Circular],
_index: undefined,
_childNodes: null,
_firstChild: null,
nodeType: 1,
ownerDocument:
{ parentNode: null,
_previousSibling: [Circular],
_nextSibling: [Circular],
_index: undefined,
_childNodes: null,
_firstChild: [Object],
nodeType: 9,
isHTML: true,
_address: 'http://localhost:54818/en/calendar',
readyState: 'loading',
implementation: [Object],
ownerDocument: null,
_contentType: 'text/html',
doctype: [Object],
documentElement: [HTMLHtmlElement],
_templateDocCache: null,
_nodeIterators: null,
_nid: 1,
_nextnid: 152,
_nodes: [Array],
byId: [Object],
modclock: 23,
_scripting_enabled: true,
defaultView: [Object],
_lastModTime: 1 },
localName: 'div',
namespaceURI: 'http://www.w3.org/1999/xhtml',
prefix: null,
_tagName: undefined,
_attrsByQName:
[Object: null prototype] { '_ngcontent-sc33': [Object], class: [Object], style: [Object] },
_attrsByLName:
[Object: null prototype] {
'|_ngcontent-sc33': [Object],
'|class': [Object],
'|style': [Object] },
_attrKeys: [ '|_ngcontent-sc33', '|class', '|style' ],
_classList:
DOMTokenList {
'0': 'position-absolute',
'1': 'z-index-plus-1',
'2': 'bottom-0',
'3': 'right-0',
'4': 'left-0',
'5': 'mb-4',
'6': 'ng-tns-c33-1',
'7': 'ng-trigger',
'8': 'ng-trigger-scale',
'9': undefined,
'10': undefined,
_getString: [Function],
_setString: [Function],
_length: 9 },
__ngContext__:
LComponentView_BottomAngleComponent [
[HTMLUnknownElement],
[TView],
211,
[LComponentView_CalendarIntroductionComponent],
null,
null,
[TNode$1],
[LCleanup],
[BottomAngleComponent],
[Object],
[AnimationRendererFactory],
[AnimationRenderer],
null,
null,
null,
[LComponentView_CalendarIntroductionComponent],
[Circular],
null,
0,
[Circular],
'small' ],
_nid: 80,
_style:
{ _element: [Circular],
_parsedStyles: [Object],
_lastParsedText: 'transform: scale(1.2); transform-style: preserve-3d;',
_names: [Array] } },
triggerName: 'scale',
fromState: 'big',
toState: 'small',
phaseName: 'done',
totalTime: 1200,
disabled: false,
_data: 1007 }
And your application hangs. I don't know if there is a better way of achieving infinite animations using Angular animations and whether it should be up to the developer to "know" that SSR will be vulnerable with them. I guess if you think that they deal with HTML elements you could argue that they all should be using isPlatformBrowser
within them.