Skip to content

Commit fd43339

Browse files
committed
fix ema tps
1 parent 9fadc15 commit fd43339

File tree

2 files changed

+162
-91
lines changed

2 files changed

+162
-91
lines changed

src/App.tsx

Lines changed: 150 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React, { useState, useEffect, useRef } from 'react';
22
import { Header } from './components/Header';
33
import { Footer } from './components/Footer';
44
import { MetricsTotal } from './components/MetricsTotal';
55
import { ChainTable } from './components/ChainTable';
66
import { RelaySelector } from './components/RelaySelector';
77
import './App.css';
8-
98
import {
109
polkadotChainNames,
1110
kusamaChainNames,
@@ -14,125 +13,179 @@ import {
1413
PolkadotChainName,
1514
KusamaChainName,
1615
} from './types/chains';
17-
1816
import useWeightConsumption from './hooks/useWeightConsumption';
1917
import { PROOF_SIZE_MB, MB_TO_GAS, GAS_TO_MGAS } from './constants';
2018

21-
// ema smoothing factor
2219
const ALPHA = 0.3;
20+
const TARGET_WINDOW_SIZE = 30000; // 30s
21+
const MAX_BLOCKS_STORE = 2000;
2322

24-
// helper to calculate ema
25-
function calculateEma(oldValue: number, newValue: number): number {
23+
// standard ema
24+
function calculateEma(oldValue: number, newValue: number) {
2625
return ALPHA * newValue + (1 - ALPHA) * oldValue;
2726
}
2827

28+
// compute rolling tps, mbs, gas from blocks in the last 30s
29+
function calculateRollingMetrics(
30+
blocks: Array<{ blockNumber: number; timestamp: number; extrinsics: number; proofSize: number }>
31+
) {
32+
if (blocks.length < 2) return { tps: 0, mbs: 0, gas: 0 };
33+
34+
const now = Date.now();
35+
const cutoff = now - TARGET_WINDOW_SIZE;
36+
const relevant = blocks.filter(b => b.timestamp >= cutoff);
37+
if (relevant.length === 0) return { tps: 0, mbs: 0, gas: 0 };
38+
39+
relevant.sort((a, b) => a.timestamp - b.timestamp);
40+
const timeWindow = relevant[relevant.length - 1].timestamp - relevant[0].timestamp;
41+
if (timeWindow <= 0) return { tps: 0, mbs: 0, gas: 0 };
42+
43+
// adjust extrinsics with -2 offset if desired
44+
const sumExtrinsics = relevant.reduce((acc, b) => acc + Math.max(0, b.extrinsics - 2), 0);
45+
const sumProofSize = relevant.reduce((acc, b) => acc + b.proofSize, 0);
46+
47+
const tps = (sumExtrinsics * 1000) / timeWindow;
48+
const mbs = (sumProofSize * PROOF_SIZE_MB * 1000) / timeWindow;
49+
const gas = mbs * (MB_TO_GAS / PROOF_SIZE_MB / GAS_TO_MGAS);
50+
51+
return { tps, mbs, gas };
52+
}
53+
54+
interface BlockRecord {
55+
chainKey: string;
56+
blockNumber: number;
57+
timestamp: number;
58+
extrinsics: number;
59+
proofSize: number;
60+
}
61+
2962
const App: React.FC = () => {
3063
const [selectedRelay, setSelectedRelay] = useState<'Polkadot' | 'Kusama'>('Polkadot');
3164

32-
// fetch consumption data
65+
// data feed
3366
const consumptionData = useWeightConsumption('https://stream.freeside.network/events');
3467

35-
// determine chain names and mappings
36-
const chainNames = selectedRelay === 'Polkadot'
37-
? polkadotChainNames
38-
: kusamaChainNames;
39-
40-
const paraIdToChainName = selectedRelay === 'Polkadot'
41-
? polkadotParaIdToChainName
42-
: kusamaParaIdToChainName;
68+
// figure out which chains we're interested in
69+
const chainNames = selectedRelay === 'Polkadot' ? polkadotChainNames : kusamaChainNames;
70+
const paraIdToChainName =
71+
selectedRelay === 'Polkadot' ? polkadotParaIdToChainName : kusamaParaIdToChainName;
4372

44-
// calculate proof-size-based weights
73+
// gather the "weightData" for all chains (just the mandatory + total_proof_size, for example)
4574
const weightData: Record<PolkadotChainName | KusamaChainName, number> = chainNames.reduce(
4675
(acc, chain) => {
4776
const paraId = Object.keys(paraIdToChainName).find(
4877
(key) => paraIdToChainName[Number(key)] === chain
4978
);
5079
if (paraId) {
5180
const key = `${selectedRelay}-${paraId}`;
52-
const chainData = consumptionData[key]?.current || {};
53-
const proofSize = chainData.ref_time?.mandatory ?? chainData.total_proof_size ?? 0;
54-
return { ...acc, [chain]: proofSize };
81+
const chainData = consumptionData[key]?.current;
82+
if (chainData) {
83+
// example: combine mandatory + total_proof_size
84+
const mandatorySize = chainData.ref_time?.mandatory ?? 0;
85+
const totalSize = chainData.total_proof_size ?? 0;
86+
const combinedProof = mandatorySize + totalSize;
87+
return { ...acc, [chain]: combinedProof };
88+
}
5589
}
5690
return acc;
5791
},
5892
{} as Record<PolkadotChainName | KusamaChainName, number>
5993
);
6094

61-
// naive tps and mbs calculations
62-
const totalTps = chainNames.reduce((acc, chain) => {
63-
const paraId = Object.keys(paraIdToChainName).find(
64-
(key) => paraIdToChainName[Number(key)] === chain
65-
);
66-
if (paraId) {
67-
const key = `${selectedRelay}-${paraId}`;
68-
const chainData = consumptionData[key]?.current || {};
69-
const extrinsicsNum = chainData.extrinsics_num || 0;
70-
const blockTimeSec = chainData.block_time_seconds || 0;
71-
if (blockTimeSec > 0) {
72-
return acc + extrinsicsNum / blockTimeSec;
73-
}
74-
}
75-
return acc;
76-
}, 0);
77-
78-
const totalMbs = chainNames.reduce((acc, chain) => {
79-
const paraId = Object.keys(paraIdToChainName).find(
80-
(key) => paraIdToChainName[Number(key)] === chain
81-
);
82-
if (paraId) {
83-
const key = `${selectedRelay}-${paraId}`;
84-
const chainData = consumptionData[key]?.current || {};
85-
const blockTimeSec = chainData.block_time_seconds || 0;
86-
const chainProofSize = weightData[chain] || 0;
87-
if (blockTimeSec > 0) {
88-
return acc + (chainProofSize * PROOF_SIZE_MB) / blockTimeSec;
89-
}
90-
}
91-
return acc;
92-
}, 0);
95+
// store rolling metrics
96+
//const [rollingTps, setRollingTps] = useState(0);
97+
const [rollingTpsEma, setRollingTpsEma] = useState(0);
9398

94-
const totalMGas = totalMbs * (MB_TO_GAS / PROOF_SIZE_MB / GAS_TO_MGAS);
99+
//const [rollingMbs, setRollingMbs] = useState(0);
100+
const [rollingMbsEma, setRollingMbsEma] = useState(0);
95101

96-
// relay chain-specific stats
102+
//const [rollingGas, setRollingGas] = useState(0);
103+
const [rollingGasEma, setRollingGasEma] = useState(0);
104+
105+
// dedup blocks
106+
const blocksRef = useRef<BlockRecord[]>([]);
107+
const lastBlockNumberByChain = useRef<Record<string, number>>({});
108+
109+
// we also want the immediate (non-ema) relay data
110+
let weightRelay = 0;
111+
let tpsRelay = 0;
112+
let gasRelay = 0;
113+
114+
// figure out the relay chain's data
97115
const relayChainName = selectedRelay === 'Polkadot' ? 'Polkadot' : 'Kusama';
98116
const relayParaId = Object.keys(paraIdToChainName).find(
99-
(key) => paraIdToChainName[Number(key)] === relayChainName
117+
(k) => paraIdToChainName[Number(k)] === relayChainName
100118
);
101-
const relayKey = relayParaId ? `${selectedRelay}-${relayParaId}` : '';
102-
const relayChainData = relayKey ? consumptionData[relayKey]?.current || {} : {};
119+
if (relayParaId) {
120+
const relayKey = `${selectedRelay}-${relayParaId}`;
121+
const relayData = consumptionData[relayKey]?.current;
122+
if (relayData && relayData.block_time_seconds) {
123+
const blockTime = relayData.block_time_seconds || 6;
124+
const mandatorySize = relayData.ref_time?.mandatory ?? 0;
125+
const totalSize = relayData.total_proof_size ?? 0;
126+
weightRelay = mandatorySize + totalSize;
127+
tpsRelay = (relayData.extrinsics_num ?? 0) / blockTime;
128+
129+
const chainMbs = (weightRelay * PROOF_SIZE_MB) / blockTime;
130+
gasRelay = chainMbs * (MB_TO_GAS / PROOF_SIZE_MB / GAS_TO_MGAS);
131+
}
132+
}
133+
134+
useEffect(() => {
135+
const newBlocks: BlockRecord[] = [];
136+
137+
chainNames.forEach((chain) => {
138+
const paraId = Object.keys(paraIdToChainName).find(
139+
(key) => paraIdToChainName[Number(key)] === chain
140+
);
141+
if (!paraId) return;
142+
const key = `${selectedRelay}-${paraId}`;
143+
const chainData = consumptionData[key]?.current;
144+
if (!chainData) return;
145+
146+
const blockNum = chainData.block_number;
147+
const ts = chainData.timestamp;
148+
// only add if we haven't seen this block_number before for this chain
149+
if (
150+
typeof blockNum === 'number' &&
151+
ts &&
152+
(lastBlockNumberByChain.current[key] === undefined ||
153+
blockNum > lastBlockNumberByChain.current[key])
154+
) {
155+
lastBlockNumberByChain.current[key] = blockNum;
156+
157+
// combine mandatory + total_proof_size
158+
const mandatorySize = chainData.ref_time?.mandatory ?? 0;
159+
const totalSize = chainData.total_proof_size ?? 0;
160+
const totalProof = mandatorySize + totalSize;
161+
162+
newBlocks.push({
163+
chainKey: key,
164+
blockNumber: blockNum,
165+
timestamp: ts,
166+
extrinsics: chainData.extrinsics_num ?? 0,
167+
proofSize: totalProof,
168+
});
169+
}
170+
});
103171

104-
const tpsRelay = relayChainData.block_time_seconds
105-
? (relayChainData.extrinsics_num || 0) / relayChainData.block_time_seconds
106-
: 0;
172+
// if we actually have new blocks, compute rolling metrics
173+
if (newBlocks.length > 0) {
174+
// add them, prune old
175+
blocksRef.current = [...blocksRef.current, ...newBlocks].slice(-MAX_BLOCKS_STORE);
107176

108-
const weightRelay = weightData[relayChainName] || 0;
109-
const gasRelay = weightRelay * PROOF_SIZE_MB / (relayChainData.block_time_seconds || 6);
177+
const { tps, mbs, gas } = calculateRollingMetrics(blocksRef.current);
110178

111-
// store ema values and reset on relay change
112-
const [emaTps, setEmaTps] = useState(totalTps);
113-
const [emaMbs, setEmaMbs] = useState(totalMbs);
114-
const [emaMGas, setEmaMGas] = useState(totalMGas);
179+
//setRollingTps(tps);
180+
setRollingTpsEma((prev) => calculateEma(prev || tps, tps));
115181

116-
// recalculate ema whenever raw totals change
117-
useEffect(() => {
118-
setEmaTps((prev) => calculateEma(prev, totalTps));
119-
setEmaMbs((prev) => calculateEma(prev, totalMbs));
120-
setEmaMGas((prev) => calculateEma(prev, totalMGas));
121-
}, [totalTps, totalMbs, totalMGas]);
182+
//setRollingMbs(mbs);
183+
setRollingMbsEma((prev) => calculateEma(prev || mbs, mbs));
122184

123-
// reset ema when relay changes
124-
useEffect(() => {
125-
setEmaTps(totalTps);
126-
setEmaMbs(totalMbs);
127-
setEmaMGas(totalMGas);
128-
}, [selectedRelay]);
129-
130-
// format for display
131-
const formattedTotalTps = emaTps.toFixed(2);
132-
const formattedTotalMbs = emaMbs.toFixed(2);
133-
const formattedTotalGas = emaMGas.toFixed(2);
134-
const formattedTpsRelay = tpsRelay.toFixed(2);
135-
const formattedGasRelay = gasRelay.toFixed(2);
185+
// setRollingGas(gas);
186+
setRollingGasEma((prev) => calculateEma(prev || gas, gas));
187+
}
188+
}, [consumptionData, chainNames, paraIdToChainName, selectedRelay]);
136189

137190
return (
138191
<div className="app-container">
@@ -147,21 +200,28 @@ const App: React.FC = () => {
147200
<RelaySelector selectedRelay={selectedRelay} onRelayChange={setSelectedRelay} />
148201

149202
<MetricsTotal
150-
totalTps={formattedTotalTps}
151-
totalMbs={formattedTotalMbs}
152-
totalGas={formattedTotalGas}
153-
tpsRelay={parseFloat(formattedTpsRelay)}
203+
// the rolling 30s metrics
204+
// rollingTps={rollingTps.toFixed(2)}
205+
totalTps={rollingTpsEma.toFixed(2)}
206+
// rollingMbs={rollingMbs.toFixed(2)}
207+
totalMbs={rollingMbsEma.toFixed(2)}
208+
// rollingGas={rollingGas.toFixed(2)}
209+
totalGas={rollingGasEma.toFixed(2)}
210+
211+
// immediate relay chain-only stats (non-ema)
212+
tpsRelay={tpsRelay}
154213
weightRelay={weightRelay}
155-
gasRelay={parseFloat(formattedGasRelay)}
214+
gasRelay={gasRelay}
156215
/>
157216

158217
<div className="textbox">
159218
<ChainTable
160219
consumptionData={consumptionData}
161-
weightData={weightData}
162220
chains={chainNames}
163221
selectedRelay={selectedRelay}
164222
paraIdToChainName={paraIdToChainName}
223+
// pass in weightData for chain usage
224+
weightData={weightData}
165225
/>
166226
</div>
167227
</div>
@@ -174,4 +234,3 @@ const App: React.FC = () => {
174234
};
175235

176236
export default App;
177-

src/types/chains.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,15 @@ export type ChainMetrics = {
114114
block: number;
115115
kbps: number;
116116
};
117+
118+
export type ChainData = {
119+
block_time_seconds?: number;
120+
extrinsics_num?: number;
121+
ref_time?: {
122+
mandatory?: number;
123+
operational?: number;
124+
normal?: number;
125+
};
126+
total_proof_size?: number;
127+
};
128+

0 commit comments

Comments
 (0)