UNPKG

@bitrix24/b24jssdk

Version:

Bitrix24 REST API JavaScript SDK

1 lines 25.8 kB
{"version":3,"file":"rate-limiter.mjs","sources":["../../../../../src/core/http/limiters/rate-limiter.ts"],"sourcesContent":["import type { ILimiter, RateLimitConfig } from '../../../types/limiters'\nimport type { LoggerInterface } from '../../../types/logger'\nimport { LoggerFactory } from '../../../logger'\n\n/**\n * Rate limiting (Leaky Bucket) with adaptive control\n */\nexport class RateLimiter implements ILimiter {\n #tokens: number\n #lastRefill: number\n #refillIntervalMs: number\n #config: RateLimitConfig\n #lockQueue: Array<() => void> = []\n\n #originalConfig: RateLimitConfig // Original configuration for recovery\n #errorThreshold: number = 5 // 60-second error threshold to reduce limits\n #successThreshold: number = 20 // Consecutive success threshold for restoring limits\n #minDrainRate: number = 0.5 // Minimum drain rate\n #minBurstLimit: number = 5 // Minimum burst limit\n #errorTimestamps: number[] = [] // Error timestamps (last 60 seconds)\n #successTimestamps: number[] = [] // Timestamps of successful requests\n\n private _logger: LoggerInterface\n\n constructor(config: RateLimitConfig) {\n this._logger = LoggerFactory.createNullLogger()\n this.#config = config\n this.#originalConfig = { ...config }\n this.#tokens = config.burstLimit\n this.#lastRefill = Date.now()\n this.#refillIntervalMs = 1000 / config.drainRate\n }\n\n getTitle(): string {\n return 'rateLimiter'\n }\n\n // region Logger ////\n setLogger(logger: LoggerInterface): void {\n this._logger = logger\n }\n\n getLogger(): LoggerInterface {\n return this._logger\n }\n // endregion ////\n\n /**\n * @inheritDoc\n */\n async canProceed(requestId: string, _method: string, _params?: any): Promise<boolean> {\n await this.#acquireLock(requestId)\n try {\n const now = Date.now()\n const timePassed = now - this.#lastRefill\n\n // Refill tokens\n const refillAmount = timePassed * this.#config.drainRate / 1000\n this.#tokens = Math.min(\n this.#config.burstLimit,\n this.#tokens + refillAmount\n )\n this.#lastRefill = now\n\n return this.#tokens >= 1\n } finally {\n this.#releaseLock()\n }\n }\n\n /**\n * @inheritDoc\n */\n async waitIfNeeded(requestId: string, _method: string, _params?: any): Promise<number> {\n await this.#acquireLock(requestId)\n\n try {\n const now = Date.now()\n const timePassed = now - this.#lastRefill\n\n // Replenishing tokens\n const refillAmount = timePassed * this.#config.drainRate / 1_000\n this.#tokens = Math.min(\n this.#config.burstLimit,\n this.#tokens + refillAmount\n )\n\n // We always update the time of the last replenishment\n this.#lastRefill = now\n\n // If there are enough tokens\n if (this.#tokens >= 1) {\n // Consume token\n this.#tokens -= 1\n return 0\n }\n\n // Calculating the waiting time for 1 token\n const deficit = 1 - this.#tokens\n return Math.ceil(deficit * this.#refillIntervalMs)\n } finally {\n this.#releaseLock()\n }\n }\n\n /**\n * Error handler.\n * If there are a lot of errors, we'll lower the limits.\n */\n async handleExceeded(requestId: string): Promise<number> {\n await this.#acquireLock(requestId)\n\n try {\n this.#recordError()\n\n // Adaptive regulation: if there are many errors, we reduce the limits\n if (this.#config.adaptiveEnabled && this.#shouldReduceLimits()) {\n this.#reduceLimits(requestId)\n }\n\n this.#tokens = 0\n // Wait for the time to restore at least one token + 1sec\n return this.#refillIntervalMs + 1_000\n } finally {\n this.#releaseLock()\n }\n }\n\n /**\n * Successful request handler.\n * If everything is OK, we'll restore the limits.\n */\n async updateStats(requestId: string, method: string, _data: any): Promise<void> {\n // skip accounting of `batch` subqueries\n if (method.startsWith('batch::')) {\n return\n }\n\n await this.#acquireLock(requestId)\n\n try {\n this.#recordSuccess()\n\n // Adaptive regulation: if we operate stably, we restore the limits\n if (this.#config.adaptiveEnabled) {\n this.#logStat(requestId)\n }\n\n if (this.#config.adaptiveEnabled && this.#shouldRestoreLimits()) {\n this.#restoreLimits(requestId)\n }\n } finally {\n this.#releaseLock()\n }\n }\n\n /**\n * @inheritDoc\n */\n async reset(): Promise<void> {\n await this.#acquireLock('reset')\n\n try {\n this.#tokens = this.#config.burstLimit\n this.#lastRefill = Date.now()\n this.#errorTimestamps = []\n this.#successTimestamps = []\n\n // Restore original settings during reset\n this.#config.drainRate = this.#originalConfig.drainRate\n this.#config.burstLimit = this.#originalConfig.burstLimit\n this.#refillIntervalMs = 1000 / this.#config.drainRate\n } finally {\n this.#releaseLock()\n }\n }\n\n /**\n * @inheritDoc\n */\n getStats() {\n return {\n tokens: this.#tokens,\n burstLimit: this.#config.burstLimit,\n originalBurstLimit: this.#originalConfig.burstLimit,\n drainRate: this.#config.drainRate,\n originalDrainRate: this.#originalConfig.drainRate,\n refillIntervalMs: this.#refillIntervalMs,\n lastRefill: this.#lastRefill,\n pendingRequests: this.#lockQueue.length,\n recentErrors: this.#errorTimestamps.length,\n recentSuccesses: this.#successTimestamps.length\n }\n }\n\n /**\n * @inheritDoc\n */\n async setConfig(config: RateLimitConfig): Promise<void> {\n await this.#acquireLock('setConfig')\n\n try {\n this.#config = config\n this.#originalConfig = { ...config }\n this.#refillIntervalMs = 1000 / this.#config.drainRate\n\n // If the new configuration increases burstLimit, we can increase the current number of tokens\n if (config.burstLimit > this.#tokens) {\n this.#tokens = Math.min(config.burstLimit, this.#tokens)\n }\n\n // Reset statistics when changing configuration\n this.#errorTimestamps = []\n this.#successTimestamps = []\n } finally {\n this.#releaseLock()\n }\n }\n\n /**\n * Acquire a lock for the critical section\n * Uses a promise queue\n */\n async #acquireLock(requestId: string): Promise<void> {\n return new Promise<void>((resolve) => {\n // Add the resolution function to the queue\n const queueLength = this.#lockQueue.push(resolve)\n\n if (queueLength > 1) {\n this.#logAcquireQueue(requestId, queueLength)\n }\n // If it's the first one in the queue, we allow it immediately\n if (this.#lockQueue.length === 1) {\n resolve()\n }\n })\n }\n\n /**\n * Releases the lock and allows the next person in the queue to proceed\n */\n #releaseLock(): void {\n // Remove the current resolver from the front of the queue\n this.#lockQueue.shift()\n\n // If there are any waiting, resolve the next one\n if (this.#lockQueue.length > 0) {\n const nextResolve = this.#lockQueue[0]!\n nextResolve()\n }\n }\n\n /**\n * Checks whether the limits need to be reduced\n */\n #shouldReduceLimits(): boolean {\n // If there are more errors than the threshold in the last 60 seconds\n return this.#errorTimestamps.length >= this.#errorThreshold\n }\n\n /**\n * Checks whether limits need to be restored\n * Restore if:\n * 1. Many successful requests (more than the threshold)\n * 2. Few errors (less than half the threshold)\n * 3. Current limits are lower than the original ones\n */\n #shouldRestoreLimits(): boolean {\n return this.#successTimestamps.length >= this.#successThreshold\n && this.#errorTimestamps.length < (this.#errorThreshold / 2)\n && (\n this.#config.drainRate < this.#originalConfig.drainRate\n || this.#config.burstLimit < this.#originalConfig.burstLimit\n )\n }\n\n /**\n * Reduces limits for frequent errors\n */\n #reduceLimits(requestId: string): void {\n // Reduce drainRate by 20%, but not below the minimum\n const newDrainRate = Math.max(\n this.#minDrainRate,\n Number.parseFloat((this.#config.drainRate * 0.8).toFixed(2))\n )\n\n // Reduce burstLimit by 20%, but not below the minimum\n const newBurstLimit = Math.max(\n this.#minBurstLimit,\n Number.parseFloat((this.#config.burstLimit * 0.8).toFixed(2))\n )\n\n // Applying new limits\n this.#config.drainRate = newDrainRate\n this.#config.burstLimit = newBurstLimit\n this.#refillIntervalMs = 1000 / newDrainRate\n\n this.#logReduceLimits(requestId, newDrainRate, newBurstLimit)\n\n // Reset error statistics after reduction\n this.#errorTimestamps = []\n this.#successTimestamps = []\n }\n\n /**\n * Restores limits during stable operation\n */\n #restoreLimits(requestId: string): void {\n if (\n this.#config.drainRate === this.#originalConfig.drainRate\n && this.#config.burstLimit === this.#originalConfig.burstLimit\n ) {\n return\n }\n\n // Restore drainRate to 10% of its original value\n const newDrainRate = Math.min(\n this.#originalConfig.drainRate,\n Number.parseFloat((this.#config.drainRate * 1.1).toFixed(2))\n )\n\n // Restore burstLimit to 10% of its original value\n const newBurstLimit = Math.min(\n this.#originalConfig.burstLimit,\n Number.parseFloat((this.#config.burstLimit * 1.1).toFixed(2))\n )\n\n // Applying new limits\n this.#config.drainRate = newDrainRate\n this.#config.burstLimit = newBurstLimit\n this.#refillIntervalMs = 1000 / newDrainRate\n\n this.#logRestoreLimits(requestId, newDrainRate, newBurstLimit)\n\n // Reset success statistics after recovery\n this.#errorTimestamps = []\n this.#successTimestamps = []\n }\n\n /**\n * Writes an error to the temporary history\n */\n #recordError(): void {\n const now = Date.now()\n this.#errorTimestamps.push(now)\n\n // Clear ALL progress\n this.#successTimestamps = []\n this.#cleanupOldErrors(now)\n }\n\n /**\n * Clears old errors (older than 60 seconds)\n */\n #cleanupOldErrors(now: number): void {\n const cutoff = now - 60_000\n this.#errorTimestamps = this.#errorTimestamps.filter(timestamp => timestamp > cutoff)\n }\n\n /**\n * Writes a successful request to the temporary history\n */\n #recordSuccess(): void {\n const now = Date.now()\n this.#successTimestamps.push(now)\n\n this.#cleanupOldSuccesses()\n this.#cleanupOldErrors(now)\n }\n\n /**\n * Clears old progress\n */\n #cleanupOldSuccesses(): void {\n this.#successTimestamps = this.#successTimestamps.slice(-1 * this.#successThreshold)\n }\n\n // region Log ////\n #logReduceLimits(requestId: string, currentDrainRate: number, currentBurstLimit: number) {\n const originalDrainRate = this.#originalConfig.drainRate\n const drainRateCondition = currentDrainRate < originalDrainRate\n\n const originalBurstLimit = this.#originalConfig.burstLimit\n const burstLimitCondition = currentBurstLimit < originalBurstLimit\n\n this.getLogger().warning(\n `${this.getTitle()} is lowering limits due to frequent errors`, {\n requestId,\n drainRate: {\n current: currentDrainRate,\n original: originalDrainRate,\n condition: drainRateCondition,\n formatted: `(${currentDrainRate} < ${originalDrainRate}) ${drainRateCondition}`\n },\n burstLimit: {\n current: currentBurstLimit,\n original: originalBurstLimit,\n condition: burstLimitCondition,\n formatted: `(${currentBurstLimit} < ${originalBurstLimit}) ${burstLimitCondition}`\n }\n }\n )\n }\n\n #logRestoreLimits(requestId: string, currentDrainRate: number, currentBurstLimit: number) {\n const originalDrainRate = this.#originalConfig.drainRate\n const drainRateCondition = currentDrainRate < originalDrainRate\n\n const originalBurstLimit = this.#originalConfig.burstLimit\n const burstLimitCondition = currentBurstLimit < originalBurstLimit\n\n this.getLogger().warning(\n `${this.getTitle()} increases limits during stable operation`, {\n requestId,\n drainRate: {\n current: currentDrainRate,\n original: originalDrainRate,\n condition: drainRateCondition,\n formatted: `(${currentDrainRate} < ${originalDrainRate}) ${drainRateCondition}`\n },\n burstLimit: {\n current: currentBurstLimit,\n original: originalBurstLimit,\n condition: burstLimitCondition,\n formatted: `(${currentBurstLimit} < ${originalBurstLimit}) ${burstLimitCondition}`\n }\n }\n )\n }\n\n #logAcquireQueue(requestId: string, queueLength: number) {\n this.getLogger().debug(`${this.getTitle()} request in queue`, {\n requestId,\n queueLength\n })\n }\n\n #logStat(\n requestId: string\n ): void {\n const successCount = this.#successTimestamps.length\n const successThreshold = this.#successThreshold\n const successCondition = successCount >= successThreshold\n\n const errorCount = this.#errorTimestamps.length\n const errorThreshold = this.#errorThreshold\n const failCondition = errorCount < (errorThreshold / 2)\n\n const currentDrainRate = this.#config.drainRate\n const originalDrainRate = this.#originalConfig.drainRate\n const drainRateCondition = currentDrainRate < originalDrainRate\n\n const currentBurstLimit = this.#config.burstLimit\n const originalBurstLimit = this.#originalConfig.burstLimit\n const burstLimitCondition = currentBurstLimit < originalBurstLimit\n\n this.getLogger().debug(`${this.getTitle()} state`, {\n requestId,\n success: {\n count: successCount,\n threshold: successThreshold,\n condition: successCondition,\n formatted: `(${successCount} >= ${successThreshold}) ${successCondition}`\n },\n fail: {\n count: errorCount,\n threshold: errorThreshold / 2,\n condition: failCondition,\n formatted: `(${errorCount} < ${errorThreshold / 2}) ${failCondition}`\n },\n drainRate: {\n current: currentDrainRate,\n original: originalDrainRate,\n condition: drainRateCondition,\n formatted: `(${currentDrainRate} < ${originalDrainRate}) ${drainRateCondition}`\n },\n burstLimit: {\n current: currentBurstLimit,\n original: originalBurstLimit,\n condition: burstLimitCondition,\n formatted: `(${currentBurstLimit} < ${originalBurstLimit}) ${burstLimitCondition}`\n }\n })\n }\n // endregion ////\n}\n"],"names":[],"mappings":"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"}