From 8f1e60bb6a8bfe3820689a1a37c86294af5b61a8 Mon Sep 17 00:00:00 2001 From: Alexander Drozdov Date: Thu, 17 Dec 2020 00:42:07 +0200 Subject: [PATCH] respect account rules, wait tweak --- src/web/price-check/trade/RateLimiter.ts | 11 ++++++- src/web/price-check/trade/common.ts | 38 ++++++++++++++---------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/web/price-check/trade/RateLimiter.ts b/src/web/price-check/trade/RateLimiter.ts index 7aa70189..35df59ec 100644 --- a/src/web/price-check/trade/RateLimiter.ts +++ b/src/web/price-check/trade/RateLimiter.ts @@ -21,7 +21,7 @@ export class RateLimiter { private async _wait (borrow: boolean, immediate = true): Promise { if (this._destroyed) throw new Error('RateLimiter is no longer active') - if (this.state.stack.length === this.max) { + if (this.state.stack.length >= this.max) { this.state.queue++ await this.state.stack[0] this.state.queue-- @@ -43,6 +43,15 @@ export class RateLimiter { })) } + forceSmallWait () { + this.state.stack.unshift(new Promise((resolve) => { + setTimeout(() => { + this.state.stack.shift() + resolve() + }, 1.5 * 1000) + })) + } + static async waitMulti (limiters: RateLimiter[]): Promise { const res = await Promise.all(limiters.map(rl => rl.wait(false))) if (res.every(immediate => immediate)) { diff --git a/src/web/price-check/trade/common.ts b/src/web/price-check/trade/common.ts index 01c400c9..d74c28bb 100644 --- a/src/web/price-check/trade/common.ts +++ b/src/web/price-check/trade/common.ts @@ -71,28 +71,38 @@ export const RATE_LIMIT_RULES = { ] } -export function adjustRateLimits (clientLimits: RateLimiter[], headers: Headers) { /* eslint-disable no-console */ - const DEBUG = false +export function adjustRateLimits (clientLimits: RateLimiter[], headers: Headers) { + if (!headers.has('x-rate-limit-rules')) return clientLimits + + const rules = headers.get('x-rate-limit-rules')!.split(',') + + return _adjustRateLimits( + clientLimits, + rules.map(rule => headers.get(`x-rate-limit-${rule}`)!).join(','), + rules.map(rule => headers.get(`x-rate-limit-${rule}-state`)!).join(',') + ) +} + +function _adjustRateLimits (clientLimits: RateLimiter[], limitStr: string, stateStr: string) { /* eslint-disable no-console */ + const DEBUG = true const DESYNC_FIX = 1 - if (!headers.has('x-rate-limit-ip') || !headers.get('x-rate-limit-ip-state')) return clientLimits - - const limitIpState = headers.get('x-rate-limit-ip-state')! + const limitRuleState = stateStr .split(',') .map(rule => rule.split(':')) .map(rule => Number(rule[0])) - const limitIp = headers.get('x-rate-limit-ip')! + const limitRule = limitStr .split(',') .map(rule => rule.split(':')) .map((rule, idx) => ({ max: Number(rule[0]), - window: Number(rule[1]) + (DESYNC_FIX + (idx + 1)), - state: limitIpState[idx] + window: Number(rule[1]) + DESYNC_FIX, + state: limitRuleState[idx] })) // destroy for (const limit of clientLimits) { - const isActive = limitIp.some(serverLimit => limit.isEqualLimit(serverLimit)) + const isActive = limitRule.some(serverLimit => limit.isEqualLimit(serverLimit)) if (!isActive) { limit.destroy() DEBUG && console.log('Destroy', limit.toString()) @@ -101,28 +111,26 @@ export function adjustRateLimits (clientLimits: RateLimiter[], headers: Headers) // filter active clientLimits = clientLimits.filter(limit => - limitIp.some(serverLimit => limit.isEqualLimit(serverLimit)) + limitRule.some(serverLimit => limit.isEqualLimit(serverLimit)) ) // compare client<>server state for (const limit of clientLimits) { - const serverLimit = limitIp.find(serverLimit => limit.isEqualLimit(serverLimit))! + const serverLimit = limitRule.find(serverLimit => limit.isEqualLimit(serverLimit))! const delta = (serverLimit.state - limit.state.stack.length) if (delta === 0) { DEBUG && console.log('Limits are in sync') } else if (delta > 0) { DEBUG && console.error(`Rate limit state on Server is greater by ${Math.abs(delta)}. Bursting to prevent rate limiting.`) - Array(delta).fill(undefined).forEach(() => { - limit.wait() - }) + limit.forceSmallWait() } else if (delta < 0) { DEBUG && console.warn(`Rate limit state on Client is greater by ${Math.abs(delta)}`) } } // add new - for (const serverLimit of limitIp) { + for (const serverLimit of limitRule) { if (clientLimits.some(limit => limit.isEqualLimit(serverLimit))) continue const rl = new RateLimiter(serverLimit.max, serverLimit.window)