Serverless infrastructures are cool af and cheap.
Why and how?
Usually when one wants to make something on the cheap and quick in cases like hackathons or minimum viable products, one usually runs to their favorite app engine of choice or a couple AWS EC2’s. You pay for unesscessary computing capacity when your service isn’t being used and when things get hot enough where you need scale up momentarily (i.e. Super Bowl), you have to pay even more to antipate a high work load even though there can be times where that much computing power isn’t needed, which can rack up your minute costs. As you’ve probably experienced or have noticed, there’s a lot of unnecessary cost, and as someone not being backed by a bunch of VCs or daddy money, this can be annoying and taxing on your pockets. The wonderful solution to this problem is a serverless infrastructure where you are only charged for when your service is actually being used while it scales up as needed.
This infrastructure is “serverless” meaning you don’t need to worry about buying a bunch of computing space and serving backend code on it. All you have to do is give a provider (like AWS) functions of code that run only when it needs to run. Of course there are servers involved with that, but you as a developer only need to worry about the code itself while a provider worries about scaling and servers.
You may be asking, “what about the routing I get with “X” web framework, will I not have that?”, yes and no. In the case of AWS Lambda (AWS’s serverless computing platform), you need to use AWS API Gateway to create your routes while you give Lambda the code for said routes as separate functions within Lambda. You can think of each Lambda function as the code behind each route you would traditionally have in a REST API. Also depending on whatever web framework you use, there may be open source projects that can map your entire existing web application to API Gateway while all you have to do is deploy your code to Lambda (like this one for Flask/Django: https://github.com/Miserlou/Zappa).
However, the beauty of serverless is the ability to breakdown your functions into microservices that can be mapped to API Gateway or triggered by events. I’m not going to get into what microservices are in this post (as there are many out there on what they are), but essentially it’s the breakdown of a software application into small, modular parts (services) that run unique processes that are deployed independently vs one huge application you deploy with everything in it. Essentially if your application becomes this huge complex thing, you would want to shift to a microservices architecture code so you don’t have to redeploy an entire application for one small change somewhere.
AWS Lambda + Other AWS Services
AWS’s service for running code when need be at an unlimited scale (serverless) is called Lambda. Instead of buying machines, you make functions that run on AWS’s many machines when needed. You can upload as much code as you want to a function, but of course you should make your architecture more organzied than just plastering the whole thing to one function. The beauty of using Lambda is that it plays with a bunch of other AWS services to create event driven functions.
AWS Lambda functions can be triggered by tradtional API calls via API Gateway or by AWS’s many services like DynamoDB, S3, SNS, and Kinesis. For example, every time my DynamoDB table is written to, I can have a Lambda function subscribe to a stream from the table to have code run when my database is updated. This can be very useful in a scenario like a voting system where I want to perhaps keep track of all collective votes for a candidate in some other table to serve real time results to a web page. You can even schedule Lambda function calls easily with another AWS service called CloudWatch Events. Sometimes you may need to run code outside the scope of the tradtional API call from the client-side and Lambda makes this really easy.
Use Case: Voting System via Messenger Bot
Problem and Solution
I had to recently build a voting system for a MC competition that integrated with an existing FB Messenger Bot where over a thousand people attending would be pressing a button to vote at the same time to vote the winner of each MC contest. This is obviously a temporary feature for an event where I wouldn’t want to have my client spend a bunch of money on a higher tier machines to be running when not needed or not be able to match the number of people who would show up to said event if capacity went above said machines. Also with a voting system, many database writes would have to happen very quickly and an easy way to batch a bunch of writes together would be needed to do efficient writes. The solution was using AWS Lambda with AWS DynamoDB (a NoSQL database), AWS SNS, and AWS SQS.
My voting system microservice consisted of a Lambda function that read a number of votes from an SQS queue to batch the number of votes I received to optimize my writes to my votes table in DynamoDB when trigged by an SNS notification and another Lambda function that subscribed to a DynamoDB stream from the votes table that tallied votes to write to a results table for each contest to be read by a web app via an API endpoint to get live results. The voting would still regularly occur from the FB Messenger Bot, but once a vote was put into my SQS queue and an SNS notification was triggered, my microservice handled everything. Now I can be sure I have a highly scalable voting solution that won’t break come event time when everyone is mashing the same buttons at once as long as I up the read and write counts on my DynamoDB when the event rolls around which is just a few clicks.
The ability to do a bunch of event driven functions wouldn’t have been as easily possible without AWS Lambda and the many AWS services that work with it, and that’s the beauty of serverless. After making this voting microservice, I even converted the FB Messenger Bot microservice to Lambda as well so even the bot can handle the blast of requests from voting so everything is now highly scalable.
Why DynamoDB and SNS+SQS
DynamoDB is an awesome NoSQL database solution and I would recommend watching videos and reading up on it as it is fully managed. If used correctly, it can create a beautifully scaled storage solution for you that is consistent and fast. The only thing you need to worry about is increasing reads and writes capacities on tables to keep up with increase of traffic, if you created a good schema (there are many videos provided by AWS on best practices), as it is infinitely scalable. If you’re coming from a SQL background, I would advise not to bring SQL mentality to NoSQL as it won’t really work. If there is one tip I can give, it’s to partition your hash keys with either tacked on dates or random numbers (if you’re coming from SQL it’s like the primary key, but it doesn’t work nearly the same at all where it auto-increments) to distribute database items evenly because if you don’t, there will be a bad effect on throughput. Creating composite keys also creates optimized queries in DynamoDB because filters on attributes on queries are applied after the query of all items described by hash and range keys (more info on this is available on the Internet, including in AWS docs). With DynamoDB, you want to design schema more around how the application will use and need the data.
SNS (Simple Notification Service) is a service provided by Amazon that let’s you trigger notifications that can be mapped to really most things. In this case, I have a Lambda function that is subscribed to a notification topic that will be triggered any time I send a message to that topic. Since I can’t trigger Lambda when I add something to my SQS queue, I needed to use SNS to do it. In my code, I simply had a message published to my SNS topic each time a vote was added to the queue.
SQS (Simple Queue Service) is a service that provides queues. Queues are essentially data structures that hold a list of items in a first in, first out order and that’s exactly what this does. The standard queue in SQS has nearly unlimited transactions per second and the FIFO queue in SQS is limited to 300 transactions per second. The difference between the two is not only in transactions per second, but also in ordering as the standard queue may put messages out of order by accident. I just used a standard queue as ordering didn’t really matter as I just wanted to grab a number of elements at a time to batch write instead of just having a bunch of writes happen at the same time in the Messenger Bot function.
So, serverless computing really does fundamentally change the economics of computing and makes backend development more affordable and efficient. As a penny conscious college student who is looking for quick and low maintenance solutions, AWS Lambda is a great option to go with. Much love.