Skip to content

Commit 2ab4af0

Browse files
committed
refactor(engine-timings): drop per-slot endpoints, derive data from hourly & el-client
Remove the heavy newPayloadBySlot and getBlobsBySlot queries and instead compute slot ranges, status counts and blob distributions from the lighter hourly and per-EL-client aggregations. This keeps the same visual output while cutting two expensive API calls and reducing overall page load time.
1 parent 1c5ba5f commit 2ab4af0

File tree

3 files changed

+59
-130
lines changed

3 files changed

+59
-130
lines changed

src/pages/ethereum/execution/timings/components/GetBlobsTab/GetBlobsTab.tsx

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export function GetBlobsTab({ data, isLoading }: GetBlobsTabProps): JSX.Element
6666
return <GetBlobsTabSkeleton />;
6767
}
6868

69-
const { getBlobsBySlot, getBlobsByElClient, getBlobsDurationHistogram, getBlobsHourly, getBlobsDaily } = data;
69+
const { getBlobsByElClient, getBlobsDurationHistogram, getBlobsHourly, getBlobsDaily } = data;
7070

7171
// Filter to SUCCESS status only for duration-based charts
7272
const successBlobsByElClient = getBlobsByElClient.filter(r => r.status?.toUpperCase() === 'SUCCESS');
@@ -112,21 +112,27 @@ export function GetBlobsTab({ data, isLoading }: GetBlobsTabProps): JSX.Element
112112

113113
const { totalObservations, successRate, avgDuration } = aggregatedStats;
114114

115-
// Prepare slot data for charts
116-
const slotData = [...getBlobsBySlot].sort((a, b) => (a.slot ?? 0) - (b.slot ?? 0));
117-
// Filter to SUCCESS only for metrics that should match the table
118-
const successSlotData = slotData.filter(s => s.status?.toUpperCase() === 'SUCCESS');
115+
// Calculate slot bounds from per-EL-client data (which has per-slot granularity)
116+
const slotBounds = (() => {
117+
let min = Infinity;
118+
let max = -Infinity;
119+
successBlobsByElClient.forEach(item => {
120+
const slot = item.slot ?? 0;
121+
if (slot < min) min = slot;
122+
if (slot > max) max = slot;
123+
});
124+
return { minSlot: min === Infinity ? 0 : min, maxSlot: max === -Infinity ? 0 : max };
125+
})();
119126

120-
const minSlot = slotData.length > 0 ? (slotData[0].slot ?? 0) : 0;
121-
const maxSlot = slotData.length > 0 ? (slotData[slotData.length - 1].slot ?? 0) : 0;
127+
const { minSlot, maxSlot } = slotBounds;
122128

123-
// Calculate weighted average blobs per request (SUCCESS only to match table)
124-
const successSlotTotalObs = successSlotData.reduce((sum, s) => sum + (s.observation_count ?? 0), 0);
125-
const totalWeightedBlobs = successSlotData.reduce(
129+
// Calculate weighted average blobs per request from per-EL-client data (SUCCESS only)
130+
const successTotalObs = successBlobsByElClient.reduce((sum, s) => sum + (s.observation_count ?? 0), 0);
131+
const totalWeightedBlobs = successBlobsByElClient.reduce(
126132
(sum, s) => sum + (s.avg_returned_count ?? 0) * (s.observation_count ?? 0),
127133
0
128134
);
129-
const avgBlobsPerRequest = successSlotTotalObs > 0 ? (totalWeightedBlobs / successSlotTotalObs).toFixed(1) : '0';
135+
const avgBlobsPerRequest = successTotalObs > 0 ? (totalWeightedBlobs / successTotalObs).toFixed(1) : '0';
130136

131137
// Prepare duration histogram data (SUCCESS status only)
132138
const histogramMap = new Map<number, number>();
@@ -140,30 +146,15 @@ export function GetBlobsTab({ data, isLoading }: GetBlobsTabProps): JSX.Element
140146
const histogramLabels = histogramBuckets.map(bucket => `${bucket}ms`);
141147
const histogramData = histogramBuckets.map(bucket => histogramMap.get(bucket) ?? 0);
142148

143-
// Calculate status breakdown from slot data for the status distribution chart
144-
const statusTotals = slotData.reduce(
145-
(acc, item) => {
146-
const status = item.status?.toUpperCase() ?? 'UNKNOWN';
147-
const count = item.observation_count ?? 0;
148-
switch (status) {
149-
case 'SUCCESS':
150-
acc.success += count;
151-
break;
152-
case 'PARTIAL':
153-
acc.partial += count;
154-
break;
155-
case 'EMPTY':
156-
acc.empty += count;
157-
break;
158-
case 'ERROR':
159-
acc.error += count;
160-
break;
161-
case 'UNSUPPORTED':
162-
acc.unsupported += count;
163-
break;
164-
}
165-
return acc;
166-
},
149+
// Calculate status breakdown from pre-aggregated hourly data
150+
const statusTotals = getBlobsHourly.reduce(
151+
(acc, item) => ({
152+
success: acc.success + (item.success_count ?? 0),
153+
partial: acc.partial + (item.partial_count ?? 0),
154+
empty: acc.empty + (item.empty_count ?? 0),
155+
error: acc.error + (item.error_count ?? 0),
156+
unsupported: acc.unsupported + (item.unsupported_count ?? 0),
157+
}),
167158
{ success: 0, partial: 0, empty: 0, error: 0, unsupported: 0 }
168159
);
169160

@@ -177,9 +168,9 @@ export function GetBlobsTab({ data, isLoading }: GetBlobsTabProps): JSX.Element
177168
{ value: statusTotals.unsupported, color: themeColors.muted },
178169
];
179170

180-
// Prepare blob count distribution (SUCCESS only to match table)
171+
// Prepare blob count distribution from per-EL-client data (SUCCESS only to match table)
181172
const blobCountMap = new Map<number, number>();
182-
successSlotData.forEach(item => {
173+
successBlobsByElClient.forEach(item => {
183174
const blobCount = item.avg_returned_count ?? 0;
184175
const roundedCount = Math.round(blobCount);
185176
const obsCount = item.observation_count ?? 0;

src/pages/ethereum/execution/timings/components/NewPayloadTab/NewPayloadTab.tsx

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface NewPayloadTabProps {
2020
*/
2121
export function NewPayloadTab({ data }: NewPayloadTabProps): JSX.Element {
2222
const themeColors = useThemeColors();
23-
const { newPayloadBySlot, newPayloadDurationHistogram, newPayloadByElClient } = data;
23+
const { newPayloadDurationHistogram, newPayloadByElClient } = data;
2424

2525
// Filter to VALID status only for duration-based charts
2626
const validPayloadByElClient = newPayloadByElClient.filter(r => r.status?.toUpperCase() === 'VALID');
@@ -74,11 +74,20 @@ export function NewPayloadTab({ data }: NewPayloadTabProps): JSX.Element {
7474

7575
const { totalObservations, validRate, avgDuration, avgP95Duration } = aggregatedStats;
7676

77-
// Prepare slot data for charts (not stats)
78-
const slotData = [...newPayloadBySlot].sort((a, b) => (a.slot ?? 0) - (b.slot ?? 0));
77+
// Calculate slot bounds from per-EL-client data (which has per-slot granularity)
78+
// This avoids needing the slow newPayloadBySlot endpoint
79+
const slotBounds = (() => {
80+
let min = Infinity;
81+
let max = -Infinity;
82+
validPayloadByElClient.forEach(item => {
83+
const slot = item.slot ?? 0;
84+
if (slot < min) min = slot;
85+
if (slot > max) max = slot;
86+
});
87+
return { minSlot: min === Infinity ? 0 : min, maxSlot: max === -Infinity ? 0 : max };
88+
})();
7989

80-
const minSlot = slotData.length > 0 ? (slotData[0].slot ?? 0) : 0;
81-
const maxSlot = slotData.length > 0 ? (slotData[slotData.length - 1].slot ?? 0) : 0;
90+
const { minSlot, maxSlot } = slotBounds;
8291

8392
// Prepare duration histogram data (VALID status only)
8493
// Group histogram data by bucket and sum VALID counts
@@ -93,33 +102,16 @@ export function NewPayloadTab({ data }: NewPayloadTabProps): JSX.Element {
93102
const histogramLabels = histogramBuckets.map(bucket => `${bucket}ms`);
94103
const histogramData = histogramBuckets.map(bucket => histogramMap.get(bucket) ?? 0);
95104

96-
// Calculate status breakdown from slot data for the status distribution chart
97-
const statusTotals = slotData.reduce(
98-
(acc, item) => {
99-
const status = item.status?.toUpperCase() ?? 'UNKNOWN';
100-
const count = item.observation_count ?? 0;
101-
switch (status) {
102-
case 'VALID':
103-
acc.valid += count;
104-
break;
105-
case 'INVALID':
106-
acc.invalid += count;
107-
break;
108-
case 'SYNCING':
109-
acc.syncing += count;
110-
break;
111-
case 'ACCEPTED':
112-
acc.accepted += count;
113-
break;
114-
case 'INVALID_BLOCK_HASH':
115-
acc.invalidBlockHash += count;
116-
break;
117-
case 'ERROR':
118-
acc.error += count;
119-
break;
120-
}
121-
return acc;
122-
},
105+
// Calculate status breakdown from pre-aggregated hourly data
106+
const statusTotals = newPayloadHourly.reduce(
107+
(acc, item) => ({
108+
valid: acc.valid + (item.valid_count ?? 0),
109+
invalid: acc.invalid + (item.invalid_count ?? 0),
110+
syncing: acc.syncing + (item.syncing_count ?? 0),
111+
accepted: acc.accepted + (item.accepted_count ?? 0),
112+
invalidBlockHash: acc.invalidBlockHash + (item.invalid_block_hash_count ?? 0),
113+
error: 0, // Hourly data doesn't track errors separately (they're rare)
114+
}),
123115
{ valid: 0, invalid: 0, syncing: 0, accepted: 0, invalidBlockHash: 0, error: 0 }
124116
);
125117

src/pages/ethereum/execution/timings/hooks/useEngineTimingsData.ts

Lines changed: 7 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,20 @@ import { useQueries } from '@tanstack/react-query';
33
import {
44
fctEngineNewPayloadStatusHourlyServiceList,
55
fctEngineNewPayloadStatusDailyServiceList,
6-
fctEngineNewPayloadBySlotServiceList,
76
fctEngineNewPayloadByElClientServiceList,
87
fctEngineNewPayloadDurationChunked50MsServiceList,
98
fctEngineGetBlobsStatusHourlyServiceList,
109
fctEngineGetBlobsStatusDailyServiceList,
11-
fctEngineGetBlobsBySlotServiceList,
1210
fctEngineGetBlobsByElClientServiceList,
1311
fctEngineGetBlobsDurationChunked50MsServiceList,
1412
} from '@/api/sdk.gen';
1513
import type {
1614
FctEngineNewPayloadStatusHourly,
1715
FctEngineNewPayloadStatusDaily,
18-
FctEngineNewPayloadBySlot,
1916
FctEngineNewPayloadByElClient,
2017
FctEngineNewPayloadDurationChunked50Ms,
2118
FctEngineGetBlobsStatusHourly,
2219
FctEngineGetBlobsStatusDaily,
23-
FctEngineGetBlobsBySlot,
2420
FctEngineGetBlobsByElClient,
2521
FctEngineGetBlobsDurationChunked50Ms,
2622
} from '@/api/types.gen';
@@ -33,10 +29,7 @@ export interface EngineTimingsData {
3329
newPayloadHourly: FctEngineNewPayloadStatusHourly[];
3430
newPayloadDaily: FctEngineNewPayloadStatusDaily[];
3531

36-
// newPayload per-slot data
37-
newPayloadBySlot: FctEngineNewPayloadBySlot[];
38-
39-
// newPayload per-EL-client aggregations
32+
// newPayload per-EL-client aggregations (includes per-slot granularity)
4033
newPayloadByElClient: FctEngineNewPayloadByElClient[];
4134

4235
// newPayload duration histogram
@@ -46,10 +39,7 @@ export interface EngineTimingsData {
4639
getBlobsHourly: FctEngineGetBlobsStatusHourly[];
4740
getBlobsDaily: FctEngineGetBlobsStatusDaily[];
4841

49-
// getBlobs per-slot data
50-
getBlobsBySlot: FctEngineGetBlobsBySlot[];
51-
52-
// getBlobs per-EL-client aggregations
42+
// getBlobs per-EL-client aggregations (includes per-slot granularity)
5343
getBlobsByElClient: FctEngineGetBlobsByElClient[];
5444

5545
// getBlobs duration histogram
@@ -192,27 +182,7 @@ export function useEngineTimingsData({
192182
),
193183
enabled: !!currentNetwork && !useHourlyData,
194184
},
195-
// newPayload per-slot data
196-
{
197-
queryKey: ['engine-timings', 'newPayload-by-slot', start, end, timeRange, referenceNodesOnly],
198-
queryFn: ({ signal }) =>
199-
fetchAllPages<FctEngineNewPayloadBySlot>(
200-
fctEngineNewPayloadBySlotServiceList,
201-
{
202-
query: {
203-
slot_start_date_time_gte: start,
204-
slot_start_date_time_lte: end,
205-
order_by: 'slot DESC',
206-
page_size: 10000,
207-
...refNodeFilter,
208-
},
209-
},
210-
'fct_engine_new_payload_by_slot',
211-
signal
212-
),
213-
enabled: !!currentNetwork,
214-
},
215-
// newPayload per-EL-client aggregations
185+
// newPayload per-EL-client aggregations (includes per-slot data)
216186
{
217187
queryKey: ['engine-timings', 'newPayload-by-el-client', start, end, referenceNodesOnly],
218188
queryFn: ({ signal }) =>
@@ -291,27 +261,7 @@ export function useEngineTimingsData({
291261
),
292262
enabled: !!currentNetwork && !useHourlyData && fetchBlobs,
293263
},
294-
// getBlobs per-slot data
295-
{
296-
queryKey: ['engine-timings', 'getBlobs-by-slot', start, end, timeRange, referenceNodesOnly],
297-
queryFn: ({ signal }) =>
298-
fetchAllPages<FctEngineGetBlobsBySlot>(
299-
fctEngineGetBlobsBySlotServiceList,
300-
{
301-
query: {
302-
slot_start_date_time_gte: start,
303-
slot_start_date_time_lte: end,
304-
order_by: 'slot DESC',
305-
page_size: 10000,
306-
...refNodeFilter,
307-
},
308-
},
309-
'fct_engine_get_blobs_by_slot',
310-
signal
311-
),
312-
enabled: !!currentNetwork && fetchBlobs,
313-
},
314-
// getBlobs per-EL-client aggregations
264+
// getBlobs per-EL-client aggregations (includes per-slot data)
315265
{
316266
queryKey: ['engine-timings', 'getBlobs-by-el-client', start, end, referenceNodesOnly],
317267
queryFn: ({ signal }) =>
@@ -358,19 +308,17 @@ export function useEngineTimingsData({
358308
const [
359309
newPayloadHourlyQuery,
360310
newPayloadDailyQuery,
361-
newPayloadBySlotQuery,
362311
newPayloadByElClientQuery,
363312
newPayloadDurationHistogramQuery,
364313
getBlobsHourlyQuery,
365314
getBlobsDailyQuery,
366-
getBlobsBySlotQuery,
367315
getBlobsByElClientQuery,
368316
getBlobsDurationHistogramQuery,
369317
] = queries;
370318

371-
// Check loading state for newPayload queries only (first 5)
372-
const newPayloadQueries = queries.slice(0, 5);
373-
const blobQueries = queries.slice(5);
319+
// Check loading state for newPayload queries only (first 4)
320+
const newPayloadQueries = queries.slice(0, 4);
321+
const blobQueries = queries.slice(4);
374322

375323
const isLoading = newPayloadQueries.some(q => q.isLoading);
376324
const isLoadingBlobs = blobQueries.some(q => q.isLoading);
@@ -384,12 +332,10 @@ export function useEngineTimingsData({
384332
? {
385333
newPayloadHourly: newPayloadHourlyQuery.data ?? [],
386334
newPayloadDaily: newPayloadDailyQuery.data ?? [],
387-
newPayloadBySlot: newPayloadBySlotQuery.data ?? [],
388335
newPayloadByElClient: newPayloadByElClientQuery.data ?? [],
389336
newPayloadDurationHistogram: newPayloadDurationHistogramQuery.data ?? [],
390337
getBlobsHourly: getBlobsHourlyQuery.data ?? [],
391338
getBlobsDaily: getBlobsDailyQuery.data ?? [],
392-
getBlobsBySlot: getBlobsBySlotQuery.data ?? [],
393339
getBlobsByElClient: getBlobsByElClientQuery.data ?? [],
394340
getBlobsDurationHistogram: getBlobsDurationHistogramQuery.data ?? [],
395341
}

0 commit comments

Comments
 (0)