Last active
June 2, 2026 13:17
-
-
Save oliver-zehentleitner/01635ac085ddee4b066481f9751f864e to your computer and use it in GitHub Desktop.
Grafana Cloud k6 fast limit-finder test for pushing distributed UBDCC REST API load across all running ASCII DepthCaches while using low-cardinality tags.
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 }, | |
| }, | |
| }, | |
| systemTags: [ | |
| 'status', | |
| 'method', | |
| 'name', | |
| 'scenario', | |
| 'group', | |
| 'check', | |
| 'error', | |
| 'error_code', | |
| ], | |
| scenarios: { | |
| distributed_dynamic_markets_fast_limit_finder: { | |
| executor: 'ramping-arrival-rate', | |
| startRate: 100, | |
| timeUnit: '1s', | |
| preAllocatedVUs: 500, | |
| maxVUs: 2000, | |
| stages: [ | |
| { target: 300, duration: '1m' }, | |
| { target: 500, duration: '1m' }, | |
| { target: 700, duration: '1m' }, | |
| { target: 900, duration: '1m' }, | |
| { target: 1100, duration: '1m' }, | |
| { target: 0, duration: '30s' }, | |
| ], | |
| }, | |
| }, | |
| thresholds: { | |
| http_req_failed: ['rate<0.05'], | |
| http_req_duration: ['p(95)<5000'], | |
| checks: ['rate>0.95'], | |
| }, | |
| }; | |
| 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', | |
| tags: { | |
| name: 'get_depthcache_list', | |
| }, | |
| }); | |
| 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: { | |
| name: endpoint, | |
| endpoint, | |
| exchange: item.exchange, | |
| test_type: 'distributed_dynamic_markets', | |
| test_phase: 'fast_limit_finder_low_cardinality', | |
| }, | |
| }); | |
| 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