Serverless architecture has become one of the most efficient ways to manage variable workloads. The approach to build and run applications without having to manage the underlying infrastructure has to be a gift from heaven for Full Stack engineers to SREs and DevOps professionals. Even though there are many perks for serverless computing, there can be downsides to it as well. It completely depends on how you build your architecture.
AWS lambda plays a major role when it comes to serverless architecture in AWS cloud computing. There are must-dos and must-nots. Here are 7 common mistakes (must nots) that developers tend to make when orchestrating serverless workloads with AWS lambda.
1. Complex Application Architecture
Serverless is a piece of cake for anyone if they are dealing in a lower testing environment with fewer microservices and smaller chunks of code. But when it comes to a real-life production environment scenario, the architecture and the code base get a lot more complicated. AWS Lambda usage can vary from serverless websites to real-time data transformation to building a serverless IoT backend for your application. The more complex the architecture, the more complicated the lambda workflow will get.
If you are dealing with a lengthy project, the lambda invocation and execution time will take a considerable hit. And also, you will have to take care of the cold starts of your lambda functions (A cold start is a delay that is introduced from the cloud provider when executing a function that has stayed inactive for a period of time). This is one of the things that you need to steer clear of when dealing with AWS Lambda architecture.
- Divide large Lambda functions into smaller ones that contain less business logic.
- Use the micro-service approach instead of the monolithic approach.
- Keep the Lambda functions as focused as possible.
- The choice of language matters with cold starts -
- Less of a cold start — Python, NodeJS, Go
- More of a cold start — Java, C#
- Make sure to optimize your code if you are dealing with languages that have higher cold start times.
- Pulling out shared clients from the handler function.
- Keeping shared data in the /tmp directory.
- Any external configurations and dependencies can be saved and referred to later after the initial execution of the function.
- Use global variables, static variables, singletons, etc. to limit the reinitialization of variables and objects on every invocation.
- Use AWS Step Functions. AWS Step Functions is a low-code visual workflow service that can be used to build serverless applications. You can easily manage the flow of several connected Lambda functions using AWS Step Functions.
2. Unwanted Packages and Dependencies
As mentioned earlier, the bigger the deployment, the slower the function will start. Generally, Lambda functions require a number of code libraries and dependencies for a function to operate as intended. According to AWS documentation, Lambda will periodically update these libraries to enable the latest set of features and security updates. These updates can change how the function will work. Using complex dependencies might also make the function code more complicated.
- Use the function’s deployment package to control your dependencies.
- Minimize the complexity of the dependencies.
- Remove unnecessary items such as unused libraries, documentation, and development-only dependencies.
- Avoid using recursive patterns that cause run-away Lambda functions. Failure to manage recursive function calls can result in infinite loops.
Bad: Pulling in the entire SDK as a dependency
Good: Select individual modules from the SDK that will be used in the application
3. Reduced Scalability
AWS Lambda is best known for its high availability and flexible scaling. You can automatically scale your application or alter its capacity by controlling consumption units rather than individual server units. That being said, the same scalable solution can be overwhelmed quickly if it is not built correctly. If you have non-scalable and/or non-serverless services integrated downstream of your Lambda function, the Lambda function can easily crush the non-scalable services. Oftentimes, these limited-throughput, non-scalable services will be bottlenecks in your architecture. It is important to recognize these early on.
It is wise to adhere to the following if you are integrating non-scalable services along with Lambda functions.
- Make use of the AWS queueing service — Amazon Simple Queue Service (SQS) — to implement a buffering message queue in front of the non-scalable services.
- Decouple. Decouple. Decouple! Reduce and decouple your dependencies on non-serverless services as much as possible.
- Use asynchronous function calls. This is discussed further below in the next topic.
4. Use of Synchronous Calls
If you are a developer, you must be familiar with synchronous and asynchronous function calls. A synchronous function call is where service A makes a function call to service B to perform its own task, and waits until service B finishes. In an asynchronous function call, after the function call is made by service A, it does not wait until service B replies. Service A can go perform some other tasks without having to wait until the first function call is completed.
As you know by now, serverless workloads can be cost-intensive in their nature if proper architectural considerations aren’t in place. If you use synchronous calls inside your lambda function, it will lead to longer runtimes and higher Lambda implementation costs. If the Lambda function has to wait for a long while after making a synchronous call, it will keep billing you for that extra wait time. This is something you should always try to avoid when implementing serverless workloads with AWS Lambda.
- Use asynchronous function calls as much as possible.
- If you absolutely must use synchronous function calls, make sure to minimize the Lambda function’s idle wait time and configure the timeout conditions correctly.
5. Sharing is not Caring
In a standard application architecture, code sharing is commonly practiced by developers allowing them to build their features quickly (eg. Copy the same caching middleware built by someone else on the team). This is not ideal when developing on AWS Lambda.
When developers frequently use the same Lambda function code to base their own work on, it can lead to implementation inconsistencies and technical debt if someone modifies the shared codebase in different portions of their project at some point.
This form of functional dependency needs to be understood and eliminated with respect to Lambda functions.
- Use Lambda Layers — Consider creating a Layer and deploying code there if you want to reuse it in several functions.
- If you are using a source code management system, the Lambda function source code needs to be very fine-grained. Always have a 1:1 relationship between Lambda functions and the code repository.
- Use a shared API — Replace the common code base with a shared API. It can help simplify the codebase.
6. Lack of Cost Controls
Lambda charges are simply based on function execution time and the resources allocated. Since Lambda functions can scale extremely quickly, it can result in billing you a fortune by surprise. The following lists a few preferable recommendations that should be considered when building cost-optimized serverless architectures.
- Rightsizing — Choosing the optimal memory size. Test your Lambda function at each of the available resource levels to determine what the optimal level of performance is for your application (Perf or Performance tests/benchmarks definitely help).
- Avoid using recursive code. It can lead to infinite and/or I/O intensive loops if not configured correctly. This will increase your AWS billing with the never-ending function invocations.
- Set up a daily spending limit using CloudWatch alarms. Configure the alarm to notify you when your limit is reached.
- Deploy a CloudWatch Alarm that notifies you when function metrics such as ConcurrentExecutions or Invocations exceed your threshold.
7. Choose wisely — It’s not for everybody
According to AWS documentation, there are some common use-cases for Lambda functions. Some of them are file processing, data and analytics, hosting websites, and mobile applications. But in certain situations, you may have other alternatives that can improve performance. As the sub-topic depicts, serverless architecture is not for everybody. It does not have solutions for everyone.
- Gather all the requirements and functions needed before implementing your proposed architecture and figure out whether you certainly need to go for a serverless architecture.
- Consider incorporating Lambda to assist Amazon EC2, Amazon ECS, and Amazon EKS, and other purpose-built services to manage long-running compute tasks.
- Use purpose-built services for functions that act as orchestrators because they can result in idle time in the function.
AWS Lambda is a popular choice among today’s developers and solutions architects to implement serverless workloads. You can build faster while focusing your development efforts on what makes your app unique and thereby reducing time to market when basing AWS Lambda as your serverless compute layer. In serverless, there are recognized patterns that can aid in the management of larger and complex systems. This article aims at explaining the seven most frequent pitfalls to avoid when architecting serverless workloads on AWS Lambda.
- Diagrams by — PatchDuty