Skip to content

Commit 7f1f762

Browse files
committed
Add caching for stats
1 parent a9dfe77 commit 7f1f762

File tree

10 files changed

+595
-433
lines changed

10 files changed

+595
-433
lines changed

cmd/main.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,12 +246,24 @@ func main() {
246246
}
247247
}
248248

249+
// Initialize stats service first
250+
statsService := service.NewStatsService(db, log)
251+
249252
// Start HTTP server for querying messages
250-
go http.InitServer(log, db)
253+
go http.InitServer(log, db, statsService)
251254

252255
// Initialize coinbase service
253256
coinbaseService := service.NewCoinbaseService(db, log)
254257

258+
// Calculate stats once on startup
259+
go func() {
260+
// Small delay to let some messages accumulate
261+
time.Sleep(5 * time.Second)
262+
if err := statsService.CalculateStats(); err != nil {
263+
log.Errorf("Failed to calculate initial stats: %v", err)
264+
}
265+
}()
266+
255267
// Start background processing of coinbase data
256268
go func() {
257269
ticker := time.NewTicker(5 * time.Minute)
@@ -267,6 +279,21 @@ func main() {
267279
}
268280
}
269281
}()
282+
283+
// Start background stats calculation (every minute)
284+
go func() {
285+
ticker := time.NewTicker(1 * time.Minute)
286+
defer ticker.Stop()
287+
288+
for {
289+
select {
290+
case <-ticker.C:
291+
if err := statsService.CalculateStats(); err != nil {
292+
log.Errorf("Failed to calculate stats: %v", err)
293+
}
294+
}
295+
}
296+
}()
270297

271298
go func() {
272299
ticker := time.NewTicker(2 * time.Minute)

frontend-react/src/components/BlockExplorer.tsx

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export const BlockExplorer: React.FC = () => {
1919
const [minHeight, setMinHeight] = useState<string>('');
2020
const [maxHeight, setMaxHeight] = useState<string>('');
2121
const [searchHash, setSearchHash] = useState<string>('');
22-
const [selectedVersion, setSelectedVersion] = useState<string>('all');
2322
const [sortField, setSortField] = useState<'height' | 'timestamp'>('height');
2423
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
2524

@@ -30,9 +29,7 @@ export const BlockExplorer: React.FC = () => {
3029

3130
// Stats
3231
const [stats, setStats] = useState({
33-
totalTracked: 0,
34-
latestHeights: {} as Record<string, number>,
35-
blocksToday: 0
32+
latestHeights: {} as Record<string, number>
3633
});
3734

3835
// State for tracking if we need to refresh
@@ -56,7 +53,7 @@ export const BlockExplorer: React.FC = () => {
5653
useEffect(() => {
5754
fetchBlockHeaders();
5855
updateStats();
59-
}, [selectedNetwork, minHeight, maxHeight, searchHash, selectedVersion, sortField, sortDirection, currentPage]);
56+
}, [selectedNetwork, minHeight, maxHeight, searchHash, sortField, sortDirection, currentPage]);
6057

6158
// Handle WebSocket refresh trigger
6259
useEffect(() => {
@@ -126,9 +123,7 @@ export const BlockExplorer: React.FC = () => {
126123
try {
127124
const stats = await ApiService.getMessageStats();
128125
setStats({
129-
totalTracked: stats.totalMessages,
130-
latestHeights: stats.latestBlockHeight,
131-
blocksToday: stats.messagesToday
126+
latestHeights: stats.latestBlockHeight
132127
});
133128
} catch (err) {
134129
console.error('Error fetching stats:', err);
@@ -172,7 +167,6 @@ export const BlockExplorer: React.FC = () => {
172167
setMinHeight('');
173168
setMaxHeight('');
174169
setSearchHash('');
175-
setSelectedVersion('all');
176170
setSelectedNetwork('all');
177171
setCurrentPage(1);
178172
};
@@ -181,8 +175,7 @@ export const BlockExplorer: React.FC = () => {
181175
selectedNetwork !== 'all',
182176
minHeight !== '',
183177
maxHeight !== '',
184-
searchHash !== '',
185-
selectedVersion !== 'all'
178+
searchHash !== ''
186179
].filter(Boolean).length;
187180

188181
return (
@@ -231,7 +224,7 @@ export const BlockExplorer: React.FC = () => {
231224
<h3 className="text-lg font-semibold text-white/90 mb-4">Network Block Heights</h3>
232225

233226
{/* Latest Heights */}
234-
<div className="space-y-3 mb-4">
227+
<div className="space-y-3">
235228
{Object.entries(stats.latestHeights).map(([network, height]) => (
236229
<div key={network} className="bg-white/10 rounded-xl p-3">
237230
<div className="flex justify-between items-center">
@@ -242,18 +235,6 @@ export const BlockExplorer: React.FC = () => {
242235
))}
243236
</div>
244237

245-
{/* Stats */}
246-
<div className="grid grid-cols-2 gap-3">
247-
<div className="bg-white/10 rounded-xl p-3">
248-
<div className="text-2xl font-bold text-white">{stats.totalTracked.toLocaleString()}</div>
249-
<div className="text-xs text-white/70">Total Tracked</div>
250-
</div>
251-
<div className="bg-white/10 rounded-xl p-3">
252-
<div className="text-2xl font-bold text-white">{stats.blocksToday.toLocaleString()}</div>
253-
<div className="text-xs text-white/70">Blocks Today</div>
254-
</div>
255-
</div>
256-
257238
{/* WebSocket Status */}
258239
<div className="mt-4 pt-4 border-t border-white/10">
259240
<WebSocketStatusComponent status={wsStatus} onReconnect={reconnect} />
@@ -330,7 +311,7 @@ export const BlockExplorer: React.FC = () => {
330311

331312
{/* Filter Content */}
332313
<div className="p-8">
333-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
314+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
334315
{/* Height Range */}
335316
<div className="space-y-2">
336317
<label className="block text-sm font-medium text-gray-700">Height Range</label>
@@ -364,20 +345,6 @@ export const BlockExplorer: React.FC = () => {
364345
/>
365346
</div>
366347

367-
{/* Version Filter */}
368-
<div className="space-y-2">
369-
<label className="block text-sm font-medium text-gray-700">Block Version</label>
370-
<select
371-
value={selectedVersion}
372-
onChange={(e) => setSelectedVersion(e.target.value)}
373-
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#1B1EA9] focus:border-transparent"
374-
>
375-
<option value="all">All Versions</option>
376-
<option value="1">Version 1</option>
377-
<option value="2">Version 2</option>
378-
<option value="536870912">Version 536870912</option>
379-
</select>
380-
</div>
381348
</div>
382349
</div>
383350
</div>
@@ -445,9 +412,6 @@ export const BlockExplorer: React.FC = () => {
445412
)}
446413
</div>
447414
</th>
448-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
449-
Version
450-
</th>
451415
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
452416
Nonce
453417
</th>
@@ -501,9 +465,6 @@ export const BlockExplorer: React.FC = () => {
501465
<div className="text-xs text-gray-400">{getRelativeTime(header.Timestamp)}</div>
502466
</div>
503467
</td>
504-
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
505-
{header.Version}
506-
</td>
507468
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
508469
{header.Nonce.toLocaleString()}
509470
</td>

frontend-react/src/components/Stats.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,16 @@ const Stats: React.FC = () => {
289289
</div>
290290
</div>
291291

292-
{/* Last Update */}
293-
{stats.lastMessageTime && (
294-
<div className="text-center text-sm text-gray-600 pb-8">
295-
Last message received: {formatTime(stats.lastMessageTime)}
292+
{/* Last Update and Cache Info */}
293+
<div className="text-center text-sm text-gray-600 pb-8">
294+
{stats.lastMessageTime && (
295+
<div>Last message received: {formatTime(stats.lastMessageTime)}</div>
296+
)}
297+
<div className="mt-1">
298+
Stats updated {stats.cacheAge < 60 ? `${stats.cacheAge} seconds` : `${Math.round(stats.cacheAge / 60)} minutes`} ago
299+
{stats.calculationTimeMs > 0 && ` • Calculated in ${stats.calculationTimeMs}ms`}
296300
</div>
297-
)}
301+
</div>
298302
</div>
299303
</div>
300304
);

frontend-react/src/services/api.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,9 @@ export class ApiService {
283283
latestBlockHeight: stats.latestBlockHeight || {},
284284
lastMessageTime: stats.lastMessageTime,
285285
topicStats: stats.topicStats || [],
286-
topPeers: stats.topPeers || []
286+
topPeers: stats.topPeers || [],
287+
cacheAge: stats.cacheAge || 0,
288+
calculationTimeMs: stats.calculationTimeMs || 0
287289
};
288290
} catch (error) {
289291
console.error('Error fetching message stats:', error);
@@ -294,7 +296,9 @@ export class ApiService {
294296
messagesToday: 0,
295297
latestBlockHeight: {},
296298
topicStats: [],
297-
topPeers: []
299+
topPeers: [],
300+
cacheAge: 0,
301+
calculationTimeMs: 0
298302
};
299303
}
300304
}

frontend-react/src/types/Message.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export interface MessageStats {
5252
lastMessageTime?: string;
5353
topicStats: TopicStat[];
5454
topPeers: PeerSummary[];
55+
cacheAge: number; // Age of cached data in seconds
56+
calculationTimeMs: number; // Time taken to calculate stats in milliseconds
5557
}
5658

5759
export interface PeerStat {

0 commit comments

Comments
 (0)