serverless: JavaScript heap out of memory

This is a Bug Report

Description

Endless recursive file loading exceeds process memory limits.

When trying to remove the serverless.yml here: https://github.com/Nordstrom/hello-retail/tree/master/web

With the command (STAGE is properly defined and references an existing stack):

../node_modules/.bin/sls remove -s $STAGE -v
  • What went wrong?
<--- Last few GCs --->

[22378:0x102804600]   192582 ms: Mark-sweep 1484.5 (1567.0) -> 1484.5 (1567.0) MB, 2065.8 / 0.0 ms  allocation failure GC in old space requested
[22378:0x102804600]   194638 ms: Mark-sweep 1484.5 (1567.0) -> 1484.4 (1536.0) MB, 2056.4 / 0.0 ms  last resort gc 
[22378:0x102804600]   196780 ms: Mark-sweep 1484.4 (1536.0) -> 1484.4 (1536.0) MB, 2141.0 / 0.0 ms  last resort gc 


<--- JS stacktrace --->

==== JS stack trace =========================================

    2: arguments adaptor frame: 1->2
Security context: 0x90122ec0d31 <JS Object>
    3: _settlePromiseFromHandler [<src-root>/hello-retail/node_modules/serverless/node_modules/bluebird/js/release/promise.js:~496] [pc=0x20e01cfbd1f3](this=0x2d4a7d8ea59 <a Promise with map 0x17140f73cc21>,handler=0x2d4a7d97671 <JS Function getValueFromSource.then.valueToPopulate (SharedFunctionInfo 0x2b325a77f011)>,receiver=0x90122e04311 <undefined>,val...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: node::Abort() [~/.nvm/versions/node/v7.2.0/bin/node]
 2: node::FatalException(v8::Isolate*, v8::Local<v8::Value>, v8::Local<v8::Message>) [~/.nvm/versions/node/v7.2.0/bin/node]
 3: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [~/.nvm/versions/node/v7.2.0/bin/node]
 4: v8::internal::Factory::NewByteArray(int, v8::internal::PretenureFlag) [~/.nvm/versions/node/v7.2.0/bin/node]
 5: v8::internal::SourcePositionTableBuilder::ToSourcePositionTable(v8::internal::Isolate*, v8::internal::Handle<v8::internal::AbstractCode>) [~/.nvm/versions/node/v7.2.0/bin/node]
 6: v8::internal::FullCodeGenerator::MakeCode(v8::internal::CompilationInfo*) [~/.nvm/versions/node/v7.2.0/bin/node]
 7: v8::internal::(anonymous namespace)::GenerateUnoptimizedCode(v8::internal::CompilationInfo*) [~/.nvm/versions/node/v7.2.0/bin/node]
 8: v8::internal::(anonymous namespace)::GetUnoptimizedCode(v8::internal::CompilationInfo*) [~/.nvm/versions/node/v7.2.0/bin/node]
 9: v8::internal::Compiler::Compile(v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Compiler::ClearExceptionFlag) [~/.nvm/versions/node/v7.2.0/bin/node]
10: v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*) [~/.nvm/versions/node/v7.2.0/bin/node]
11: 0x20e01ca063a7
12: 0x20e01ca0675a
13: 0x20e01ca07e55
14: 0x20e01cfbd1f3
15: 0x20e01cfe1eb4
Abort trap: 6
  • What did you expect should have happened?

Expected: removal of stack

Downgrading to serverless@1.11.0 allowed this to proceed as expected.

  • What was the config you used?

https://github.com/Nordstrom/hello-retail/blob/master/web/serverless.yml

  • What stacktrace or error message from your provider did you see?

see above.

Additional Data

I suspect that this change may have introduced the new behavior but am not certain: https://github.com/serverless/serverless/commit/971a27994c3566d9904f166a2e943c21ba4dfe54#diff-b921cbcedd2151d24391fb39acb84adeR88

  • Serverless Framework Version you’re using: 1.13.2, 1.14.0
  • Operating System: osx
  • Stack Trace: see above
  • Provider Error messages: n/a

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 21
  • Comments: 24 (16 by maintainers)

Most upvoted comments

I have been able to validate this fix plus a small delta (#4146) as resolving the matter for both the entirety of Hello, Retail! as well as our latest internal project that was also subject to the problem. Thanks @eahefnawy!

@dashmug You can check the comments in https://github.com/serverless-heaven/serverless-webpack/issues/299. There has been a lengthy discussion of people facing the TypeScript memory problem and I remember there was some workaround that did the job.

I’m excited to! Will report results.

Whilst trying to deploy https://github.com/laardee/serverless-authentication-boilerplate

~/serverless-authentication-boilerplate/authentication$ sls deploy
^E



<--- Last few GCs --->

[1763:0x77e0886bb0]   464860 ms: Mark-sweep 1935.7 (2020.2) -> 1935.4 (2000.7) MB, 2310.7 / 0.0 ms  (+ 0.0 ms in 0 steps since start of marking, biggest step $
.0 ms, walltime since start of marking 2311 ms) last resort
[1763:0x77e0886bb0]   468052 ms: Mark-sweep 1935.4 (2000.7) -> 1935.7 (2001.7) MB, 3190.0 / 0.0 ms  last resort


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x394c3289cef1 <JSObject>
    0: builtin exit frame: captureStackTrace(aka captureStackTrace)(this=0x394c32882241 <undefined>,0x1179e377dd99 <JSFunction CapturedTrace (sfi = 0x2c1f8a45$
71)>,0x1cdf2d0cf9b1 <CapturedTrace map = 0x3014df317651>)

    1: _then [/home/hendry/.node_modules/lib/node_modules/serverless/node_modules/bluebird/js/release/promise.js:~219] [pc=0x2e99750076c1](this=0x1cdf2d0bd4f9
<Promise map = 0x3014...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: node::Abort() [node]
 2: 0x77dfaf553f [node]
 3: v8::Utils::ReportOOMFailure(char const*, bool) [node]
 4: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [node]
 5: v8::internal::Factory::NewRawOneByteString(int, v8::internal::PretenureFlag) [node]
 6: v8::internal::Factory::NewStringFromOneByte(v8::internal::Vector<unsigned char const>, v8::internal::PretenureFlag) [node]
 7: v8::internal::Factory::NumberToString(v8::internal::Handle<v8::internal::Object>, bool) [node]
 8: 0x77df7c9d7a [node]
 9: v8::internal::JSStackFrame::ToString() [node]
10: v8::internal::ErrorUtils::FormatStackTrace(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::Object>
) [node]
11: 0x77df39ed70 [node]
12: 0x2e9974ebc8dd
Aborted (core dumped)

@erikerikson Exactly. The inner references have to be resolved first as their values build up the outer references. So the dereferencing itself has to work with states that are stored on a stack in this case and the system would be rewinded to that state after the inner reference has been evaluated.

Additionally the newly generated references can cause circular references depending on the value of the inner reference(s). The algorithm should be able to detect that, i.e. the dynamically generated reference has to be put into the map of visited objects. A point here is, that it is quite easy to detect these circular references and stop processing, but for the user it should show a kind of stack trace (i.e. the states that triggered a “rewind” or “reevaluate”) to really show where the root of the circle is located.

This appears to be the result of changing the strategy of the variable resolution code so that it doesn’t “replace in place” before recursively populating the resolved value (i.e. instead one should return the result and modify the original containing object before then trying to resolve the child).

I haven’t tried but the following seems like it would reproduce:

~/config.yml

someVal: ${self:custom.config.anotherVal}
anotherVal: foo

~/serverless.yml

custom:
  config: ${file(./config.yml)}
  1. sls loads ~/serverless.yml
  2. sls loads config.yml
  3. sls hasn’t updated the original serverless.yml-based object but begins populating what was loaded in 2. Namely ${self:custom.config.anotherVal}
  4. sls loads self and finds that the custom.config value is ${file(./config.yml)}: goto 2.

entirely speculation.