Merge pull request #2714 from 0xProject/feat/asset-swapper/rfq-maker-blacklist-logging

asset-swapper: log RFQ maker (un)blacklistings
This commit is contained in:
F. Eugene Aumson 2020-10-02 15:52:23 -04:00 committed by GitHub
commit a35d1b8a9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 17 deletions

View File

@ -1,9 +1,10 @@
import { BigNumber } from '@0x/utils'; import { BigNumber, logUtils } from '@0x/utils';
import { import {
ExchangeProxyContractOpts, ExchangeProxyContractOpts,
ExtensionContractType, ExtensionContractType,
ForwarderExtensionContractOpts, ForwarderExtensionContractOpts,
LogFunction,
OrderPrunerOpts, OrderPrunerOpts,
OrderPrunerPermittedFeeTypes, OrderPrunerPermittedFeeTypes,
RfqtRequestOpts, RfqtRequestOpts,
@ -89,6 +90,11 @@ const DEFAULT_RFQT_REQUEST_OPTS: Partial<RfqtRequestOpts> = {
makerEndpointMaxResponseTimeMs: 1000, makerEndpointMaxResponseTimeMs: 1000,
}; };
export const DEFAULT_INFO_LOGGER: LogFunction = (obj, msg) =>
logUtils.log(`${msg ? `${msg}: ` : ''}${JSON.stringify(obj)}`);
export const DEFAULT_WARNING_LOGGER: LogFunction = (obj, msg) =>
logUtils.warn(`${msg ? `${msg}: ` : ''}${JSON.stringify(obj)}`);
export const constants = { export const constants = {
ETH_GAS_STATION_API_URL, ETH_GAS_STATION_API_URL,
PROTOCOL_FEE_MULTIPLIER, PROTOCOL_FEE_MULTIPLIER,
@ -113,4 +119,6 @@ export const constants = {
PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE, MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE,
BRIDGE_ASSET_DATA_PREFIX: '0xdc1600f3', BRIDGE_ASSET_DATA_PREFIX: '0xdc1600f3',
DEFAULT_INFO_LOGGER,
DEFAULT_WARNING_LOGGER,
}; };

View File

@ -9,7 +9,6 @@ import {
TokenAdjacencyGraph, TokenAdjacencyGraph,
} from './utils/market_operation_utils/types'; } from './utils/market_operation_utils/types';
import { QuoteReport } from './utils/quote_report_generator'; import { QuoteReport } from './utils/quote_report_generator';
import { LogFunction } from './utils/quote_requestor';
/** /**
* expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m). * expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
@ -273,7 +272,7 @@ export interface RfqtMakerAssetOfferings {
[endpoint: string]: Array<[string, string]>; [endpoint: string]: Array<[string, string]>;
} }
export { LogFunction } from './utils/quote_requestor'; export type LogFunction = (obj: object, msg?: string, ...args: any[]) => void;
export interface SwapQuoterRfqtOpts { export interface SwapQuoterRfqtOpts {
takerApiKeyWhitelist: string[]; takerApiKeyWhitelist: string[];

View File

@ -2,13 +2,13 @@ import { schemas, SchemaValidator } from '@0x/json-schemas';
import { assetDataUtils, orderCalculationUtils, SignedOrder } from '@0x/order-utils'; import { assetDataUtils, orderCalculationUtils, SignedOrder } from '@0x/order-utils';
import { RFQTFirmQuote, RFQTIndicativeQuote, TakerRequest } from '@0x/quote-server'; import { RFQTFirmQuote, RFQTIndicativeQuote, TakerRequest } from '@0x/quote-server';
import { ERC20AssetData } from '@0x/types'; import { ERC20AssetData } from '@0x/types';
import { BigNumber, logUtils } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import Axios, { AxiosInstance } from 'axios'; import Axios, { AxiosInstance } from 'axios';
import { Agent as HttpAgent } from 'http'; import { Agent as HttpAgent } from 'http';
import { Agent as HttpsAgent } from 'https'; import { Agent as HttpsAgent } from 'https';
import { constants } from '../constants'; import { constants } from '../constants';
import { MarketOperation, RfqtMakerAssetOfferings, RfqtRequestOpts } from '../types'; import { LogFunction, MarketOperation, RfqtMakerAssetOfferings, RfqtRequestOpts } from '../types';
import { ONE_SECOND_MS } from './market_operation_utils/constants'; import { ONE_SECOND_MS } from './market_operation_utils/constants';
import { RfqMakerBlacklist } from './rfq_maker_blacklist'; import { RfqMakerBlacklist } from './rfq_maker_blacklist';
@ -107,20 +107,18 @@ function convertIfAxiosError(error: any): Error | object /* axios' .d.ts has Axi
} }
} }
export type LogFunction = (obj: object, msg?: string, ...args: any[]) => void;
export class QuoteRequestor { export class QuoteRequestor {
private readonly _schemaValidator: SchemaValidator = new SchemaValidator(); private readonly _schemaValidator: SchemaValidator = new SchemaValidator();
private readonly _orderSignatureToMakerUri: { [orderSignature: string]: string } = {}; private readonly _orderSignatureToMakerUri: { [orderSignature: string]: string } = {};
constructor( constructor(
private readonly _rfqtAssetOfferings: RfqtMakerAssetOfferings, private readonly _rfqtAssetOfferings: RfqtMakerAssetOfferings,
private readonly _warningLogger: LogFunction = (obj, msg) => private readonly _warningLogger: LogFunction = constants.DEFAULT_WARNING_LOGGER,
logUtils.warn(`${msg ? `${msg}: ` : ''}${JSON.stringify(obj)}`), private readonly _infoLogger: LogFunction = constants.DEFAULT_INFO_LOGGER,
private readonly _infoLogger: LogFunction = (obj, msg) =>
logUtils.log(`${msg ? `${msg}: ` : ''}${JSON.stringify(obj)}`),
private readonly _expiryBufferMs: number = constants.DEFAULT_SWAP_QUOTER_OPTS.expiryBufferMs, private readonly _expiryBufferMs: number = constants.DEFAULT_SWAP_QUOTER_OPTS.expiryBufferMs,
) {} ) {
rfqMakerBlacklist.infoLogger = this._infoLogger;
}
public async requestRfqtFirmQuotesAsync( public async requestRfqtFirmQuotesAsync(
makerAssetData: string, makerAssetData: string,
@ -395,7 +393,7 @@ export class QuoteRequestor {
}, },
}, },
}); });
rfqMakerBlacklist.logTimeoutOrLackThereof(url, latencyMs > maxResponseTimeMs); rfqMakerBlacklist.logTimeoutOrLackThereof(url, latencyMs >= maxResponseTimeMs);
result.push({ response: response.data, makerUri: url }); result.push({ response: response.data, makerUri: url });
} catch (err) { } catch (err) {
const latencyMs = Date.now() - timeBeforeAwait; const latencyMs = Date.now() - timeBeforeAwait;
@ -411,7 +409,7 @@ export class QuoteRequestor {
}, },
}, },
}); });
rfqMakerBlacklist.logTimeoutOrLackThereof(url, latencyMs > maxResponseTimeMs); rfqMakerBlacklist.logTimeoutOrLackThereof(url, latencyMs >= maxResponseTimeMs);
this._warningLogger( this._warningLogger(
convertIfAxiosError(err), convertIfAxiosError(err),
`Failed to get RFQ-T ${quoteType} quote from market maker endpoint ${url} for API key ${ `Failed to get RFQ-T ${quoteType} quote from market maker endpoint ${url} for API key ${

View File

@ -4,11 +4,16 @@
*/ */
import { constants } from '../constants'; import { constants } from '../constants';
import { LogFunction } from '../types';
export class RfqMakerBlacklist { export class RfqMakerBlacklist {
private readonly _makerTimeoutStreakLength: { [makerUrl: string]: number } = {}; private readonly _makerTimeoutStreakLength: { [makerUrl: string]: number } = {};
private readonly _makerBlacklistedUntilDate: { [makerUrl: string]: number } = {}; private readonly _makerBlacklistedUntilDate: { [makerUrl: string]: number } = {};
constructor(private readonly _blacklistDurationMinutes: number, private readonly _timeoutStreakThreshold: number) {} constructor(
private readonly _blacklistDurationMinutes: number,
private readonly _timeoutStreakThreshold: number,
public infoLogger: LogFunction = constants.DEFAULT_INFO_LOGGER,
) {}
public logTimeoutOrLackThereof(makerUrl: string, didTimeout: boolean): void { public logTimeoutOrLackThereof(makerUrl: string, didTimeout: boolean): void {
if (!this._makerTimeoutStreakLength.hasOwnProperty(makerUrl)) { if (!this._makerTimeoutStreakLength.hasOwnProperty(makerUrl)) {
this._makerTimeoutStreakLength[makerUrl] = 0; this._makerTimeoutStreakLength[makerUrl] = 0;
@ -16,8 +21,12 @@ export class RfqMakerBlacklist {
if (didTimeout) { if (didTimeout) {
this._makerTimeoutStreakLength[makerUrl] += 1; this._makerTimeoutStreakLength[makerUrl] += 1;
if (this._makerTimeoutStreakLength[makerUrl] === this._timeoutStreakThreshold) { if (this._makerTimeoutStreakLength[makerUrl] === this._timeoutStreakThreshold) {
this._makerBlacklistedUntilDate[makerUrl] = const blacklistEnd = Date.now() + this._blacklistDurationMinutes * constants.ONE_MINUTE_MS;
Date.now() + this._blacklistDurationMinutes * constants.ONE_MINUTE_MS; this._makerBlacklistedUntilDate[makerUrl] = blacklistEnd;
this.infoLogger(
{ makerUrl, blacklistedUntil: new Date(blacklistEnd).toISOString() },
'maker blacklisted',
);
} }
} else { } else {
this._makerTimeoutStreakLength[makerUrl] = 0; this._makerTimeoutStreakLength[makerUrl] = 0;
@ -27,6 +36,7 @@ export class RfqMakerBlacklist {
const now = Date.now(); const now = Date.now();
if (now > this._makerBlacklistedUntilDate[makerUrl]) { if (now > this._makerBlacklistedUntilDate[makerUrl]) {
delete this._makerBlacklistedUntilDate[makerUrl]; delete this._makerBlacklistedUntilDate[makerUrl];
this.infoLogger({ makerUrl }, 'maker unblacklisted');
} }
return this._makerBlacklistedUntilDate[makerUrl] > now; return this._makerBlacklistedUntilDate[makerUrl] > now;
} }