tests — load
tests — load
This document describes the tests/load/multi-agent-load.test.ts module, which provides a framework for simulating and evaluating the performance characteristics of a multi-agent system under various load conditions.
1. Introduction
The multi-agent-load.test.ts module is a dedicated suite for conducting load tests on a simulated multi-agent system. Its primary goal is to assess the system's performance, stability, and resource utilization when processing a high volume of concurrent requests. This module helps identify potential bottlenecks, verify concurrency handling, and ensure consistent response times and memory usage.
The tests are designed to be flexible, allowing developers to define different load profiles (concurrency, total requests) and evaluate key metrics such as:
- Total and successful request counts
- Average, min, and max response times
- Requests per second (throughput)
- Memory consumption
2. Core Concepts: Simulated Agent Processing
At the heart of this load testing framework is the simulation of individual agent work. Since these are load tests and not integration tests, they do not call actual application APIs. Instead, they mimic the behavior of an agent processing a request.
simulateAgentProcessing(id: number)
This asynchronous function simulates the work an agent would perform.
- It takes an
idto represent a unique request or agent instance. - It introduces a variable delay (between 1ms and 50ms) using
setTimeoutto mimic realistic, non-uniform processing times. This variability is crucial for identifying issues that might not appear with fixed delays. - It returns an object containing the agent's
idand thetimetaken for its simulated processing.
3. Load Test Execution Flow
The runLoadTest function orchestrates the entire load test, managing concurrency and collecting performance metrics.
High-Level Flow
graph TD
A[runLoadTest] --> B{Loop totalRequests by concurrency};
B --> C[runBatch];
C --> D[simulateAgentProcessing (concurrently)];
D --> E[Collect results];
E --> F[Calculate LoadTestResult];
runLoadTest(concurrency: number, totalRequests: number)
This is the main function for executing a load test scenario.
- Initialization: It initializes an empty array
resultsto store individual response times, and counters forsuccessfulandfailedrequests. It also records thestartTimeof the test. - Batch Processing: It iterates through the
totalRequestsin chunks defined by theconcurrencyparameter. For each chunk, it callsrunBatch. - Concurrency Management: The
runBatchhelper function is responsible for creating and awaiting a batch ofsimulateAgentProcessingpromises concurrently usingPromise.all.
- Each successful
simulateAgentProcessingcall adds its response time toresultsand incrementssuccessful. - Any failed promise (though none are expected in this current simulation) would increment
failed.
- Metric Calculation: After all batches are processed,
runLoadTestcalculates various performance metrics:
totalRequests,successfulRequests,failedRequestsavgResponseTime,maxResponseTime,minResponseTimefrom the collectedresults.requestsPerSecond(throughput) based on successful requests and total test duration.
- Result Return: It returns a
LoadTestResultobject containing all calculated metrics.
runBatch(batchStart: number, batchSize: number)
This internal helper function is called by runLoadTest to execute a specific number of simulated agent processes concurrently. It creates an array of promises by calling simulateAgentProcessing for each request in the batch and then uses Promise.all to wait for all of them to complete.
4. Key Components
interface LoadTestResult
Defines the structure of the object returned by runLoadTest, encapsulating all the performance metrics gathered during a test run.
interface LoadTestResult {
totalRequests: number;
successfulRequests: number;
failedRequests: number;
avgResponseTime: number;
maxResponseTime: number;
minResponseTime: number;
requestsPerSecond: number;
}
simulateAgentProcessing(id: number): Promise<{ id: number; time: number }>
As described above, this function simulates a single unit of work with a variable delay.
runLoadTest(concurrency: number, totalRequests: number): Promise
The primary function for initiating and managing a load test, returning comprehensive performance statistics.
runBatch(batchStart: number, batchSize: number): Promise
An internal utility function used by runLoadTest to execute a group of simulateAgentProcessing calls concurrently.
5. Test Suites and Assertions
The module uses Jest's describe and it blocks to define various load test scenarios and assert expected outcomes.
Multi-Agent Load Tests
This suite focuses on the performance and stability of the simulated system under increasing load.
Concurrent Agent Processing
should handle 10 concurrent requests: TestsrunLoadTestwith 10 concurrent requests for a total of 50, asserting all requests are successful.should handle 50 concurrent requests: Increases concurrency to 50 for 100 total requests, asserting success and an average response time below 100ms.should handle 100 concurrent requests: Further increases concurrency to 100 for 200 total requests, asserting all requests are successful.
Throughput Tests
should maintain reasonable throughput: Tests with 20 concurrent requests for 100 total, asserting thatrequestsPerSecondis greater than 10. This ensures the system can process a minimum number of operations per second.
Response Time Distribution
should have consistent response times: Tests with 10 concurrent requests for 50 total, asserting that the variance betweenmaxResponseTimeandminResponseTimeis less than 500ms. This checks for consistency in processing, indicating that no requests are experiencing excessively long delays compared to others.
Memory and Resource Tests
This suite specifically targets potential memory leaks, which are critical for long-running systems.
should not leak memory during repeated operations
- This test repeatedly calls
runLoadTest(5 times with 10 concurrency and 20 requests each) to simulate sustained operation. - It captures
process.memoryUsage().heapUsedbefore and after the loop. - It attempts to force garbage collection (
global.gc()) if available, to get a more accurate post-operation memory footprint. - It asserts that the memory growth percentage is less than 50%, allowing for some natural fluctuation but guarding against significant leaks.
6. How to Extend and Contribute
To add new load test scenarios or modify existing ones:
- Define a new
itblock: Within an existingdescribeblock or a new one, create a new test case usingit('should describe your scenario', async () => { ... });. - Call
runLoadTest: Invokeawait runLoadTest(concurrency: number, totalRequests: number)with your desired load parameters. - Add Assertions: Use
expect(result.metric).toBe...to assert against theLoadTestResultobject. Consider asserting:
successfulRequestsandfailedRequestsfor reliability.avgResponseTime,maxResponseTimefor performance.requestsPerSecondfor throughput.- Memory usage if testing resource consumption.
- Adjust Timeout: If your test involves a large number of requests or high delays, increase the Jest timeout for the test using
it('...', async () => { ... }, timeoutMs);. - Refine
simulateAgentProcessing: If the simulation needs to be more complex (e.g., simulating I/O, CPU-bound tasks, or external service calls), modifysimulateAgentProcessingto reflect those behaviors.
This module provides a robust and flexible foundation for understanding the performance characteristics of a multi-agent system, even in a simulated environment.