-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Testing] Add load-testing foundation (#278)
* load testing foundation * --wip-- [skip ci] * --wip-- [skip ci] * --wip-- [skip ci] * --wip-- [skip ci] * add docs and better structure * wip * requested changes * change error output * more cleanup * requested changes
- Loading branch information
Showing
12 changed files
with
317 additions
and
583 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
--- | ||
sidebar_position: 1 | ||
title: Load Testing | ||
--- | ||
|
||
# Load Testing <!-- omit in toc --> | ||
|
||
Poktroll load-testing suite. | ||
|
||
- [Overview](#overview) | ||
- [Dependencies](#dependencies) | ||
- [How to run tests](#how-to-run-tests) | ||
- [Common k6 CLI flags](#common-k6-cli-flags) | ||
- [Understanding Output](#understanding-output) | ||
- [CLI Output](#cli-output) | ||
- [HTML Report](#html-report) | ||
- [Adding a new load test](#adding-a-new-load-test) | ||
- [Sophisticated Reporting](#sophisticated-reporting) | ||
- [Troubleshooting](#troubleshooting) | ||
- [File structure](#file-structure) | ||
|
||
## Overview | ||
|
||
We use [k6](https://k6.io/) for load testing. For detailed information about k6 internals and best practices, please refer to the [k6 documentation](https://grafana.com/docs/k6/latest/). | ||
|
||
## Dependencies | ||
|
||
- [k6](https://grafana.com/docs/k6/latest/get-started/installation/) | ||
- (For local suite execution) [LocalNet](../localnet.md) | ||
|
||
## How to run tests | ||
|
||
Tests are stored in the [load-testing/tests](https://github.com/pokt-network/poktroll/tree/main/load-testing/tests) folder, covering various use cases. | ||
|
||
For instance, here's a basic load test sending requests through `AppGateServer`, which proxies them to `RelayMiner`: | ||
|
||
```bash | ||
k6 run load-testing/tests/appGateServerEtherium.js | ||
``` | ||
|
||
### Common k6 CLI flags | ||
|
||
The `k6` load testing tool provides various command-line flags to customize and control your load tests. Below are some of the key flags that you can use: | ||
|
||
- `--vus`: Specifies the number of virtual users (VUs) to simulate. This flag allows you to set the concurrency level of your load test. | ||
- Example: `k6 run script.js --vus=50` runs the test with 50 virtual users. | ||
|
||
- `--duration`: Defines the duration for which the test should run. You can specify the time in seconds (s), minutes (m), or hours (h). | ||
- Example: `k6 run script.js --duration=1m` runs the test for 1 minute. | ||
|
||
- `--http-debug`: Enables HTTP debugging. This flag can be set to `full` for detailed logging of all HTTP requests and responses. Useful for troubleshooting and debugging your tests. | ||
- Use `--http-debug` for a summary of HTTP requests. | ||
- Use `--http-debug=full` for detailed request and response logging. | ||
- Example: `k6 run script.js --http-debug=full` provides a detailed log of HTTP transactions. | ||
|
||
The default configurations for VUs and duration are in the [config file](https://github.com/pokt-network/poktroll/tree/main/load-testing/config/index.js). Override these defaults by passing the appropriate flags to the `k6` command. For example: | ||
|
||
```bash | ||
k6 run load-testing/tests/appGateServerEtherium.js --vus=300 --duration=30s | ||
``` | ||
|
||
For a comprehensive list of available flags and their usage, refer to the [k6 documentation](https://grafana.com/docs/k6/latest/). | ||
|
||
## Understanding Output | ||
|
||
### CLI Output | ||
|
||
Post-test, basic performance metrics are available: | ||
|
||
``` | ||
scenarios: (100.00%) 1 scenario, 100 max VUs, 1m30s max duration (incl. graceful stop): | ||
* default: 100 looping VUs for 1m0s (gracefulStop: 30s) | ||
INFO[0061] [k6-reporter v2.3.0] Generating HTML summary report source=console | ||
✓ is status 200 | ||
✓ is successful JSON-RPC response | ||
checks.........................: 100.00% ✓ 12000 ✗ 0 | ||
data_received..................: 1.6 MB 27 kB/s | ||
data_sent......................: 1.2 MB 19 kB/s | ||
http_req_blocked...............: avg=52.52µs min=0s med=2µs max=7.08ms p(90)=4µs p(95)=6µs | ||
http_req_connecting............: avg=37.73µs min=0s med=0s max=5.48ms p(90)=0s p(95)=0s | ||
http_req_duration..............: avg=8.51ms min=1.58ms med=5.98ms max=81.98ms p(90)=15.45ms p(95)=19.97ms | ||
{ expected_response:true }...: avg=8.51ms min=1.58ms med=5.98ms max=81.98ms p(90)=15.45ms p(95)=19.97ms | ||
http_req_failed................: 0.00% ✓ 0 ✗ 6000 | ||
http_req_receiving.............: avg=42.18µs min=5µs med=25µs max=5.71ms p(90)=65µs p(95)=125µs | ||
http_req_sending...............: avg=26.5µs min=2µs med=9µs max=7.2ms p(90)=21µs p(95)=42µs | ||
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s | ||
http_req_waiting...............: avg=8.44ms min=1.55ms med=5.93ms max=81.28ms p(90)=15.38ms p(95)=19.82ms | ||
http_reqs......................: 6000 98.915624/s | ||
iteration_duration.............: avg=1s min=1s med=1s max=1.08s p(90)=1.01s p(95)=1.02s | ||
iterations.....................: 6000 98.915624/s | ||
vus............................: 100 min=100 max=100 | ||
running (1m00.7s), 000/100 VUs, 6000 complete and 0 interrupted iterations | ||
default ✓ [======================================] 100 VUs 1m0s | ||
``` | ||
|
||
### HTML Report | ||
|
||
An HTML report is generated in the execution directory. Open it in your default browser: | ||
|
||
``` | ||
open summary.html | ||
``` | ||
|
||
### Adding a new load test | ||
|
||
TODO(@okdas): Add link to PR next time a new type of load test is added. | ||
|
||
|
||
### Sophisticated Reporting | ||
|
||
We're developing advanced reporting that integrates additional tags set in the code. This will require time-series databases and is planned for DevNets. | ||
|
||
## Troubleshooting | ||
|
||
To debug, activate the logging feature: | ||
|
||
`--http-debug` or `--http-debug=full` | ||
|
||
## File structure | ||
|
||
``` | ||
load-testing | ||
├── README.md | ||
├── config | ||
│ └── index.js | ||
├── modules # reusable code for scenarios and tests | ||
│ └── etheriumRequests.js | ||
├── scenarios # different scenarios for tests | ||
│ └── requestBlockNumberEtherium.js | ||
└── tests # test scripts | ||
├── anvilDirectEtherium.js | ||
└── appGateServerEtherium.js | ||
``` |
2 changes: 1 addition & 1 deletion
2
...ocs/infrastructure/testing/load_test_1.md → ...frastructure/testing/load_testing_plan.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { htmlReport } from "https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js"; | ||
import { textSummary } from "https://jslib.k6.io/k6-summary/0.0.1/index.js"; | ||
|
||
// Environment configuration | ||
export const ENV_CONFIG = { | ||
anvilBaseUrl: __ENV.ANVIL_BASE_URL || 'http://localhost:8547', | ||
nginxBaseUrl: __ENV.NGINX_BASE_URL || 'http://localhost:8548', | ||
AppGateServerAnvilUrl: __ENV.APP_GATE_SERVER_ANVIL_URL || 'http://localhost:42069/anvil', | ||
}; | ||
|
||
// We can export this function in our tests to generate HTML summary. | ||
export function handleSummary(data) { | ||
return { | ||
"summary.html": htmlReport(data), | ||
stdout: textSummary(data, { indent: " ", enableColors: true }), | ||
}; | ||
} | ||
|
||
export const options = { | ||
// A number specifying the number of VUs to run concurrently. | ||
vus: 100, | ||
// A string specifying the total duration of the test run. | ||
duration: '60s', | ||
|
||
// The following section contains configuration options for execution of this | ||
// test script in Grafana Cloud. | ||
// | ||
// See https://grafana.com/docs/grafana-cloud/k6/get-started/run-cloud-tests-from-the-cli/ | ||
// to learn about authoring and running k6 test scripts in Grafana k6 Cloud. | ||
// | ||
// ext: { | ||
// loadimpact: { | ||
// // The ID of the project to which the test is assigned in the k6 Cloud UI. | ||
// // By default tests are executed in default project. | ||
// projectID: "", | ||
// // The name of the test in the k6 Cloud UI. | ||
// // Test runs with the same name will be grouped. | ||
// name: "script.js" | ||
// } | ||
// }, | ||
|
||
// Uncomment this section to enable the use of Browser API in your tests. | ||
// | ||
// See https://grafana.com/docs/k6/latest/using-k6-browser/running-browser-tests/ to learn more | ||
// about using Browser API in your test scripts. | ||
// | ||
// scenarios: { | ||
// // The scenario name appears in the result summary, tags, and so on. | ||
// // You can give the scenario any name, as long as each name in the script is unique. | ||
// ui: { | ||
// // Executor is a mandatory parameter for browser-based tests. | ||
// // Shared iterations in this case tells k6 to reuse VUs to execute iterations. | ||
// // | ||
// // See https://grafana.com/docs/k6/latest/using-k6/scenarios/executors/ for other executor types. | ||
// executor: 'shared-iterations', | ||
// options: { | ||
// browser: { | ||
// // This is a mandatory parameter that instructs k6 to launch and | ||
// // connect to a chromium-based browser, and use it to run UI-based | ||
// // tests. | ||
// type: 'chromium', | ||
// }, | ||
// }, | ||
// }, | ||
// } | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Import necessary modules | ||
import http from 'k6/http'; | ||
import { check } from 'k6'; | ||
|
||
// Function to send a generic JSON-RPC request to Anvil | ||
export function sendJsonRPCRequest(baseUrl, method, params = [], tags = {}) { | ||
const payload = JSON.stringify({ | ||
jsonrpc: "2.0", | ||
method: method, | ||
params: params, | ||
id: 1 | ||
}); | ||
|
||
const requestOptions = { | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
tags: tags | ||
}; | ||
|
||
let response = http.post(baseUrl, payload, requestOptions); | ||
|
||
// Basic check for HTTP 200 OK and a valid JSON-RPC response | ||
let passed = check(response, { | ||
"is status 200": (r) => r.status === 200, | ||
"is successful JSON-RPC response": (r) => { | ||
let jsonResponse = JSON.parse(r.body); | ||
// Check for 'result' and 'id', and ensure 'error' is not present | ||
return jsonResponse.hasOwnProperty("result") && jsonResponse.hasOwnProperty("id") && !jsonResponse.hasOwnProperty("error"); | ||
} | ||
}, tags); | ||
|
||
if (!passed) { | ||
// Logging output includes vital information for troubleshooting: request/response body, status code, etc. | ||
console.log(`Request to ${response.request.url} failed:`, JSON.stringify(response, null, 2)); | ||
} | ||
|
||
return response; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Import necessary modules | ||
import { check } from 'k6'; | ||
import { sendJsonRPCRequest } from '../modules/sendJsonRPCRequest.js'; | ||
|
||
export function requestBlockNumberEtheriumScenario(baseUrl) { | ||
// Example method and parameters for the Ethereum JSON-RPC request | ||
const method = "eth_blockNumber"; | ||
const params = []; | ||
|
||
// We can populate tags in addition to the defaults. | ||
// const tags = { | ||
// method: method, | ||
// baseUrl: baseUrl, | ||
// } | ||
|
||
// Send request and receive response | ||
let response = sendJsonRPCRequest(baseUrl, method, params); | ||
|
||
// Additional checks specific to the scenario can be written like that: | ||
// check(response, { | ||
// "is the response format correct": (r) => { | ||
// let jsonResponse = JSON.parse(r.body); | ||
// return jsonResponse.hasOwnProperty("result") && jsonResponse.hasOwnProperty("id"); | ||
// }, | ||
// }, tags); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Our custom handleSummary produces HTML report with CLI output and some default configuration. | ||
export { handleSummary, options } from '../config/index.js'; | ||
import { requestBlockNumberEtheriumScenario } from '../scenarios/requestBlockNumberEtherium.js'; | ||
import { ENV_CONFIG } from '../config/index.js'; | ||
import { sleep } from 'k6'; | ||
|
||
|
||
// The function that defines VU logic. | ||
// | ||
// See https://grafana.com/docs/k6/latest/examples/get-started-with-k6/ to learn more | ||
// about authoring k6 scripts. | ||
export default function () { | ||
requestBlockNumberEtheriumScenario(ENV_CONFIG.anvilBaseUrl); | ||
|
||
// Simulate think time | ||
sleep(1); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Our custom handleSummary produces HTML report with CLI output and some default configuration. | ||
export { handleSummary, options } from '../config/index.js'; | ||
import { requestBlockNumberEtheriumScenario } from '../scenarios/requestBlockNumberEtherium.js'; | ||
import { ENV_CONFIG } from '../config/index.js'; | ||
import { sleep } from 'k6'; | ||
|
||
// TODO(@okdas): expand options to allow multiple stages: | ||
// export const options = { | ||
// stages: [ | ||
// { duration: '30s', target: 20 }, | ||
// { duration: '1m30s', target: 10 }, | ||
// { duration: '20s', target: 0 }, | ||
// ], | ||
// }; | ||
|
||
// The function that defines VU logic. | ||
// | ||
// See https://grafana.com/docs/k6/latest/examples/get-started-with-k6/ to learn more | ||
// about authoring k6 scripts. | ||
export default function () { | ||
requestBlockNumberEtheriumScenario(ENV_CONFIG.AppGateServerAnvilUrl); | ||
|
||
// Simulate think time | ||
sleep(1); | ||
} |
Oops, something went wrong.