AWS Bites Podcast

77. How do you use Lambda Response Streaming?

Are you tired of waiting for your Lambda functions to finish before getting a response? Well, now you don't have to! In this episode of the AWS Bites podcast, we will talk about Lambda Response Streaming, a new feature recently added by AWS that lets you stream responses from your Lambda functions in real time.

We'll start by explaining what Lambda Response Streaming is and how it differs from buffering. We'll also discuss HTTP Chunking and other benefits of streaming. If you're a Node.js developer, you'll be happy to know that we'll cover how to work with streams in Node.js and how the new Lambda Response Streaming API works with the Node.js runtime.

But that's not all! We'll also discuss how to consume Lambda Response Streaming responses and compare that with S3 Object Response. And if you're wondering about pricing and quotas, we'll cover that too.

Finally, we'll answer the question on everyone's mind: will we get streaming requests as well? You'll have to watch the video to find out!

So if you're interested in learning more about Lambda Response Streaming and how it can improve the performance of your serverless applications, make sure to tune in. We promise it'll be worth your time.

AWS Bites is sponsored by fourTheorem, an AWS Consulting Partner offering training, cloud migration, and modern application architecture.

In this episode, we mentioned the following resources:

Let's talk!

Do you agree with our opinions? Do you have interesting AWS questions you'd like us to chat about? Leave a comment on YouTube or connect with us on:

00:00:53

AWS Bites is sponsored by fourTheorem. fourTheorem is an AWS partner for migration, architecture, and training. Find out more at fourtheorem.com. That link is in the show notes. Okay, Luciano, for Lambda response streaming, let's talk a little bit about what it is. It was announced, I think, just about a week ago, and it's a Lambda feature and it's currently only possible for Node.js runtimes and custom runtimes as well. And the idea here is that it allows you to send a response from a Lambda function incrementally. Previously, you had to kind of build up your whole response and then send it back in one go. So instead of sending it back in one block, you can now start to send some bytes as soon as you have them and your client can start receiving them. So this is useful for streaming data, like let's think about some use cases, let's say records from a CSV file if you're generating them on the fly, or even server side rendering. I think that's a big use case for this kind of functionality. So the client can start receiving the data straight away, even while processing is still happening. Now, how do you actually integrate this into your client? So it currently works with Lambda function URLs. This is something we talked about in a very recent episode, link is in the show notes, but you can also use that then with a CloudFront distribution in front of it if you want to get all the benefits of caching and edge locations with CloudFront CDN. It doesn't really work with API Gateway or Application Load Balancer. I mean, it kind of works, but it doesn't give you the streaming benefit. So you can send use it with an API Gateway in front of it, but the API Gateway is still going to buffer it up for you. But there are actually some subtle differences when you use it with API Gateway that give you some benefits and we'll cover that later. On support, it's supported in CloudFormation and SAM. I mean, you don't actually have to change the Lambda function itself, only the code. But if you want to use it with function URLs, there is a new CloudFormation feature. It's not available in CDK yet. It is available in SAM and it's not yet available in the serverless framework, although there is a pull request open for it. Luciano, you're a bit of a streaming guru, if you don't mind me saying so. What is a stream really and how does it differ from buffering? Now I feel the pressure.

00:04:21

So again, in a CSV file example, what that can be useful for is the client can start to do analysis on that CSV data. Maybe it's doing a query so I can filter out the records that are relevant and keep only the ones that display to the user the ones that match the query. Or maybe if it needs to do some kind of streaming aggregation, I don't know, maybe you're calculating a sum or an average or something like that, you can start to do all the computation real time. And even with partial data, you can start to show something useful to the user. Other examples are you could, for instance, if you are plotting a chart that has thousands or millions of points, if those points are sent to you in a streaming fashion, you can start to display them straight away as soon as they arrive rather than waiting for the entire response to arrive. Similarly, you could render pictures that are encoding formats that are progressive, so you could start to render the picture as it arrives, even before it's all received. Or even web pages, you mentioned the case of server-side rendering pages, and I think this is kind of a common trend in the front-end world. Lots of single-pages application frameworks now have capabilities to server-side render to make sure that you get the advantage of that time to first byte, which is a very important metric on the web today. Search engines will prioritize web pages that will start to render as soon as possible. So this is probably the most interesting use case, and I would bet that this is the main motivation why AWS spent energies enabling this feature, because I've seen a lot of other competitors trying to support the story of if you're using Svelte, if you're using Vue.js, if you're using React, if you're using Solid or Quick, they all offer today ways to server-side render and be able to send dynamic content to the users, and probably Lambda needs to be on par with these other engines and make sure that this use case is supported as well. Now, another interesting technical detail is that if you're really curious to understand what is the actual protocol that gets used, this is just using plain HTTP chunked encoding behind the scenes, and we will have a link in the show notes if you're curious, but the idea is that because you need to send small frames of data, you need to tell the receiving side how big is every frame, so there is a protocol that kind of documents how do you encode that information, so not just the raw data, but encapsulated in a frame that also specifies the length of that frame, and then of course there is a way to mention, well, to specify when the entire stream is over and the entire request can be considered completed, so I hope that kind of clarifies what do we mean when we say buffering and streaming and what are the benefits there and the use cases, but I don't know, maybe there is something more to add on why one model could be better than the other or when one is more convenient than the other.

00:13:14

So you mentioned that there's lots of different cool things you can do with Node.js stream. So you could use pipeline if you've got multiple steps in the pipeline to create those streams. Like we talked about gzipping and base64 encoding, that would be one example you can do with it. Now, it is a bit of a weird thing, this global object, awslambda. We haven't had this before. I'm slightly surprised by this because globals are generally frowned upon. This is weird for a few different reasons. Your linter or TypeScript will complain about this object because it's just not the done thing these days to have globals. So you'll have to configure your linter to ignore this global. You won't be able to run your Lambda function code globally. So you'll have to fix something for your unit tests. It does seem to work with local invoke, which is good news. But I did try the latest serverless framework version and the one from the PR that has this function streaming URL support. It didn't work with local invoke. It didn't know what the AWS Lambda global was at all. So it's a little bit of a strange kind of black box that you have to work with. The code doesn't seem to be publicly available on GitHub. Although I think Luciana, you found that there is something you can find from the Lambda emulator container, the container image for Lambda. You can actually reverse engineer that a little bit and find out how it works. If you don't want to deal with all of these problems, MIDI has actually been very quick and added support for response streams and added a nice interface that hides a lot of the complexity. There's a link to the documentation in the show notes. Now, Luciano, is there anything you'd like to add to that? And maybe you can talk about how do you actually read the response of a Lambda using response streaming?

00:22:20

You probably will end up using something like a strict resigned URLs, which by the way, something else we talked about before, we will have the link in the show notes as well if you're curious. So I think once we get now, since we got now the response streaming functionality, it's legit to ask ourselves, are we going to get also requests streaming? So maybe we can support uploads or other use cases where even the request is streaming, or maybe when you need to send a lot of information to the Lambda and you want the Lambda to start to process that information as soon as possible. Now, I don't really have an answer, of course, we are just speculating here, but I think this was a couple of years ago, I was curious about the same question. We didn't even have response streaming at the time. And I was just trying to understand how the Lambda runtime was working behind the scenes. And the way I did the research was basically, okay, let me try to build a custom runtime myself for Node.js and see if I can make that custom runtime closer to an actual web framework in Node.js. So rather than receiving an event and the context, I actually wanted to be able in my handler to receive a request object and a response object where the request object was a readable stream and the response object was a writable stream, which is basically the lower level interface that you get in any web framework, even the HTTP library that is built in with Node.js. So I was actually able to achieve that in a bit of a hacky way. And the reason why that worked is because the Lambda runtime, when you want to build a custom runtime, AWS gives you two HTTP endpoints to work with. The first HTTP endpoint is like a polling endpoint where you have to constantly poll from your runtime to see, is there a new message there? Is there a new event that I need to handle?

Help us to make this transcription better! If you find an error, please submit a PR with your corrections.