I often need to debug complex Lambda setups that are tightly integrated into other services and are hard to simulate in my local dev environment. Even if I manage to grab the event JSON it is hard to send the response back in a timely manner. What I always wanted was to invoke Lambda handlers locally in response to AWS events in real time. This post describes one such solution I've been using successfully for the last few weeks.
The core of my solution is an AWS lambda function that acts as a proxy. It sends both
Context to a predefined SQS queue and then waits for a response that is deserialized and passed back onto the runtime.
In the meantime, you can read the message from the queue, deserialize
Context, process it locally and send back a response. If your local lambda fails you can re-read the message until either the proxy lambda times out or you send back a reply.
The benefit of this approach vs using a local mock environment is the full integration in your infra. It is not always possible to use mock input or try to copy the input by hand. The proxy function will give you near-real time request/response with real data.
- Lambda Runtime - a modified Lambda runtime for Rust that adds a few required features
- Lambda Handler Proxy - a handler that relays data to/from SQS queues
- Request Queue - an SQS queue for forwarding event details to the dev environment
- Response Queue - an SQS queue for forwarding response details from the dev environment
- Lambda Handler Wrapper - a Lambda handler wrapper running in dev environment and communicating with SQS queues
- Lambda Handler - the handler function that is supposed to run on AWS Lambda, but will be debugged in the local dev environment
If you are not familiar with AWS SQS you may not know that the messages have to be explicitly deleted from the queue. The request messages are deleted by the handler wrapper when the handler returns a response. This allows re-running the handler if it fails before sending a response, which is a handy debugging feature. The response messages are deleted by the handler proxy as soon as they arrive.
It is possible for the response to arrive too late because either the Lambda Runtime or the caller timed out. For example, AWS APIGateway wait is limited to 30s. The Lambda function can be configured to wait for up to 15 minutes. I usually finish debugging regardless of the timeout and then re-run the request.
The lambda handler running on your local machine has to be wrapped into a local client that reads the queue, de-serializes the payload into your required format, calls your handler function and sends the response back. It is the exact reverse of what the proxy function running at the AWS end does. See local.rs example for a sample Rust implementation. It should be easy to port the sample wrapper into the language of your Lambda handler.
Check out the source code on GitHub: https://github.com/rimutaka/lambda-debug-proxy.