-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(core): many nested stacks make NodeJS run out of memory (#11250)
Because of a confluence of factors (but principally the poor design behind how cross-stack references are detected and rendered), having a lot of nested stacks leads to a wasteful amount of work being done and the NodeJS program running out of memory. An internal project has 26 nested stacks, and was taking `2m20` to synth and ran out of the default NodeJS heap (having to increase it to `8G`). The reason is because the entire construct tree is being resynthesized for every nested stack, and because this particular application was using CloudWatch Dashboards, a lot of temporary tokens were being created (and retained in the global encoded token map) on each of the 26 synthesis operations. With the current patch applied, that same project synthesizes in `51s` and takes `~1G` of memory (this can be brought down further to about 60% of this number by caching of `Lazy`s, but that change will be introduced in a different PR). The problem ----------- (Legacy) assets work by exploiting cross-stack references: they add `CfnParameter`s at the top-level, and then reference them inside a nested template. Cross-stack reference detection picks this up and adds `CfnParameters` and `AWS::CloudFormation::Stack` parameters to all stacks on the root path to "transport" the values to the right nested stack. To define a nested stack asset, we need to know the template hash, so we must have already resolved stack references to their final form. However, the process of defining the nested stack assets may add new references (as mentioned before) which need to be resolved, leading to our existing implementation of calling `resolveReferences()` once for every nested stack in the application. Calling `resolveReferences()` leads to all Tokens being resolved. It happens that the Tokens are `Lazy`s that resolve to other `Lazy`s, perhaps indirectly in the following form: ```ts Lazy.stringValue({ produce: () => someFunction() }) function someFunction() { return Lazy.stringValue({ produce: () => /* something else */ }); } ``` For every resolution for every nested stack, the *inner* `Lazy` would be reconstructed anew, and subsequently registered in the global token map (along with its stack trace). A wasteful amount of temporary Token objects are created and infinitely retained in this process. The fix ------- We resolve references twice, instead of once per nested stack. Once at the start, to resolve all cross-stack references put there by the user to their final form. Then we add nested stack assets in the right order, and then we finally do one more "resolve references" to make sure the stack asset parameters are forwarded to the right nested stacks. The nested stack asset hash will now be calculated over a template that is not yet the final template, but the hash still includes the user-mutable parts and will change if the user introduces changes (compare it to a `sourceHash` we use for other assets). ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
- Loading branch information
Showing
5 changed files
with
80 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters