Last active
June 2, 2026 13:16
-
-
Save oliver-zehentleitner/dcc7d8b314c7ea81b8ffa8760e67d261 to your computer and use it in GitHub Desktop.
Grafana Cloud k6 distributed plateau test that dynamically loads all running ASCII UBDCC DepthCaches and validates sustained REST API load.
This file contains hidden or 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
| import http from 'k6/http'; | |
| import { check, fail } from 'k6'; | |
| const BASE_URL = __ENV.BASE_URL || 'http://YOUR_UBDCC_IP'; | |
| const LIMIT = __ENV.LIMIT || '100'; | |
| const ONLY_RUNNING = (__ENV.ONLY_RUNNING || 'true') === 'true'; | |
| export const options = { | |
| cloud: { | |
| distribution: { | |
| frankfurt: { loadZone: 'amazon:de:frankfurt', percent: 100 }, | |
| }, | |
| }, | |
| scenarios: { | |
| distributed_dynamic_markets_plateau: { | |
| executor: 'ramping-arrival-rate', | |
| startRate: 50, | |
| timeUnit: '1s', | |
| preAllocatedVUs: 300, | |
| maxVUs: 1000, | |
| stages: [ | |
| { target: 100, duration: '2m' }, | |
| { target: 200, duration: '2m' }, | |
| { target: 300, duration: '2m' }, | |
| { target: 400, duration: '5m' }, | |
| { target: 0, duration: '1m' }, | |
| ], | |
| }, | |
| }, | |
| thresholds: { | |
| http_req_failed: ['rate<0.01'], | |
| http_req_duration: ['p(95)<1000'], | |
| checks: ['rate>0.99'], | |
| }, | |
| }; | |
| function allReplicasRunning(depthcache) { | |
| const distribution = depthcache.DISTRIBUTION || {}; | |
| const desiredQuantity = depthcache.DESIRED_QUANTITY || 0; | |
| const runningReplicas = Object.values(distribution).filter( | |
| (replica) => replica.STATUS === 'running' | |
| ).length; | |
| return desiredQuantity > 0 && runningReplicas >= desiredQuantity; | |
| } | |
| function isAsciiSymbol(symbol) { | |
| return /^[A-Z0-9]+$/.test(symbol); | |
| } | |
| export function setup() { | |
| const url = `${BASE_URL}/get_depthcache_list`; | |
| const res = http.get(url, { timeout: '30s' }); | |
| if (res.status !== 200) { | |
| fail(`Failed to fetch depthcache list: HTTP ${res.status}`); | |
| } | |
| const data = res.json(); | |
| if (!data.depthcache_list || typeof data.depthcache_list !== 'object') { | |
| fail('Unsupported response format from /get_depthcache_list'); | |
| } | |
| const markets = []; | |
| let totalDepthCaches = 0; | |
| let skippedNonAscii = 0; | |
| let skippedNotRunning = 0; | |
| let skippedInvalid = 0; | |
| for (const [exchangeName, exchangeMarkets] of Object.entries(data.depthcache_list)) { | |
| for (const [marketName, depthcache] of Object.entries(exchangeMarkets)) { | |
| totalDepthCaches++; | |
| const exchange = depthcache.EXCHANGE || exchangeName; | |
| const market = depthcache.MARKET || marketName; | |
| if (!exchange || !market) { | |
| skippedInvalid++; | |
| continue; | |
| } | |
| if (!isAsciiSymbol(market)) { | |
| skippedNonAscii++; | |
| continue; | |
| } | |
| if (ONLY_RUNNING && !allReplicasRunning(depthcache)) { | |
| skippedNotRunning++; | |
| continue; | |
| } | |
| markets.push({ | |
| exchange, | |
| market, | |
| }); | |
| } | |
| } | |
| if (markets.length === 0) { | |
| fail('No usable depthcache markets found.'); | |
| } | |
| console.log(`Total DepthCaches found: ${totalDepthCaches}`); | |
| console.log(`Loaded usable DepthCaches: ${markets.length}`); | |
| console.log(`Skipped non-ASCII markets: ${skippedNonAscii}`); | |
| console.log(`Skipped not fully running: ${skippedNotRunning}`); | |
| console.log(`Skipped invalid entries: ${skippedInvalid}`); | |
| console.log(`ONLY_RUNNING=${ONLY_RUNNING}`); | |
| console.log(`LIMIT=${LIMIT}`); | |
| return { | |
| markets, | |
| }; | |
| } | |
| export default function (data) { | |
| const item = data.markets[Math.floor(Math.random() * data.markets.length)]; | |
| const endpoint = Math.random() < 0.5 ? 'get_asks' : 'get_bids'; | |
| const url = | |
| `${BASE_URL}/${endpoint}` + | |
| `?exchange=${encodeURIComponent(item.exchange)}` + | |
| `&market=${encodeURIComponent(item.market)}` + | |
| `&limit_count=${encodeURIComponent(LIMIT)}`; | |
| const res = http.get(url, { | |
| timeout: '5s', | |
| tags: { | |
| endpoint, | |
| exchange: item.exchange, | |
| market: item.market, | |
| limit: LIMIT, | |
| test_type: 'distributed_dynamic_markets', | |
| test_phase: 'plateau', | |
| }, | |
| }); | |
| check(res, { | |
| 'request did not timeout': (r) => r.status !== 0, | |
| 'status is 200': (r) => r.status === 200, | |
| 'body is not empty': (r) => r.body && r.body.length > 10, | |
| }); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment