Optimize price API response size reduce the number of query to that API
This commit is contained in:
		
							parent
							
								
									f6c7839524
								
							
						
					
					
						commit
						5749820999
					
				@ -41,7 +41,13 @@ class MiningRoutes {
 | 
			
		||||
      res.header('Pragma', 'public');
 | 
			
		||||
      res.header('Cache-control', 'public');
 | 
			
		||||
      res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
 | 
			
		||||
      res.status(200).send(await PricesRepository.$getHistoricalPrice());
 | 
			
		||||
      if (req.query.timestamp) {
 | 
			
		||||
        res.status(200).send(await PricesRepository.$getNearestHistoricalPrice(
 | 
			
		||||
          parseInt(<string>req.query.timestamp ?? 0, 10)
 | 
			
		||||
        ));
 | 
			
		||||
      } else {
 | 
			
		||||
        res.status(200).send(await PricesRepository.$getHistoricalPrices());
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      res.status(500).send(e instanceof Error ? e.message : e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -104,9 +104,48 @@ class PricesRepository {
 | 
			
		||||
    return rates[0];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async $getHistoricalPrice(): Promise<Conversion | null> {
 | 
			
		||||
  public async $getNearestHistoricalPrice(timestamp: number | undefined): Promise<Conversion | null> {
 | 
			
		||||
    try {
 | 
			
		||||
      const [rates]: any[] = await DB.query(`SELECT *, UNIX_TIMESTAMP(time) as time FROM prices ORDER BY time DESC`);
 | 
			
		||||
      const [rates]: any[] = await DB.query(`
 | 
			
		||||
        SELECT *, UNIX_TIMESTAMP(time) AS time
 | 
			
		||||
        FROM prices
 | 
			
		||||
        WHERE UNIX_TIMESTAMP(time) < ?
 | 
			
		||||
        ORDER BY time DESC
 | 
			
		||||
        LIMIT 1`,
 | 
			
		||||
        [timestamp]
 | 
			
		||||
      );
 | 
			
		||||
      if (!rates) {
 | 
			
		||||
        throw Error(`Cannot get single historical price from the database`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Compute fiat exchange rates
 | 
			
		||||
      const latestPrice = await this.$getLatestConversionRates();
 | 
			
		||||
      const exchangeRates: ExchangeRates = {
 | 
			
		||||
        USDEUR: Math.round(latestPrice.EUR / latestPrice.USD * 100) / 100,
 | 
			
		||||
        USDGBP: Math.round(latestPrice.GBP / latestPrice.USD * 100) / 100,
 | 
			
		||||
        USDCAD: Math.round(latestPrice.CAD / latestPrice.USD * 100) / 100,
 | 
			
		||||
        USDCHF: Math.round(latestPrice.CHF / latestPrice.USD * 100) / 100,
 | 
			
		||||
        USDAUD: Math.round(latestPrice.AUD / latestPrice.USD * 100) / 100,
 | 
			
		||||
        USDJPY: Math.round(latestPrice.JPY / latestPrice.USD * 100) / 100,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        prices: rates,
 | 
			
		||||
        exchangeRates: exchangeRates
 | 
			
		||||
      };
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      logger.err(`Cannot fetch single historical prices from the db. Reason ${e instanceof Error ? e.message : e}`);
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async $getHistoricalPrices(): Promise<Conversion | null> {
 | 
			
		||||
    try {
 | 
			
		||||
      const [rates]: any[] = await DB.query(`
 | 
			
		||||
        SELECT *, UNIX_TIMESTAMP(time) AS time
 | 
			
		||||
        FROM prices
 | 
			
		||||
        ORDER BY time DESC
 | 
			
		||||
      `);
 | 
			
		||||
      if (!rates) {
 | 
			
		||||
        throw Error(`Cannot get average historical price from the database`);
 | 
			
		||||
      }
 | 
			
		||||
@ -127,7 +166,7 @@ class PricesRepository {
 | 
			
		||||
        exchangeRates: exchangeRates
 | 
			
		||||
      };
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      logger.err(`Cannot fetch averaged historical prices from the db. Reason ${e instanceof Error ? e.message : e}`);
 | 
			
		||||
      logger.err(`Cannot fetch historical prices from the db. Reason ${e instanceof Error ? e.message : e}`);
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -327,7 +327,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
            this.fetchRbfHistory$.next(this.tx.txid);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          this.priceService.getBlockPrice$(tx.status.block_time).pipe(
 | 
			
		||||
          this.priceService.getBlockPrice$(tx.status.block_time, true).pipe(
 | 
			
		||||
            tap((price) => {
 | 
			
		||||
              this.blockConversion = price;
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@ export class TxBowtieGraphTooltipComponent implements OnChanges {
 | 
			
		||||
 | 
			
		||||
  ngOnChanges(changes): void {
 | 
			
		||||
    if (changes.line?.currentValue) {
 | 
			
		||||
      this.priceService.getBlockPrice$(changes.line?.currentValue.timestamp).pipe(
 | 
			
		||||
      this.priceService.getBlockPrice$(changes.line?.currentValue.timestamp, true).pipe(
 | 
			
		||||
        tap((price) => {
 | 
			
		||||
          this.blockConversion = price;
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
@ -305,7 +305,10 @@ export class ApiService {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getHistoricalPrice$(): Observable<Conversion> {
 | 
			
		||||
    return this.httpClient.get<Conversion>( this.apiBaseUrl + this.apiBasePath + '/api/v1/historical-price');
 | 
			
		||||
  getHistoricalPrice$(timestamp: number | undefined): Observable<Conversion> {
 | 
			
		||||
    return this.httpClient.get<Conversion>(
 | 
			
		||||
      this.apiBaseUrl + this.apiBasePath + '/api/v1/historical-price' +
 | 
			
		||||
        (timestamp ? `?timestamp=${timestamp}` : '')
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -41,6 +41,10 @@ export interface ConversionDict {
 | 
			
		||||
})
 | 
			
		||||
export class PriceService {
 | 
			
		||||
  priceObservable$: Observable<Conversion>;
 | 
			
		||||
  singlePriceObservable$: Observable<Conversion>;
 | 
			
		||||
 | 
			
		||||
  lastQueriedTimestamp: number;
 | 
			
		||||
  lastPriceHistoryUpdate: number;
 | 
			
		||||
 | 
			
		||||
  historicalPrice: ConversionDict = {
 | 
			
		||||
    prices: null,
 | 
			
		||||
@ -63,9 +67,39 @@ export class PriceService {
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlockPrice$(blockTimestamp: number): Observable<Price | undefined> {
 | 
			
		||||
    if (!this.priceObservable$) {
 | 
			
		||||
      this.priceObservable$ = this.apiService.getHistoricalPrice$().pipe(shareReplay());
 | 
			
		||||
  getBlockPrice$(blockTimestamp: number, singlePrice = false): Observable<Price | undefined> {
 | 
			
		||||
    const now = new Date().getTime() / 1000;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Query nearest price for a specific blockTimestamp. The observable is invalidated if we
 | 
			
		||||
     * query a different timestamp than the last one
 | 
			
		||||
     */
 | 
			
		||||
    if (singlePrice) {
 | 
			
		||||
      if (!this.singlePriceObservable$ || (this.singlePriceObservable$ && blockTimestamp !== this.lastQueriedTimestamp)) {
 | 
			
		||||
        this.singlePriceObservable$ = this.apiService.getHistoricalPrice$(blockTimestamp).pipe(shareReplay());
 | 
			
		||||
        this.lastQueriedTimestamp = blockTimestamp;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return this.singlePriceObservable$.pipe(
 | 
			
		||||
        map((conversion) => {
 | 
			
		||||
          return {
 | 
			
		||||
            price: {
 | 
			
		||||
              USD: conversion.prices[0].USD, EUR: conversion.prices[0].EUR, GBP: conversion.prices[0].GBP, CAD: conversion.prices[0].CAD,
 | 
			
		||||
              CHF: conversion.prices[0].CHF, AUD: conversion.prices[0].AUD, JPY: conversion.prices[0].JPY
 | 
			
		||||
            },
 | 
			
		||||
            exchangeRates: conversion.exchangeRates,
 | 
			
		||||
          };
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Query all price history only once. The observable is invalidated after 1 hour
 | 
			
		||||
     */
 | 
			
		||||
    else {
 | 
			
		||||
      if (!this.priceObservable$ || (this.priceObservable$ && (now - this.lastPriceHistoryUpdate > 3600))) {
 | 
			
		||||
        this.priceObservable$ = this.apiService.getHistoricalPrice$(undefined).pipe(shareReplay());
 | 
			
		||||
        this.lastPriceHistoryUpdate = new Date().getTime() / 1000;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return this.priceObservable$.pipe(
 | 
			
		||||
@ -112,4 +146,4 @@ export class PriceService {
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user