Merge pull request #5134 from mempool/natsoni/improve-conversions-price-service
Improve price conversions fetching from free API
This commit is contained in:
commit
82e2f46eba
@ -76,7 +76,7 @@ class FreeCurrencyApi implements ConversionFeed {
|
||||
}
|
||||
|
||||
public async $fetchConversionRates(date: string): Promise<ConversionRates> {
|
||||
const response = await query(`${this.API_URL_PREFIX}historical?date=${date}&apikey=${this.API_KEY}`);
|
||||
const response = await query(`${this.API_URL_PREFIX}historical?date=${date}&apikey=${this.API_KEY}`, true);
|
||||
if (response && response['data'] && (response['data'][date] || this.PAID)) {
|
||||
if (this.PAID) {
|
||||
response['data'] = this.convertData(response['data']);
|
||||
|
@ -59,7 +59,7 @@ class PriceUpdater {
|
||||
private currencyConversionFeed: ConversionFeed | undefined;
|
||||
private newCurrencies: string[] = ['BGN', 'BRL', 'CNY', 'CZK', 'DKK', 'HKD', 'HRK', 'HUF', 'IDR', 'ILS', 'INR', 'ISK', 'KRW', 'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN', 'RON', 'RUB', 'SEK', 'SGD', 'THB', 'TRY', 'ZAR'];
|
||||
private lastTimeConversionsRatesFetched: number = 0;
|
||||
private latestConversionsRatesFromFeed: ConversionRates = {};
|
||||
private latestConversionsRatesFromFeed: ConversionRates = { USD: -1 };
|
||||
private ratesChangedCallback: ((rates: ApiPrice) => void) | undefined;
|
||||
|
||||
constructor() {
|
||||
@ -157,9 +157,9 @@ class PriceUpdater {
|
||||
try {
|
||||
this.latestConversionsRatesFromFeed = await this.currencyConversionFeed.$fetchLatestConversionRates();
|
||||
this.lastTimeConversionsRatesFetched = Math.round(new Date().getTime() / 1000);
|
||||
logger.debug(`Fetched currencies conversion rates from external API: ${JSON.stringify(this.latestConversionsRatesFromFeed)}`);
|
||||
logger.debug(`Fetched currencies conversion rates from conversions API: ${JSON.stringify(this.latestConversionsRatesFromFeed)}`);
|
||||
} catch (e) {
|
||||
logger.err(`Cannot fetch conversion rates from the API. Reason: ${(e instanceof Error ? e.message : e)}`);
|
||||
logger.err(`Cannot fetch conversion rates from conversions API. Reason: ${(e instanceof Error ? e.message : e)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,17 +408,17 @@ class PriceUpdater {
|
||||
try {
|
||||
const remainingQuota = await this.currencyConversionFeed?.$getQuota();
|
||||
if (remainingQuota['month']['remaining'] < 500) { // We need some calls left for the daily updates
|
||||
logger.debug(`Not enough currency API credit to insert missing prices in ${priceTimesToFill.length} rows (${remainingQuota['month']['remaining']} calls left).`, logger.tags.mining);
|
||||
logger.debug(`Not enough conversions API credit to insert missing prices in ${priceTimesToFill.length} rows (${remainingQuota['month']['remaining']} calls left).`, logger.tags.mining);
|
||||
this.additionalCurrenciesHistoryInserted = true; // Do not try again until next day
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.err(`Cannot fetch currency API credit, insertion of missing prices aborted. Reason: ${(e instanceof Error ? e.message : e)}`);
|
||||
logger.err(`Cannot fetch conversions API credit, insertion of missing prices aborted. Reason: ${(e instanceof Error ? e.message : e)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.additionalCurrenciesHistoryRunning = true;
|
||||
logger.debug(`Fetching missing conversion rates from external API to fill ${priceTimesToFill.length} rows`, logger.tags.mining);
|
||||
logger.debug(`Inserting missing historical conversion rates using conversions API to fill ${priceTimesToFill.length} rows`, logger.tags.mining);
|
||||
|
||||
let conversionRates: { [timestamp: number]: ConversionRates } = {};
|
||||
let totalInserted = 0;
|
||||
@ -430,10 +430,23 @@ class PriceUpdater {
|
||||
const month = new Date(priceTime.time * 1000).getMonth();
|
||||
const yearMonthTimestamp = new Date(year, month, 1).getTime() / 1000;
|
||||
if (conversionRates[yearMonthTimestamp] === undefined) {
|
||||
conversionRates[yearMonthTimestamp] = await this.currencyConversionFeed?.$fetchConversionRates(`${year}-${month + 1 < 10 ? `0${month + 1}` : `${month + 1}`}-01`) || { USD: -1 };
|
||||
if (conversionRates[yearMonthTimestamp]['USD'] < 0) {
|
||||
logger.err(`Cannot fetch conversion rates from the API for ${year}-${month + 1 < 10 ? `0${month + 1}` : `${month + 1}`}-01. Aborting insertion of missing prices.`, logger.tags.mining);
|
||||
this.lastFailedHistoricalRun = Math.round(new Date().getTime() / 1000);
|
||||
try {
|
||||
if (year === new Date().getFullYear() && month === new Date().getMonth()) { // For rows in the current month, we use the latest conversion rates
|
||||
conversionRates[yearMonthTimestamp] = this.latestConversionsRatesFromFeed;
|
||||
} else {
|
||||
conversionRates[yearMonthTimestamp] = await this.currencyConversionFeed?.$fetchConversionRates(`${year}-${month + 1 < 10 ? `0${month + 1}` : `${month + 1}`}-15`) || { USD: -1 };
|
||||
}
|
||||
|
||||
if (conversionRates[yearMonthTimestamp]['USD'] < 0) {
|
||||
throw new Error('Incorrect USD conversion rate');
|
||||
}
|
||||
} catch (e) {
|
||||
if ((e instanceof Error ? e.message : '').includes('429')) { // Continue 60 seconds later if and only if error is 429
|
||||
this.lastFailedHistoricalRun = Math.round(new Date().getTime() / 1000);
|
||||
logger.info(`Got a 429 error from conversions API. This is expected to happen a few times during the initial historical price insertion, process will resume in 60 seconds.`, logger.tags.mining);
|
||||
} else {
|
||||
logger.err(`Cannot fetch conversion rates from conversions API for ${year}-${month + 1 < 10 ? `0${month + 1}` : `${month + 1}`}-01, trying again next day. Error: ${(e instanceof Error ? e.message : e)}`, logger.tags.mining);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import config from '../config';
|
||||
import logger from '../logger';
|
||||
import * as https from 'https';
|
||||
|
||||
export async function query(path): Promise<object | undefined> {
|
||||
export async function query(path, throwOnFail: boolean = false): Promise<object | undefined> {
|
||||
type axiosOptions = {
|
||||
headers: {
|
||||
'User-Agent': string
|
||||
@ -21,6 +21,7 @@ export async function query(path): Promise<object | undefined> {
|
||||
timeout: config.SOCKS5PROXY.ENABLED ? 30000 : 10000
|
||||
};
|
||||
let retry = 0;
|
||||
let lastError: any = null;
|
||||
|
||||
while (retry < config.MEMPOOL.EXTERNAL_MAX_RETRY) {
|
||||
try {
|
||||
@ -50,6 +51,7 @@ export async function query(path): Promise<object | undefined> {
|
||||
}
|
||||
return data.data;
|
||||
} catch (e) {
|
||||
lastError = e;
|
||||
logger.warn(`Could not connect to ${path} (Attempt ${retry + 1}/${config.MEMPOOL.EXTERNAL_MAX_RETRY}). Reason: ` + (e instanceof Error ? e.message : e));
|
||||
retry++;
|
||||
}
|
||||
@ -59,5 +61,10 @@ export async function query(path): Promise<object | undefined> {
|
||||
}
|
||||
|
||||
logger.err(`Could not connect to ${path}. All ${config.MEMPOOL.EXTERNAL_MAX_RETRY} attempts failed`);
|
||||
|
||||
if (throwOnFail && lastError) {
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user