“The importance of application reliability cannot be overstated in today’s digital landscape. As we rely on digital technologies for communication, commerce, and various daily tasks, smooth and consistent operation of applications has become crucial. Users expect applications to be available and responsive, regardless of the platform or device they are using. In the realm of e-commerce, finance, healthcare, and other critical sectors, application reliability becomes paramount. Whether gearing up for a seasonal event like Black Friday, handling tax filings, or striving to meet performance requirements during application development, ensuring uninterrupted service is crucial. Downtime or glitches in applications can lead to significant financial losses, damage to reputation, and user dissatisfaction. As technology continues to advance, the emphasis on application reliability will only intensify, highlighting the need for reliable apps. I’ve asked Senior Product Manager, Nikita Nallamothu to share more about the importance of performance testing, strategies to be applied, and provide a discussion on practical scenarios.”—Mark Russinovich, CTO, Azure.
Different types of performance testing
There are different types of performance tests, each of which can help improve specific aspects of your application reliability and efficacy.
- Load testing: Can the application handle anticipated load?
- Stress testing: How does the application react and recover under extreme conditions?
- Soak/Endurance testing: how does the application behave over an extended period of time?
- Breakpoint testing: What is maximum load that the application can bear?
- Scalability testing: Does the application scale up or down under varying load?
Testing strategy
A comprehensive performance testing strategy involves a nuanced understanding of critical elements that contribute to the reliability and effectiveness of an application. In this section we’ll look at different considerations while defining the test, running the test, and analyzing the results.
Defining the test
Firstly, realistic user scenarios form the foundation of a robust strategy. Designing tests that closely mirror actual user behavior ensures accurate simulation, enabling a more authentic assessment of the system’s responsiveness and performance under real-world conditions. Some ways to achieve this are:
- Identify different user personas based on real-world usage patterns. Consider factors such as user roles, frequency of interaction, and typical workflows.
- Introduce think time between user actions. Users don’t interact with applications in a continuous and rapid manner. Incorporate delays between actions to simulate real user thinking and interaction patterns, providing a more accurate representation of user behavior.
- Mimic the variability in user load that the application is likely to encounter. Real-world user traffic is seldom constant, so performance tests should include scenarios with varying levels of concurrent users to assess how the system handles fluctuations in demand.
- Many user interactions involve multi-step transactions. Design tests that emulate these complex transactions, such as completing a purchase, submitting a form, or navigating through multiple pages.
- Consider the geographical distribution of actual users. If your application is accessed globally, simulate tests from various geographical locations to assess the impact of latency and network conditions on performance.
Setting benchmarks is another key aspect of an effective performance testing strategy. By establishing performance benchmarks, you can define measurable standards and criteria for assessing the application’s performance. These benchmarks serve as reference points, allowing for the comparison of different builds or versions, and facilitating the identification of performance improvements or regressions over time. You establish a baseline for these benchmarks by running load tests frequently make it part of the application quality gates. Typically, benchmarks are set on metrics like response time, throughput, error rate, resource utilization, and network latency.
Running the test
It’s important to understand how frequently to test, which type of tests to run and where to run these tests.
Type of testing | When to perform | Frequency | Environment |
Load testing | When there are significant changes (updates, new features, or infrastructure changes). | Perform periodic tests for ongoing validation. | Initial load testing in the development environment. UAT environment testing for validating expected user loads. |
Stress testing | After load testing, usually in later stages or before major releases. | Less frequent than load testing but repeat after significant changes. | Stress tests in the staging environment to assess extreme conditions. |
Soak testing | Typically after load and stress testing to evaluate extended behavior. | Less frequent but periodic, especially for continuous operation. | Conduct soak testing in the staging environment over an extended period. |
Breakpoint testing | Essential after significant changes impacting capacity. | Done after load and stress testing, not as frequent. | Perform in a pre-production environment mirroring production. |
Scalability testing | Conduct when assessing ability to scale with increased load or architecture changes. | Can be performed less frequently for future growth planning. | Evaluate scalability in an environment replicating production infrastructure |
In addition to conducting end-to-end application testing, there are instances where it becomes essential to test individual components within the system. Consider a scenario where an e-commerce website facilitates customer shopping alongside a seller application responsible for updating the product catalog. Both applications interact with the products inventory database. In such cases, it becomes crucial to perform individual testing of the database, given its interaction with multiple applications.
Analyzing results
Monitoring various metrics during performance testing is crucial to get a comprehensive understanding of your application’s behavior under different conditions. Analysis of test results should be done in relation to the benchmarks to assess if the application is meeting the goals. To diagnose performance issues, you need to go one step further to get insights that help identify the root cause. Azure Load Testing gives you a dashboard which shows the client-side and server-side metrics enabling you to get the required insights in identifying bottlenecks. Here are some metrics that you should be monitoring.
- Response time: This measures the time taken for the system to respond to a request. Taking it a step ahead, examining detailed breakdowns of response times for various components or transactions helps identify bottlenecks and optimize critical paths within the application.
- Error rate: This tracks the percentage of errors during testing. Investigating the types and patterns of errors provides insights into the application’s error resilience, helping to enhance overall system stability.
- Throughput: This measures the number of transactions processed per unit of time. Analyzing throughput patterns under different loads aids in understanding the system’s capacity and helps in capacity planning.
- Concurrency: This assesses the number of simultaneous users or connections. Identifying peak concurrency levels assists in optimizing resources and infrastructure to handle varying user loads effectively.
- Resource utilization: This includes monitoring the CPU, memory, disk, and network utilization. Examining resource usage patterns helps in identifying resource-intensive operations, optimizing resource allocation, and preventing resource exhaustion.
While these metrics are widely applicable to most applications, make sure to also monitor the metrics which are specific to your application components.
Continuous performance testing
Continuous performance testing involves the seamless integration of performance testing into CI/CD pipelines, ensuring that applications are evaluated for scalability and responsiveness throughout the development lifecycle. This practice is crucial for identifying and addressing performance issues early in the development process, minimizing the risk of deploying applications with sub-optimal performance. It allows for the early detection of performance bottlenecks, allowing developers to address issues in the initial stages of development. This reduces the cost and effort associated with fixing performance problems later in the development cycle.
Some key considerations for integrating into CI/CD pipelines are:
- Frequency: Perform basic tests for every new build, to catch regression promptly. Conduct more comprehensive performance tests on a regular schedule, such as weekly builds, depending on the development velocity. Conduct thorough performance testing before critical releases or major updates to ensure that the application meets performance criteria before reaching production.
- Environments: Use environments that closely resemble the production setup, including server configurations, databases, and network conditions. This helps identify issues specific to the production environment.
- Analysis: Define performance benchmarks to identify performance regressions and deviations. Automated alerts can notify teams of potential issues.
Stress testing in action
In this section, we explore a detailed scenario for stress testing, including the identification of critical system components, and insights into interpreting test results. We will use Azure Load Testing and Application Insights to achieve this.
Contoso Ltd. is an e-commerce company preparing for Black Friday—a major online sale event. During this event, the system experiences a massive influx of users simultaneously attempting to access the platform, browser products, and make purchases. The stress test simulates a scenario where the user load surpasses the typical peak traffic the system handles, aiming to determine how well the platform copes with the increased demand. The application comprises of front-end infrastructure, with the Contoso Ltd. e-commerce website and back end APIs.
Contoso Ltd. prepare their test plans to test for different critical user flows like searching for products, adding products to cart and checkout. They begin stress testing the system using Azure Load Testing by gradually increasing the user load to go beyond its expected capacity. The goal is to observe how the system responds as load increases, identifying the threshold at which performance degrades. As shown in the image below, the Carts API starts to eventually fail under the increased load.
To debug the errors, a closer inspection is performed using Application Insights. The Failures tab in App Insights details the encountered failures during the load test, revealing a 500 error caused by an exception indicating a gateway timeout in Cosmos DB.
When you look at the server-side metrics in Azure Load Testing, you can see that normalized Request Units (RU) consumption for Cosmos DB eventually starts to peg at 100% under load.
This signifies that the application is failing due to Cosmos DB struggling to handle the incoming requests. As a solution, Contoso Ltd. can address this by increasing the Request Units (RU) for Cosmos DB or moving to Autoscale mode.
Source: microsoft.com