With AWS Lambda and FaaS, squeezing every ounce of performance to reduce cost and improve user experience can be important. While adopting Rust is not a decision that should be made lightly for mature organizations if it's not a skill that exists already— this is my journey writing one and learning Rust the hard way to prove its value.
The final code for all the lambda functions can be found on GitHub here:
At this point, I assume you know AWS Lambda and Rust, see further links at the bottom for help getting started!
Coming from C#/Java/Python/etc, it took me a while to figure out how to create static objects to ensure we are not repeating code/processing inside the lambda handler function.
This is what it ended up looking like:
The idea here is that the Valid Issuer list and JWK list are only parsed once from the Lambda environment variable.
If the auth provider supports key rotation the assumption is that would be a manual step to update the Lambda environment variables. While its obviously good policy to perform key rotation, supporting that automatically means looking at performing a web request to retrieve JWK’s from Cognito and ensuring that we are safe from JWK attacks where the caller rotates the kid to force refreshing every time, etc (leaving this for another day).
Implementing this code in Rust as below:
Validation is done using a fairly popular Rust crate called jsonwebtoken, and for what it's worth, some validation was done by jwt.io at https://jwt.io/libraries.
For this implementation, I went with validating:
Future steps could be to look at the audience, etc. The code snippet is below:
Method
Using Artillery, short bursts of up to 100 requests per second, the values below represent the 99th percentile for each runtime
In each case, the focus has been spent making the function code as efficient as possible, feedback is always welcome! (Please raise any improvements on GitHub).
Results
256mb Memory allocated.
TBD -> Golang,nodejs,java and testing on 512/1024mb.
Scenario
Assuming the authorizer is fronting a system that has constant use and spikes are negligible (to cost), at 1 million unique login/tokens a day (rest would be cached) -> 31 million a month.
Rust -> $9.30/month
Python -> $35.27/month
Dotnet -> $29.58/month
It wasn't easy for me to adopt Rust coming from higher-level languages, but looking at the results of building for large-scale use, then it certainly feels worth it both from a cost perspective (factor x3 saved) and from a performance perspective.
It doesn't feel like purely looking at a Lambda Authorizer as a use case provides enough clarity on any potential benefits, so watch out for part 2 where we look at a backend with real-world functionality to test out if language plays a big part in cost/performance on the AWS Lambda platform.
The AWS repositories below provide some great guidance on how to start with Rust + AWS Lambda:
The links below provide some great starting points for an API Gateway Authorizer: