Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save oliver-zehentleitner/dcc7d8b314c7ea81b8ffa8760e67d261 to your computer and use it in GitHub Desktop.

Select an option

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.
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