Skip to content
This repository was archived by the owner on Apr 26, 2026. It is now read-only.
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
38 changes: 35 additions & 3 deletions src/query/utils/queryProviderOptimizerMetricsHandlerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export interface MetricsItem {
tier2: number;
tier3: number;
};
selection_availability: number;
selection_latency: number;
selection_sync: number;
selection_stake: number;
selection_composite: number;
}

export interface BaseAggregatedMetrics {
Expand All @@ -48,6 +53,11 @@ export interface BaseAggregatedMetrics {
generic_score: number;
provider_stake: number;
epoch: number;
selection_availability: number;
selection_latency: number;
selection_sync: number;
selection_stake: number;
selection_composite: number;
}

export interface AggregatedMetricsWithTiers extends BaseAggregatedMetrics {
Expand Down Expand Up @@ -136,7 +146,12 @@ export function aggregateMetrics(
tier1: number[],
tier2: number[],
tier3: number[]
}
},
selection_availability: number[],
selection_latency: number[],
selection_sync: number[],
selection_stake: number[],
selection_composite: number[]
}>();

for (const metric of metrics) {
Expand Down Expand Up @@ -167,7 +182,12 @@ export function aggregateMetrics(
tier1: [],
tier2: [],
tier3: []
}
},
selection_availability: [],
selection_latency: [],
selection_sync: [],
selection_stake: [],
selection_composite: []
});
}

Expand Down Expand Up @@ -221,6 +241,13 @@ export function aggregateMetrics(
if (metric.tier_chances.tier2 != null) agg.tier_chances.tier2.push(parseFloat(String(metric.tier_chances.tier2)));
if (metric.tier_chances.tier3 != null) agg.tier_chances.tier3.push(parseFloat(String(metric.tier_chances.tier3)));
}

// Aggregate WRS selection scores
if (metric.selection_availability != null) agg.selection_availability.push(parseFloat(String(metric.selection_availability)));
if (metric.selection_latency != null) agg.selection_latency.push(parseFloat(String(metric.selection_latency)));
if (metric.selection_sync != null) agg.selection_sync.push(parseFloat(String(metric.selection_sync)));
if (metric.selection_stake != null) agg.selection_stake.push(parseFloat(String(metric.selection_stake)));
if (metric.selection_composite != null) agg.selection_composite.push(parseFloat(String(metric.selection_composite)));
}

return Array.from(aggregations.entries()).map(([key, agg]) => {
Expand All @@ -236,7 +263,12 @@ export function aggregateMetrics(
entry_index: parseFloat(avg(agg.entry_indices).toFixed(9)),
generic_score: parseFloat(avg(agg.generic_scores).toFixed(9)),
provider_stake: parseFloat(String(agg.provider_stake)),
epoch: parseFloat(String(agg.epoch))
epoch: parseFloat(String(agg.epoch)),
selection_availability: parseFloat(avg(agg.selection_availability).toFixed(9)),
selection_latency: parseFloat(avg(agg.selection_latency).toFixed(9)),
selection_sync: parseFloat(avg(agg.selection_sync).toFixed(9)),
selection_stake: parseFloat(avg(agg.selection_stake).toFixed(9)),
selection_composite: parseFloat(avg(agg.selection_composite).toFixed(9)),
};

if (!includeTiers) {
Expand Down
52 changes: 47 additions & 5 deletions src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export interface MetricsItem {
tier2: number;
tier3: number;
};
selection_availability?: number;
selection_latency?: number;
selection_sync?: number;
selection_stake?: number;
selection_composite?: number;
}

export interface BaseAggregatedMetrics {
Expand All @@ -49,6 +54,11 @@ export interface BaseAggregatedMetrics {
generic_score: number;
provider_stake: number;
epoch: number;
selection_availability: number;
selection_latency: number;
selection_sync: number;
selection_stake: number;
selection_composite: number;
}

export interface AggregatedMetricsWithTiers extends BaseAggregatedMetrics {
Expand Down Expand Up @@ -163,7 +173,12 @@ export function aggregateMetrics(
tier1: number[],
tier2: number[],
tier3: number[]
}
},
selection_availability: number[],
selection_latency: number[],
selection_sync: number[],
selection_stake: number[],
selection_composite: number[]
}>();

for (const metric of metrics) {
Expand Down Expand Up @@ -191,7 +206,12 @@ export function aggregateMetrics(
tier1: [],
tier2: [],
tier3: []
}
},
selection_availability: [],
selection_latency: [],
selection_sync: [],
selection_stake: [],
selection_composite: []
});
}

Expand Down Expand Up @@ -245,6 +265,13 @@ export function aggregateMetrics(
if (metric.tier_chances.tier2 != null) agg.tier_chances.tier2.push(parseFloat(String(metric.tier_chances.tier2)));
if (metric.tier_chances.tier3 != null) agg.tier_chances.tier3.push(parseFloat(String(metric.tier_chances.tier3)));
}

// Aggregate WRS selection scores
if (metric.selection_availability != null) agg.selection_availability.push(parseFloat(String(metric.selection_availability)));
if (metric.selection_latency != null) agg.selection_latency.push(parseFloat(String(metric.selection_latency)));
if (metric.selection_sync != null) agg.selection_sync.push(parseFloat(String(metric.selection_sync)));
if (metric.selection_stake != null) agg.selection_stake.push(parseFloat(String(metric.selection_stake)));
if (metric.selection_composite != null) agg.selection_composite.push(parseFloat(String(metric.selection_composite)));
}

return Array.from(aggregations.entries()).map(([key, agg]) => {
Expand All @@ -259,7 +286,12 @@ export function aggregateMetrics(
entry_index: parseFloat(avg(agg.entry_indices).toFixed(9)),
generic_score: parseFloat(avg(agg.generic_scores).toFixed(9)),
provider_stake: parseFloat(String(agg.provider_stake)),
epoch: parseFloat(String(agg.epoch))
epoch: parseFloat(String(agg.epoch)),
selection_availability: parseFloat(avg(agg.selection_availability).toFixed(9)),
selection_latency: parseFloat(avg(agg.selection_latency).toFixed(9)),
selection_sync: parseFloat(avg(agg.selection_sync).toFixed(9)),
selection_stake: parseFloat(avg(agg.selection_stake).toFixed(9)),
selection_composite: parseFloat(avg(agg.selection_composite).toFixed(9)),
};

if (!includeTiers) {
Expand Down Expand Up @@ -446,7 +478,12 @@ export const AVAILABLE_METRICS = {
sync_score: "Sync Score",
generic_score: "Reputation Score",
node_error_rate: "Error Rate",
entry_index: "Entry Index"
entry_index: "Entry Index",
selection_latency: "Latency Score",
selection_availability: "Availability Score",
selection_sync: "Sync Score",
selection_stake: "Stake Score",
selection_composite: "Composite Score"
} as const;

export const AVAILABLE_METRICS_FULL = {
Expand All @@ -458,7 +495,12 @@ export const AVAILABLE_METRICS_FULL = {
tier_chances_tier3: "Tier 3 Chance",
provider_stake: "Provider Stake",
metrics_count: "Metrics Count",
epoch: "Epoch"
epoch: "Epoch",
selection_availability: "Availability Score",
selection_latency: "Latency Score",
selection_sync: "Sync Score",
selection_stake: "Stake Score",
selection_composite: "Composite Score"
} as const;

export interface MetricsResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export interface ConsumerOptimizerMetricsByProviderItem {
consumer_hostname: string;
metrics_count: number;
provider_stake: number;
provider: string;
provider_moniker: string;
latency_score: number;
availability_score: number;
sync_score: number;
Expand All @@ -28,6 +30,18 @@ export interface ConsumerOptimizerMetricsByProviderItem {
entry_index: number;
chain: string;
epoch: number;
tier_average: number;
tier_chances: {
tier0: number;
tier1: number;
tier2: number;
tier3: number;
};
selection_availability: number;
selection_latency: number;
selection_sync: number;
selection_stake: number;
selection_composite: number;
}

export interface ConsumerOptimizerMetricsByProviderResponse {
Expand Down Expand Up @@ -100,7 +114,18 @@ export class ConsumerOptimizerMetricsByProviderResource extends RedisResourceBas
node_error_rate_sum: aggregatedConsumerOptimizerMetrics.node_error_rate_sum,
entry_index_sum: aggregatedConsumerOptimizerMetrics.entry_index_sum,
provider_stake: aggregatedConsumerOptimizerMetrics.max_provider_stake,
max_epoch: aggregatedConsumerOptimizerMetrics.max_epoch
max_epoch: aggregatedConsumerOptimizerMetrics.max_epoch,
tier_sum: aggregatedConsumerOptimizerMetrics.tier_sum,
tier_metrics_count: aggregatedConsumerOptimizerMetrics.tier_metrics_count,
tier_chance_0_sum: aggregatedConsumerOptimizerMetrics.tier_chance_0_sum,
tier_chance_1_sum: aggregatedConsumerOptimizerMetrics.tier_chance_1_sum,
tier_chance_2_sum: aggregatedConsumerOptimizerMetrics.tier_chance_2_sum,
tier_chance_3_sum: aggregatedConsumerOptimizerMetrics.tier_chance_3_sum,
selection_availability_sum: aggregatedConsumerOptimizerMetrics.selection_availability_sum,
selection_latency_sum: aggregatedConsumerOptimizerMetrics.selection_latency_sum,
selection_sync_sum: aggregatedConsumerOptimizerMetrics.selection_sync_sum,
selection_stake_sum: aggregatedConsumerOptimizerMetrics.selection_stake_sum,
selection_composite_sum: aggregatedConsumerOptimizerMetrics.selection_composite_sum,
})
.from(aggregatedConsumerOptimizerMetrics)
.where(and(
Expand All @@ -112,6 +137,7 @@ export class ConsumerOptimizerMetricsByProviderResource extends RedisResourceBas
, `ConsumerOptimizerMetricsByProviderResource::getAggregatedMetrics_${provider}_${from}_${to}`);

const validMetrics: ConsumerOptimizerMetricsByProviderItem[] = [];
const providerMoniker = await ProviderMonikerService.GetMonikerForProvider(provider) || provider;

for (const m of metrics) {
if (m.latency_score_sum === null ||
Expand Down Expand Up @@ -149,13 +175,37 @@ export class ConsumerOptimizerMetricsByProviderResource extends RedisResourceBas
consumer_hostname: m.consumer_hostname,
metrics_count: m.metrics_count,
provider_stake: m.provider_stake,
provider: provider,
provider_moniker: providerMoniker,
latency_score: Number(m.latency_score_sum) / Number(m.metrics_count),
availability_score: Number(m.availability_score_sum) / Number(m.metrics_count),
sync_score: Number(m.sync_score_sum) / Number(m.metrics_count),
generic_score: Number(m.generic_score_sum) / Number(m.metrics_count),
node_error_rate: Number(m.node_error_rate_sum) / Number(m.metrics_count),
entry_index: Number(m.entry_index_sum) / Number(m.metrics_count),
epoch: Number(m.max_epoch),
tier_average: (m.tier_metrics_count ?? 0) > 0 && m.tier_sum != null ?
Number(m.tier_sum) / Number(m.tier_metrics_count) : 0,
tier_chances: {
tier0: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_0_sum != null ?
Number(m.tier_chance_0_sum) / Number(m.tier_metrics_count) : 0,
tier1: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_1_sum != null ?
Number(m.tier_chance_1_sum) / Number(m.tier_metrics_count) : 0,
tier2: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_2_sum != null ?
Number(m.tier_chance_2_sum) / Number(m.tier_metrics_count) : 0,
tier3: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_3_sum != null ?
Number(m.tier_chance_3_sum) / Number(m.tier_metrics_count) : 0,
},
selection_availability: m.selection_availability_sum != null ?
Number(m.selection_availability_sum) / Number(m.metrics_count) : 0,
selection_latency: m.selection_latency_sum != null ?
Number(m.selection_latency_sum) / Number(m.metrics_count) : 0,
selection_sync: m.selection_sync_sum != null ?
Number(m.selection_sync_sum) / Number(m.metrics_count) : 0,
selection_stake: m.selection_stake_sum != null ?
Number(m.selection_stake_sum) / Number(m.metrics_count) : 0,
selection_composite: m.selection_composite_sum != null ?
Number(m.selection_composite_sum) / Number(m.metrics_count) : 0,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export interface ConsumerOptimizerMetricsFullByProviderItem {
consumer_hostname: string;
metrics_count: number;
provider_stake: number;
provider: string;
provider_moniker: string;
latency_score: number;
availability_score: number;
sync_score: number;
Expand All @@ -35,6 +37,12 @@ export interface ConsumerOptimizerMetricsFullByProviderItem {
tier2: number;
tier3: number;
};
// WRS normalized scores (0-1, higher is better)
selection_availability: number;
selection_latency: number;
selection_sync: number;
selection_stake: number;
selection_composite: number;
}

export interface ConsumerOptimizerMetricsFullByProviderResponse {
Expand Down Expand Up @@ -96,6 +104,7 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc

const metrics = await queryRelays(db =>
db.select({
provider: aggregatedConsumerOptimizerMetrics.provider,
chain: aggregatedConsumerOptimizerMetrics.chain,
hourly_timestamp: aggregatedConsumerOptimizerMetrics.hourly_timestamp,
consumer: aggregatedConsumerOptimizerMetrics.consumer,
Expand All @@ -115,6 +124,11 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc
tier_chance_1_sum: aggregatedConsumerOptimizerMetrics.tier_chance_1_sum,
tier_chance_2_sum: aggregatedConsumerOptimizerMetrics.tier_chance_2_sum,
tier_chance_3_sum: aggregatedConsumerOptimizerMetrics.tier_chance_3_sum,
selection_availability_sum: aggregatedConsumerOptimizerMetrics.selection_availability_sum,
selection_latency_sum: aggregatedConsumerOptimizerMetrics.selection_latency_sum,
selection_sync_sum: aggregatedConsumerOptimizerMetrics.selection_sync_sum,
selection_stake_sum: aggregatedConsumerOptimizerMetrics.selection_stake_sum,
selection_composite_sum: aggregatedConsumerOptimizerMetrics.selection_composite_sum,
})
.from(aggregatedConsumerOptimizerMetrics)
.where(and(
Expand All @@ -132,6 +146,7 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc
// });

const validMetrics: ConsumerOptimizerMetricsFullByProviderItem[] = [];
const providerMoniker = await ProviderMonikerService.GetMonikerForProvider(provider) || provider;

for (const m of metrics) {
if (m.latency_score_sum === null ||
Expand Down Expand Up @@ -169,6 +184,8 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc
consumer_hostname: m.consumer_hostname,
metrics_count: m.metrics_count,
provider_stake: m.provider_stake,
provider: m.provider || provider,
provider_moniker: providerMoniker,
latency_score: Number(m.latency_score_sum) / Number(m.metrics_count),
availability_score: Number(m.availability_score_sum) / Number(m.metrics_count),
sync_score: Number(m.sync_score_sum) / Number(m.metrics_count),
Expand All @@ -187,7 +204,17 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc
Number(m.tier_chance_2_sum) / Number(m.tier_metrics_count) : 0,
tier3: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_3_sum != null ?
Number(m.tier_chance_3_sum) / Number(m.tier_metrics_count) : 0,
}
},
selection_availability: m.selection_availability_sum != null ?
Number(m.selection_availability_sum) / Number(m.metrics_count) : 0,
selection_latency: m.selection_latency_sum != null ?
Number(m.selection_latency_sum) / Number(m.metrics_count) : 0,
selection_sync: m.selection_sync_sum != null ?
Number(m.selection_sync_sum) / Number(m.metrics_count) : 0,
selection_stake: m.selection_stake_sum != null ?
Number(m.selection_stake_sum) / Number(m.metrics_count) : 0,
selection_composite: m.selection_composite_sum != null ?
Number(m.selection_composite_sum) / Number(m.metrics_count) : 0,
});
}

Expand Down
Loading