Matthew Bonig

Blog Resume Timeline

A Typescript Runtime for Lambda and Why You May Not Want To Use It

March 3, 2019

aws, lambda, serverless, typescript

At AWS’s re:Invent a huge announcement was dropped for Lambda: custom runtimes! While Lambda already supported a number of good languages, like Node, C#, and Python, it is now possible to use a wide array of languages, as long as you are willing to build the custom runtime. Kelsey Hightower even famously showed off using Docker containers to create a runtime for Fortran.

So, it got me thinking about having Typescript as a Lambda runtime. Thanks to Angular, Nest.js and others, the popularity of Typescript is growing rapidly. While personally I don’t find much value in what it adds over vanilla Javascript, I can’t deny other people really love it.

While you can use Typescript by transpiling to a Node 6/8 runtime already available, it does have some downsides. Mostly, that any review/editing of your code through the Lambda console is considerably more difficult. While this isn’t something I find myself doing often, it is very handy in some situations and something I don’t like losing.

The initial work was easy, and largely based on the runtime put together for Node v10/11 here. It was pretty trivial to add a --require /opt/node_modules/ts-node/register and we were off to the races. However, the main issue at hand became very apparent quite quickly when running my simple test Lambda function, a simple ‘echo’ function. The final Report has some interesting numbers:

        
REPORT RequestId: 77d17761-98aa-4a28-9cc9-700a5d7b3d60
Init Duration: 1608.70 ms
Duration: 1001.15 ms
Billed Duration: 2700 ms
Memory Size: 128 MB
Max Memory Used: 128 MB
        
    

That was the cold-start time, almost 3 seconds. Not good! Too much time and already maxing out the memory. My test function was a simple echo, so that’s all runtime initialization! Subsequent runs were fine:

        
REPORT RequestId: 76ae5d3b-0aa0-49e4-b312-03c3d31cb0de    Duration: 28.41 ms
Billed Duration: 100 ms
Memory Size: 128 MB
Max Memory Used: 128 MB
         
    

So, still maxing the memory out with no actual logic. Not great. Can this be made better? Sure! Just have to up our memory allocation, since that also increases CPU and makes the Typescript compilation run quicker. At 1024 MB the cold-start time decreases somewhat:

        
REPORT RequestId: 4e125647-1b8a-48a5-943e-6bde2dfd792e
Init Duration: 1507.07 ms
Duration: 7.34 ms
Billed Duration: 1600 ms
Memory Size: 1024 MB
Max Memory Used: 179 MB
         
    

But 1.5 seconds for the runtime warm-up still isn’t good. Let’s keep going, 2048 MB:

        
REPORT RequestId: f28e76e6-2c1b-4eae-b9b8-44c86c50c427
Init Duration: 1565.81 ms
Duration: 9.46 ms
Billed Duration: 1600 ms
Memory Size: 2048 MB
Max Memory Used: 157 MB
        
    

Wow, no apparent affect. So, we're not bottlenecked by memory or cpu. In fact, just bumping it up to 192MB is enough to get comparable results:

        
REPORT RequestId: f57119d8-bcc8-4b04-a15d-8d8e2dfda94c
Init Duration: 1614.29 ms
Duration: 53.32 ms
Billed Duration: 1700 ms
Memory Size: 192 MB
Max Memory Used: 150 MB
        
    

So, there we go, that’s about the best we can do on reducing cold-start times. I think that’s just the nature of Typescript and TSC doing the compilation at warm-up. Unless they make a Typescript compiler that can work quicker or a straight-up Typescript runtime (and not just using Node), I don’t see this getting much better.

But, you do still get a Typescript runtime, which has some value, and if you can handle the cold-start times, then maybe this is a good option for you.

The repo can be found here. Follow the README and you can build a runtime.

So, there you go. You could build this Typescript runtime and build your Lambda function in pure Typescript. But, you’ll take a pretty big hit on every cold invocation and that really adds up. If these Lambdas were sitting behind an ALB or an API Gateway, I couldn’t recommend a paying client go this direction in most situations. Long cold-starts are something that have a very negative impact to the user. Perhaps this would be fine if you were triggering off of things like DynamoDB Streams or Kinesis where long cold-starts are less of an issue. Maybe in the future there will be some better options for getting Typescript into Lambda.