feat(report): 처리량(TPS) 지표 추가

- /admin/report에 throughput(초당 버킷): 접수/완결 최대 TPS, 완결 평균 TPS(활성구간), 활성 초수
- 보고서 화면에 🚀 처리량(TPS) 섹션(지연시간 아래) + 드라이버 부하 기준 주의 문구

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
rtgs
2026-07-16 14:29:47 +09:00
parent 61ced29510
commit 5b706bfc33
2 changed files with 26 additions and 0 deletions

View File

@@ -148,6 +148,18 @@ class AdminController(
AND t.created_at >= r.received_at -- 인과 정합(접수≤순번) 행만: BMI 재사용 등 시계 역전 건 제외
""".trimIndent())
// 처리량(TPS) — 초 단위 버킷의 최대/평균. 접수(수용) vs 완결(처리) 관측치.
// active_secs = 완결이 있었던 초 개수 → settled_avg는 활성 구간 평균(유휴 제외).
val tps = jdbc.queryForMap(
"""
WITH recv AS (SELECT received_at/1000 AS s, count(*) c FROM raw_message WHERE received_at > 0 GROUP BY received_at/1000),
done AS (SELECT updated_at/1000 AS s, count(*) c FROM transfer WHERE status='ACCC' GROUP BY updated_at/1000)
SELECT COALESCE((SELECT max(c) FROM recv),0) AS received_peak,
COALESCE((SELECT max(c) FROM done),0) AS settled_peak,
COALESCE((SELECT round(avg(c)) FROM done),0) AS settled_avg,
COALESCE((SELECT count(*) FROM done),0) AS active_secs
""".trimIndent())
// 기관별 송·수신 집계(ACCC 기준) + 현재 잔액
val byInstitution = jdbc.queryForList(
"""
@@ -172,6 +184,7 @@ class AdminController(
),
"amount" to amt,
"latency" to lat,
"throughput" to tps,
"byInstitution" to byInstitution,
)
}

View File

@@ -32,6 +32,7 @@ function ReportInner() {
const rec = rep.reconciliation ?? {}
const amt = rep.amount ?? {}
const lat = rep.latency ?? {}
const tp = rep.throughput ?? {}
const cnt = (st: string) => n((t.byStatus ?? []).find((b: any) => b.status === st)?.cnt)
const total = n(t.transfers)
const accc = cnt('ACCC'), rjct = cnt('RJCT')
@@ -106,6 +107,18 @@ function ReportInner() {
</table>
</section>
{/* 처리량(TPS) */}
<section style={box}>
<h3 style={{ marginTop: 0 }}>🚀 (TPS) <span style={{ fontSize: 12, color: '#888' }}>( · , )</span></h3>
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
<div style={{ ...card, background: '#f7fbff' }}><div style={lbl}> TPS</div><div style={{ ...big, color: '#1a7f37' }}>{num(tp.settled_peak)}<span style={{ fontSize: 13, color: '#777' }}> /s</span></div></div>
<div style={card}><div style={lbl}> TPS <span style={{ fontSize: 11 }}>()</span></div><div style={big}>{num(tp.settled_avg)}<span style={{ fontSize: 13, color: '#777' }}> /s</span></div></div>
<div style={card}><div style={lbl}> TPS</div><div style={big}>{num(tp.received_peak)}<span style={{ fontSize: 13, color: '#777' }}> /s</span></div></div>
<div style={card}><div style={lbl}> </div><div style={big}>{num(tp.active_secs)}<span style={{ fontSize: 13, color: '#777' }}> s</span></div></div>
</div>
<p style={{ color: '#888', fontSize: 12, margin: '10px 0 0' }}> · TPS가 (= ). k6 .</p>
</section>
{/* 기관별 */}
<section style={box}>
<h3 style={{ marginTop: 0 }}>🏛 · <span style={{ fontSize: 12, color: '#888' }}>( ACCC )</span></h3>