Skip to content

Commit

Permalink
changes for run
Browse files Browse the repository at this point in the history
  • Loading branch information
mikecot committed Sep 10, 2024
1 parent fbcf8ae commit 41f6ee3
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 46 deletions.
45 changes: 20 additions & 25 deletions src/query/handlers/provider/providerChartsV2Handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { RequestHandlerBase } from '../../classes/RequestHandlerBase';
import { GetAndValidateProviderAddressFromRequest, GetAndValidateSpecIdFromRequestWithAll } from '../../utils/queryRequestArgParser';
import { PgColumn } from 'drizzle-orm/pg-core';
import { logger } from '../../../utils/utils';
import { RedisCache } from '../../classes/RedisCache';

type ProviderChartDataPoint = {
date: string;
Expand Down Expand Up @@ -75,30 +76,39 @@ class ProviderChartsV2Data extends RequestHandlerBase<ProviderChartsV2Response>
}

private async getAllAvailableSpecs(): Promise<string[]> {
logger.info('Starting getAllAvailableSpecs', { provider: this.provider });
const cacheKey = `provider-specs-chart-v2:${this.provider}`;

try {
const specs = await QueryGetJsinfoReadDbInstance()
.selectDistinct({ specId: JsinfoProviderAgrSchema.aggDailyRelayPayments.specId })
const cachedSpecs = await RedisCache.getArray(cacheKey);
if (cachedSpecs) {
logger.info('Retrieved specs from cache', { provider: this.provider, chain: this.chain });
return cachedSpecs;
}

const query = QueryGetJsinfoReadDbInstance()
.select({ specId: JsinfoProviderAgrSchema.aggDailyRelayPayments.specId })
.from(JsinfoProviderAgrSchema.aggDailyRelayPayments)
.where(eq(JsinfoProviderAgrSchema.aggDailyRelayPayments.provider, this.provider));
.where(eq(JsinfoProviderAgrSchema.aggDailyRelayPayments.provider, this.provider))
.groupBy(JsinfoProviderAgrSchema.aggDailyRelayPayments.specId);

const specs = await query;
const result = ['all', ...specs.filter(s => s.specId !== null).map(s => s.specId!)];

await RedisCache.setArray(cacheKey, result, 1200); // 20 minutes

logger.info('getAllAvailableSpecs completed successfully', { specsLength: specs.length });
return ['all', ...specs.filter(s => s.specId !== null).map(s => s.specId!)];
return result;
} catch (error) {
logger.error('Error in getAllAvailableSpecs', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : 'No stack trace available',
context: { provider: this.provider }
context: { provider: this.provider, chain: this.chain }
});
throw error;
}
}

private async getProviderData(from: Date, to: Date): Promise<ProviderChartDataPoint[]> {
logger.info(`Starting getProviderData for provider: ${this.provider}, chain: ${this.chain}, from: ${from.toISOString()}, to: ${to.toISOString()}`);
try {
const formattedData: ProviderChartDataPoint[] = [];

const qosMetricWeightedAvg = (metric: PgColumn) => sql<number>`SUM(${metric} * ${JsinfoProviderAgrSchema.aggDailyRelayPayments.relaySum}) / SUM(CASE WHEN ${metric} IS NOT NULL THEN ${JsinfoProviderAgrSchema.aggDailyRelayPayments.relaySum} ELSE 0 END)`;

let conditions = and(
Expand All @@ -123,18 +133,12 @@ class ProviderChartsV2Data extends RequestHandlerBase<ProviderChartsV2Response>
.where(conditions)
.orderBy(desc(JsinfoProviderAgrSchema.aggDailyRelayPayments.dateday));

logger.info(`SQL Query: ${query.toSQL().sql}`);
logger.info(`SQL Params: ${JSON.stringify(query.toSQL().params)}`);

let data = await query;

logger.info(`Raw data returned: ${JSON.stringify(data)}`);

if (data.length === 0) {
logger.warn(`No data found for provider: ${this.provider}, chain: ${this.chain}, from: ${from.toISOString()}, to: ${to.toISOString()}`);
}

logger.info('getProviderData completed successfully', { dataLength: formattedData.length });
return data.map(item => ({
date: DateToISOString(item.date),
qos: Math.cbrt(Number(item.qosSyncAvg) * Number(item.qosAvailabilityAvg) * Number(item.qosLatencyAvg)),
Expand All @@ -161,16 +165,11 @@ Context:

protected async fetchDateRangeRecords(from: Date, to: Date): Promise<ProviderChartsV2Response[]> {
try {
logger.info('Starting fetchDateRangeRecords', { provider: this.provider, chain: this.chain, from, to });

await QueryCheckJsinfoReadDbInstance();
logger.info('QueryCheckJsinfoReadDbInstance completed');

const chartData = await this.getProviderData(from, to);
logger.info('getProviderData completed', { dataLength: chartData.length });

const allAvailableSpecs = await this.getAllAvailableSpecs();
logger.info('getAllAvailableSpecs completed', { specsLength: allAvailableSpecs.length });

const response = {
provider: this.provider,
Expand Down Expand Up @@ -205,7 +204,6 @@ export async function ProviderChartsV2RawHandler(request: FastifyRequest, reply:
}

let chain = await GetAndValidateSpecIdFromRequestWithAll(request, reply);
console.log("chain", chain);
if (chain === '') {
return reply;
}
Expand All @@ -217,9 +215,6 @@ export async function ProviderChartsV2RawHandler(request: FastifyRequest, reply:
}

let ret = result.data[0];
ret.chartData = FormatDateItems<ProviderChartDataPoint>(ret.chartData);

logger.info(`ProviderChartsV2RawHandler result: ${JSON.stringify(ret)}`);

return reply.send(ret);
}
238 changes: 238 additions & 0 deletions src/query/handlers/spec/specChartsV2Handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
// src/query/handlers/spec/specChartsV2Handler.ts

import { FastifyReply, FastifyRequest, RouteShorthandOptions } from 'fastify';
import { QueryCheckJsinfoReadDbInstance, QueryGetJsinfoReadDbInstance } from '../../queryDb';
import * as JsinfoProviderAgrSchema from '../../../schemas/jsinfoSchema/providerRelayPaymentsAgregation';
import { sql, gt, and, lt, desc, eq } from "drizzle-orm";
import { DateToISOString } from '../../utils/queryDateUtils';
import { RequestHandlerBase } from '../../classes/RequestHandlerBase';
import { GetAndValidateSpecIdFromRequest, GetAndValidateProviderAddressFromRequestWithAll } from '../../utils/queryRequestArgParser';
import { PgColumn } from 'drizzle-orm/pg-core';
import { logger } from '../../../utils/utils';
import { RedisCache } from '../../classes/RedisCache';
import { MonikerCache } from '../../classes/MonikerCache';

type SpecChartDataPoint = {
date: string;
qos: number;
qosSyncAvg: number;
qosAvailabilityAvg: number;
qosLatencyAvg: number;
cus: number;
relays: number;
};

type SpecChartsV2Response = {
spec: string;
selectedProvider: string;
allAvailableProviders: { [key: string]: string };
chartData: SpecChartDataPoint[];
totalItemCount: number;
};

export const SpecChartsV2RawHandlerOpts: RouteShorthandOptions = {
schema: {
response: {
200: {
type: 'object',
properties: {
spec: { type: 'string' },
selectedProvider: { type: 'string' },
allAvailableProviders: {
type: 'object',
additionalProperties: { type: 'string' }
},
chartData: {
type: 'array',
items: {
type: 'object',
properties: {
date: { type: 'string' },
qos: { type: 'number' },
qosSyncAvg: { type: 'number' },
qosAvailabilityAvg: { type: 'number' },
qosLatencyAvg: { type: 'number' },
cus: { type: 'number' },
relays: { type: 'number' }
}
}
},
totalItemCount: { type: 'number' }
}
}
}
}
};

class SpecChartsV2Data extends RequestHandlerBase<SpecChartsV2Response> {
private spec: string;
private provider: string;

constructor(spec: string, provider: string) {
super("SpecChartsV2Data");
this.spec = spec;
this.provider = provider;
}

public static GetInstance(spec: string, provider: string): SpecChartsV2Data {
return SpecChartsV2Data.GetInstanceBase(spec, provider);
}

private async getAllAvailableProviders(): Promise<{ [key: string]: string }> {
const cacheKey = `spec-providers-chart-v2:${this.spec}`;

try {
const cachedProviders = await RedisCache.getDict(cacheKey);
if (cachedProviders) {
logger.info('Retrieved providers from cache', { spec: this.spec });
return cachedProviders;
}

const sixMonthsAgo = new Date();
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);

const query = QueryGetJsinfoReadDbInstance()
.select({
provider: JsinfoProviderAgrSchema.aggDailyRelayPayments.provider,
latestDate: sql<Date>`MAX(${JsinfoProviderAgrSchema.aggDailyRelayPayments.dateday})`
})
.from(JsinfoProviderAgrSchema.aggDailyRelayPayments)
.where(eq(JsinfoProviderAgrSchema.aggDailyRelayPayments.specId, this.spec))
.groupBy(JsinfoProviderAgrSchema.aggDailyRelayPayments.provider)
.having(gt(sql<Date>`MAX(${JsinfoProviderAgrSchema.aggDailyRelayPayments.dateday})`, sixMonthsAgo));

const providers = await query;
const result: { [key: string]: string } = { 'all': 'All Providers' };

for (const p of providers) {
if (p.provider) {
const moniker = await MonikerCache.GetMonikerForProvider(p.provider);
result[p.provider] = moniker || p.provider;
}
}

await RedisCache.setDict(cacheKey, result, 1200); // 20 minutes

return result;
} catch (error) {
logger.error('Error in getAllAvailableProviders', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : 'No stack trace available',
context: { spec: this.spec }
});
throw error;
}
}

private async getSpecData(from: Date, to: Date): Promise<SpecChartDataPoint[]> {
try {
const qosMetricWeightedAvg = (metric: PgColumn) => sql<number>`SUM(${metric} * ${JsinfoProviderAgrSchema.aggDailyRelayPayments.relaySum}) / SUM(CASE WHEN ${metric} IS NOT NULL THEN ${JsinfoProviderAgrSchema.aggDailyRelayPayments.relaySum} ELSE 0 END)`;

let conditions = and(
eq(JsinfoProviderAgrSchema.aggDailyRelayPayments.specId, this.spec),
gt(JsinfoProviderAgrSchema.aggDailyRelayPayments.dateday, sql<Date>`${from}`),
lt(JsinfoProviderAgrSchema.aggDailyRelayPayments.dateday, sql<Date>`${to}`)
);

if (this.provider !== 'all') {
conditions = and(conditions, eq(JsinfoProviderAgrSchema.aggDailyRelayPayments.provider, this.provider));
}

let query = QueryGetJsinfoReadDbInstance().select({
date: JsinfoProviderAgrSchema.aggDailyRelayPayments.dateday,
qosSyncAvg: qosMetricWeightedAvg(JsinfoProviderAgrSchema.aggDailyRelayPayments.qosSyncAvg),
qosAvailabilityAvg: qosMetricWeightedAvg(JsinfoProviderAgrSchema.aggDailyRelayPayments.qosAvailabilityAvg),
qosLatencyAvg: qosMetricWeightedAvg(JsinfoProviderAgrSchema.aggDailyRelayPayments.qosLatencyAvg),
cus: sql<number>`SUM(${JsinfoProviderAgrSchema.aggDailyRelayPayments.cuSum})`,
relays: sql<number>`SUM(${JsinfoProviderAgrSchema.aggDailyRelayPayments.relaySum})`,
}).from(JsinfoProviderAgrSchema.aggDailyRelayPayments)
.groupBy(JsinfoProviderAgrSchema.aggDailyRelayPayments.dateday)
.where(conditions)
.orderBy(desc(JsinfoProviderAgrSchema.aggDailyRelayPayments.dateday));

let data = await query;

if (data.length === 0) {
logger.warn(`No data found for spec: ${this.spec}, provider: ${this.provider}, from: ${from.toISOString()}, to: ${to.toISOString()}`);
}

return data.map(item => ({
date: DateToISOString(item.date),
qos: Math.cbrt(Number(item.qosSyncAvg) * Number(item.qosAvailabilityAvg) * Number(item.qosLatencyAvg)),
qosSyncAvg: Number(item.qosSyncAvg),
qosAvailabilityAvg: Number(item.qosAvailabilityAvg),
qosLatencyAvg: Number(item.qosLatencyAvg),
cus: Number(item.cus),
relays: Number(item.relays)
}));
} catch (error) {
const errorMessage = `Error in getSpecData:
Error: ${error instanceof Error ? error.message : String(error)}
Stack: ${error instanceof Error ? error.stack : 'No stack trace available'}
Context:
Spec: ${this.spec}
Provider: ${this.provider}
From: ${from.toISOString()}
To: ${to.toISOString()}`;

logger.error(errorMessage);
throw error;
}
}

protected async fetchDateRangeRecords(from: Date, to: Date): Promise<SpecChartsV2Response[]> {
try {
await QueryCheckJsinfoReadDbInstance();

const chartData = await this.getSpecData(from, to);

const allAvailableProviders = await this.getAllAvailableProviders();

const response: SpecChartsV2Response = {
spec: this.spec,
selectedProvider: this.provider,
allAvailableProviders: allAvailableProviders,
chartData: chartData,
totalItemCount: chartData.length
};

logger.info('fetchDateRangeRecords completed successfully');
return [response];
} catch (error) {
const errorMessage = `Error in fetchDateRangeRecords:
Error: ${error instanceof Error ? error.message : String(error)}
Stack: ${error instanceof Error ? error.stack : 'No stack trace available'}
Context:
Spec: ${this.spec}
Provider: ${this.provider}
From: ${from.toISOString()}
To: ${to.toISOString()}`;

logger.error(errorMessage);

throw new Error(`Failed to fetch date range records: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
}

export async function SpecChartsV2RawHandler(request: FastifyRequest, reply: FastifyReply) {
let spec = await GetAndValidateSpecIdFromRequest(request, reply);
if (spec === '') {
return reply;
}

let provider = await GetAndValidateProviderAddressFromRequestWithAll('SpecChartsV2RawHandler', request, reply);
if (provider === '') {
return reply;
}

let result = await SpecChartsV2Data.GetInstance(spec, provider).DateRangeRequestHandler(request, reply);

if (result === null) {
return reply;
}

let ret = result.data[0];

return reply.send(ret);
}
Loading

0 comments on commit 41f6ee3

Please sign in to comment.