diff --git a/src/query/utils/queryProviderOptimizerMetricsHandlerUtils.ts b/src/query/utils/queryProviderOptimizerMetricsHandlerUtils.ts index ffa9d666..e6cc0d37 100644 --- a/src/query/utils/queryProviderOptimizerMetricsHandlerUtils.ts +++ b/src/query/utils/queryProviderOptimizerMetricsHandlerUtils.ts @@ -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 { @@ -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 { @@ -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) { @@ -167,7 +182,12 @@ export function aggregateMetrics( tier1: [], tier2: [], tier3: [] - } + }, + selection_availability: [], + selection_latency: [], + selection_sync: [], + selection_stake: [], + selection_composite: [] }); } @@ -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]) => { @@ -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) { diff --git a/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts b/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts index a1479328..6d519b51 100644 --- a/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts +++ b/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts @@ -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 { @@ -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 { @@ -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) { @@ -191,7 +206,12 @@ export function aggregateMetrics( tier1: [], tier2: [], tier3: [] - } + }, + selection_availability: [], + selection_latency: [], + selection_sync: [], + selection_stake: [], + selection_composite: [] }); } @@ -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]) => { @@ -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) { @@ -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 = { @@ -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 { diff --git a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetrics.ts b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetrics.ts index 2a14e93b..cb871e13 100644 --- a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetrics.ts +++ b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetrics.ts @@ -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; @@ -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 { @@ -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( @@ -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 || @@ -149,6 +175,8 @@ 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), @@ -156,6 +184,28 @@ export class ConsumerOptimizerMetricsByProviderResource extends RedisResourceBas 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, }); } diff --git a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts index 7add77f7..34b23edc 100644 --- a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts +++ b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts @@ -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; @@ -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 { @@ -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, @@ -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( @@ -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 || @@ -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), @@ -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, }); } diff --git a/src/redis/resources/spec/SpecConsumerOptimizerMetrics.ts b/src/redis/resources/spec/SpecConsumerOptimizerMetrics.ts index f4c40df9..9418a802 100644 --- a/src/redis/resources/spec/SpecConsumerOptimizerMetrics.ts +++ b/src/redis/resources/spec/SpecConsumerOptimizerMetrics.ts @@ -30,6 +30,11 @@ export interface ConsumerOptimizerMetricsBySpecItem { node_error_rate: number; entry_index: number; epoch: number; + selection_availability: number; + selection_latency: number; + selection_sync: number; + selection_stake: number; + selection_composite: number; } export interface ConsumerOptimizerMetricsBySpecResponse { @@ -107,7 +112,12 @@ export class ConsumerOptimizerMetricsBySpecResource extends RedisResourceBase 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, }); } diff --git a/src/schemas/relaysSchema.ts b/src/schemas/relaysSchema.ts index 5722bc42..d0f1b9b6 100644 --- a/src/schemas/relaysSchema.ts +++ b/src/schemas/relaysSchema.ts @@ -61,7 +61,14 @@ export const aggregatedConsumerOptimizerMetrics = pgTable('aggregated_consumer_o tier_chance_1_sum: numeric('tier_chance_1_sum'), tier_chance_2_sum: numeric('tier_chance_2_sum'), tier_chance_3_sum: numeric('tier_chance_3_sum'), - tier_metrics_count: integer('tier_metrics_count') + tier_metrics_count: integer('tier_metrics_count'), + + // WRS normalized scores + selection_availability_sum: numeric('selection_availability_sum'), + selection_latency_sum: numeric('selection_latency_sum'), + selection_sync_sum: numeric('selection_sync_sum'), + selection_stake_sum: numeric('selection_stake_sum'), + selection_composite_sum: numeric('selection_composite_sum'), }); export type AggregatedConsumerOptimizerMetrics = typeof aggregatedConsumerOptimizerMetrics.$inferSelect