Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions tests/unit/handlers/metrics.handlers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ test('getHashrate - grouped by miner uses type group aggregation', async (t) =>
capturedPayload = payload
return [{
ts: 1700006400000,
hashrate_mhs_5m_type_group_sum_aggr: 123456
hashrate_mhs_5m_type_group_sum_aggr: { 'S19-Pro': 100000, 'S21': 23456 }
}]
}
}
Expand All @@ -88,8 +88,12 @@ test('getHashrate - grouped by miner uses type group aggregation', async (t) =>
t.is(capturedPayload.fields.hashrate_mhs_5m_type_group_sum, 1, 'should request type-group source field')
t.is(capturedPayload.aggrFields.hashrate_mhs_5m_type_group_sum_aggr, 1, 'should request type-group aggregate field')
t.is(result.log.length, 1, 'should map one grouped row')
t.is(result.log[0].hashrateMhs, 123456, 'should map grouped hashrate value')
t.alike(result.summary, {}, 'grouped response should return empty summary')
t.alike(result.log[0].hashrateMhs, { 'S19-Pro': 100000, 'S21': 23456 }, 'should map grouped hashrate value')
t.is(result.summary.totalHashrateMhs, 123456, 'should have site-wide total')
t.is(result.summary.avgHashrateMhs, 123456, 'should have site-wide average')
t.ok(result.summary.groupedBy, 'should have per-miner breakdown')
t.is(result.summary.groupedBy['S19-Pro'].totalHashrateMhs, 100000, 'should have per-miner total')
t.is(result.summary.groupedBy['S21'].totalHashrateMhs, 23456, 'should have per-miner total')
t.pass()
})

Expand All @@ -102,7 +106,7 @@ test('getHashrate - grouped by container uses container group aggregation', asyn
capturedPayload = payload
return [{
ts: 1700006400000,
hashrate_mhs_5m_container_group_sum_aggr: 777
hashrate_mhs_5m_container_group_sum_aggr: { 'container-A': 500, 'container-B': 277 }
}]
}
}
Expand All @@ -115,8 +119,11 @@ test('getHashrate - grouped by container uses container group aggregation', asyn
t.is(capturedPayload.fields.hashrate_mhs_5m_container_group_sum, 1, 'should request container-group source field')
t.is(capturedPayload.aggrFields.hashrate_mhs_5m_container_group_sum_aggr, 1, 'should request container-group aggregate field')
t.is(result.log.length, 1, 'should map grouped row')
t.is(result.log[0].hashrateMhs, 777, 'should map container grouped hashrate value')
t.alike(result.summary, {}, 'grouped response should return empty summary')
t.alike(result.log[0].hashrateMhs, { 'container-A': 500, 'container-B': 277 }, 'should map container grouped hashrate value')
t.is(result.summary.totalHashrateMhs, 777, 'should have site-wide total')
t.ok(result.summary.groupedBy, 'should have per-container breakdown')
t.is(result.summary.groupedBy['container-A'].totalHashrateMhs, 500, 'should have per-container total')
t.is(result.summary.groupedBy['container-B'].totalHashrateMhs, 277, 'should have per-container total')
t.pass()
})

Expand All @@ -131,7 +138,8 @@ test('getHashrate - grouped mode handles empty results', async (t) => {
})

t.is(result.log.length, 0, 'grouped log should be empty when no data is returned')
t.alike(result.summary, {}, 'grouped summary should still be empty object')
t.is(result.summary.avgHashrateMhs, null, 'grouped empty summary should have null avg')
t.is(result.summary.totalHashrateMhs, 0, 'grouped empty summary should have zero total')
t.pass()
})

Expand Down
44 changes: 43 additions & 1 deletion workers/lib/server/handlers/metrics.handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ async function getGoupedHashrate (ctx, req) {
return aggr
}, [])

return { log, summary: {} }
const summary = calculateGroupedHashrateSummary(log, groupBy)

return { log, summary }
}

function processHashrateData (results) {
Expand Down Expand Up @@ -112,6 +114,45 @@ function calculateHashrateSummary (log) {
}
}

function calculateGroupedHashrateSummary (log, groupBy) {
if (!log.length) {
return {
avgHashrateMhs: null,
totalHashrateMhs: 0
}
}

const groupTotals = {}
const groupCounts = {}

for (const entry of log) {
const hashrate = entry.hashrateMhs
if (typeof hashrate === 'object' && hashrate !== null) {
for (const [name, val] of Object.entries(hashrate)) {
const v = Number(val) || 0
groupTotals[name] = (groupTotals[name] || 0) + v
groupCounts[name] = (groupCounts[name] || 0) + 1
}
}
}

const byGroup = {}
let siteTotal = 0
for (const [name, total] of Object.entries(groupTotals)) {
byGroup[name] = {
avgHashrateMhs: safeDiv(total, groupCounts[name]),
totalHashrateMhs: total
}
siteTotal += total
}

return {
avgHashrateMhs: safeDiv(siteTotal, log.length),
totalHashrateMhs: siteTotal,
groupedBy: byGroup
}
}

async function getConsumption (ctx, req) {
const { start, end } = validateStartEnd(req)

Expand Down Expand Up @@ -728,6 +769,7 @@ module.exports = {
getHashrate,
processHashrateData,
calculateHashrateSummary,
calculateGroupedHashrateSummary,
getConsumption,
processConsumptionData,
calculateConsumptionSummary,
Expand Down
Loading