How to leverage AWS Lambda timeouts with Go context cancellation

In some scenarios you may want to not hardcode timeouts directly at the level of HTTP client inside your AWS Lambda function but share Go context across the whole function and pass it to each operation, making good use of the Go context cancellation. It’s supported by many packages including standard Go HTTP client or AWS SDK for Go.
Thanks to this approach every subsequent operation in your lambda function will get the best possible chance to finish successfully. Otherwise, you may end up with timing out your operations too soon, even tough they would finish successfully if they only had slightly more time “borrowed” from other operations that wouldn’t use up their whole time assigned to them. This approach is well described in this article.
Go context.Context cancellation
Before diving into lambda timeouts, very brief code explanation of what context cancellation is in Go. Then we’ll see how we can combine these two concepts together.

Output of the program above will be following:
Get http://www.mocky.io/v2/5e369e393200006400ae3cb2?mocky-delay=5s: context deadline exceededHow to leverage this in AWS Lambda?
Every time lambda function is invoked handler receives a context with already set deadline based on the timeout that you have assigned to your function. Deadline in this case is basically a point in time when our function should terminate. Currently maximum timeout for a lambda function is 15 minutes.
We can leverage this fact by grabbing the deadline from a context that is passed to our lambda function and creating our own child context with a deadline that is reduced by small amount of time in relation to original deadline.
In our example we’ll prepare a child context with deadline of 100ms less than original deadline, but you should adjust this accordingly to your needs. Personally, I like to think about this moment before the end of function execution as a good opportunity to gracefully finish the function, e.g. rollback transactions, close open resources etc.

When you may want to consider this approach?
- You want to give each subsequent operation the very best chance to be accomplished and you want to gracefully finish execution of you function. Keep in mind that this approach may be valid only in serverless approach where each lambda function has a fixed timeout.
- When you’re in early phase of your project and you’re not sure about actual response times of your integration points and you don’t want to hardcode some roughly guessed numbers.
- When you’re integrating with services that you’re not sure about their consistency / their response time is not consistent or you can’t put any numbers based on your previous experience in interacting with them
In my opinion this should be default approach to timeouts in lambda functions unless you have some specific requirements. Tweaking with specific timeout values can be done afterwards when we gain more insights into our application metrics.