Skip to content

Instantly share code, notes, and snippets.

@skubakdj
Last active August 15, 2019 07:36
Show Gist options
  • Save skubakdj/668bbc9f186213f17026163567566071 to your computer and use it in GitHub Desktop.
Save skubakdj/668bbc9f186213f17026163567566071 to your computer and use it in GitHub Desktop.
Stress test OpenMonero by starting a bunch of threads.
/*
steps to run (fill out the 'BASIC CONFIG' first):
mkdir stress_test && cd stress_test
npm init -y
npm install typescript ts-node axios git://github.com/mymonero/mymonero-core-js.git#1.0.0
[download gist as threads.ts]
node_modules/.bin/ts-node threads.ts
*/
import axios from 'axios'
const walletUtils = require('mymonero-core-js/monero_utils/monero_wallet_utils')
/**
* BASIC CONFIG
*/
const LIGHTWALLET_ENDPOINT = 'http://192.168.1.1:1984/'
const TARGET_THREAD_COUNT = 5000
const NETWORK: 'mainnet' | 'stagenet' = 'mainnet'
const THREAD_TEST_DURATION_MS = 1000 * 60 * 60 // one hour
/**
* ADDITIONAL CONFIG
*/
const ADDRESS_LOOP_TIMEOUT_MS = 60000
const HEARTBEAT_MS = 5000
/**
* VARS
*/
const startTime = new Date()
const http = axios.create({
baseURL: LIGHTWALLET_ENDPOINT,
timeout: 40000,
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
})
const moneroKeys = generateKeyObject(TARGET_THREAD_COUNT, NETWORK)
let allKeysStarted = false
let allKeysStartedTime: Date
let globalAddressState: GlobalAddressState = {}
let targetEndTime: Date
let finishing = false
/**
* TYPES
*/
type AddressInfoRequest = {
sentTime: Date
recievedTime: Date
responseTimeMs: number
response: any
}
type AddressState = {
loginReq: any
address: string
view_key: string
addressInfoRequests: AddressInfoRequest[]
}
type GlobalAddressState = {
[key: string]: AddressState
}
type KeyObject = {
[key: string]: {
address: string
view_key: string
}
}
async function main() {
const keys = Object.keys(moneroKeys)
for (const key of keys) {
try {
if (finishing) return
const { address, view_key } = moneroKeys[key]
const loginReq = await makeLoginRequest(address, view_key)
addressLoop(loginReq, key, address, view_key)
console.log(`Started key ${key}`)
} catch (err) {
console.log('Error caught in startImportStressTest:', err)
}
}
allKeysStarted = true
allKeysStartedTime = new Date()
targetEndTime = new Date(
allKeysStartedTime.getTime() + THREAD_TEST_DURATION_MS,
)
heartbeat()
}
const addressLoop = async (
loginReq: any,
key: string,
address: string,
view_key: string,
) => {
let addressState: AddressState = {
loginReq,
address,
view_key,
addressInfoRequests: [],
}
let working = true
globalAddressState[key] = addressState
await sleep(5000)
while (working && !finishing) {
try {
const sentTime = new Date()
const response = await makeAddressInfoRequest(address, view_key)
const recievedTime = new Date()
const responseTimeMs = recievedTime.getTime() - sentTime.getTime()
addressState.addressInfoRequests.push({
sentTime,
recievedTime,
responseTimeMs,
response,
})
globalAddressState[key] = addressState
} catch (err) {
console.log(`makeAddressInfoRequest failed for key ${key}`)
} finally {
await sleep(ADDRESS_LOOP_TIMEOUT_MS)
}
}
}
async function heartbeat() {
let working = true
console.log('heartbeat started')
while (working && !finishing) {
const now = new Date()
await sleep(HEARTBEAT_MS)
if (!allKeysStarted) continue
if (now >= targetEndTime) {
console.log('target end time reached')
finished()
working = false
} else {
logStats()
}
}
}
const logStats = () => {
// average response time
const keys = Object.keys(globalAddressState)
const responseTimes = []
for (const key of keys) {
const addrState = globalAddressState[key]
const responseCount = addrState.addressInfoRequests.length
if (!responseCount) continue
for (const info of addrState.addressInfoRequests) {
responseTimes.push(info.responseTimeMs)
}
}
const averageResponseTime = Math.floor(
sumArr(responseTimes) / responseTimes.length,
)
const now = new Date()
const msToFinish = targetEndTime.getTime() - now.getTime()
const timeToFinish = timeConversion(msToFinish)
console.log(
`Avg resp time: ${averageResponseTime} ms | Remaining: ${timeToFinish}`,
)
}
const finished = async () => {
const endTime = new Date()
finishing = true
console.log(
`Total time: ${timeConversion(endTime.getTime() - startTime.getTime())}`,
)
console.log('finished! exiting...')
process.exit()
}
/**
* HELPERS
*/
const makeLoginRequest = async (address: string, view_key: string) => {
const { data } = await http.post('login', {
address,
view_key,
create_account: true,
generated_locally: true,
})
return data
}
const makeAddressInfoRequest = async (address: string, view_key: string) => {
const { data } = await http.post('get_address_info', {
address,
view_key,
})
return data
}
const sumArr = (arr: number[]) => arr.reduce((a, b) => a + b, 0)
const sleep = (time: number) =>
new Promise(resolve =>
setTimeout(() => {
resolve()
}, time),
)
// inspired by Nofi:
// https://stackoverflow.com/questions/19700283/how-to-convert-time-milliseconds-to-hours-min-sec-format-in-javascript/19700358
function timeConversion(millisec: number) {
const seconds: number = millisec / 1000
const minutes = millisec / (1000 * 60)
const hours = millisec / (1000 * 60 * 60)
const days = millisec / (1000 * 60 * 60 * 24)
if (seconds < 60) {
return seconds.toFixed(1) + ' Sec'
} else if (minutes < 60) {
return minutes.toFixed(1) + ' Min'
} else if (hours < 24) {
return hours.toFixed(1) + ' Hrs'
} else {
return days.toFixed(1) + ' Days'
}
}
function generateKeyObject(
total: number,
network: 'stagenet' | 'mainnet',
): KeyObject {
const networkNumber = network === 'mainnet' ? 0 : 2
let count = 0
let ret: KeyObject = {}
while (count < total) {
ret[`${count}`] = createKeys(networkNumber)
count++
}
return ret
}
function createKeys(
network: number = 2,
): { address: string; view_key: string } {
const wallet = walletUtils.NewlyCreatedWallet('english', network) // 2 == stagenet
return {
address: wallet.keys.public_addr,
view_key: wallet.keys.view.pub,
}
}
// call so we can handle SIGINT
process.stdin.resume()
process.on('SIGINT', async () => {
console.log('ctrl + c event detected')
await finished()
console.log('finished. exiting...')
process.exit()
})
if (require.main === module) {
main()
}
@moneroexamples
Copy link

npm i @types/node --save-dev was also needed to run it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment