@bitrix24/b24jssdk
Version:
Bitrix24 REST API JavaScript SDK
1 lines • 11 kB
Source Map (JSON)
{"version":3,"file":"operating-limiter.mjs","sources":["../../../../../src/core/http/limiters/operating-limiter.ts"],"sourcesContent":["import type { OperatingLimitConfig, ILimiter } from '../../../types/limiters'\nimport type { PayloadTime } from '../../../types/payloads'\nimport type { LoggerInterface } from '../../../types/logger'\nimport { LoggerFactory } from '../../../logger'\n\ninterface OperatingStats {\n /*\n * operating time in 10 minutes (in ms)\n */\n operating: number\n /**\n * reset time (timestamp in ms)\n */\n operating_reset_at: number\n lastUpdated: number\n}\n\n/**\n * Operating limiting\n *\n * @todo docs\n */\nexport class OperatingLimiter implements ILimiter {\n #config: OperatingLimitConfig\n #methodStats = new Map<string, OperatingStats>()\n #stats = {\n /** Heavy requests */\n heavyRequestCount: 0\n }\n\n private _logger: LoggerInterface\n\n getTitle(): string {\n return 'operatingLimiter'\n }\n\n constructor(config: OperatingLimitConfig) {\n this._logger = LoggerFactory.createNullLogger()\n this.#config = config\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 get limitMs(): number {\n return this.#config.limitMs\n }\n\n getMethodStat(method: string): undefined | OperatingStats {\n const stats = this.#methodStats.get(method)\n if (!stats) {\n return undefined\n }\n\n return stats\n }\n\n async canProceed(requestId: string, method: string, params?: any): Promise<boolean> {\n const timeToFree = await this.getTimeToFree(requestId, method, params)\n return timeToFree === 0\n }\n\n async waitIfNeeded(requestId: string, method: string, params?: any): Promise<number> {\n return this.getTimeToFree(requestId, method, params)\n }\n\n /**\n * Returns the time until the method's operating limit is released (in ms)\n * The analysis is based on the previous function call.\n * It's important to understand that we're talking about locks of up to 10 minutes.\n * This is a fairly strict lock based on the limit:\n * - not reached - no lock\n * - reached - lock until the unlock time + 1 second\n */\n async getTimeToFree(\n requestId: string,\n method: string,\n params?: any,\n _error?: any\n ): Promise<number> {\n this.#cleanupOldStats()\n\n if (method === 'batch') {\n return this.#getTimeToFreeBatch(requestId, params)\n }\n\n const stats = this.#methodStats.get(method)\n if (!stats) {\n return 0\n }\n\n // Use limit with buffer. When calculating the operating limit, we will take 5 seconds less\n const limitWithBuffer = Math.max(1_000, this.#config.limitMs - 5_000)\n if (stats.operating >= limitWithBuffer) {\n const now = Date.now()\n if (stats.operating_reset_at > now) {\n // Return the time before reset_at + 1 second\n return (stats.operating_reset_at - now) + 1_000\n }\n return 5_000 // 5 seconds by default\n }\n\n return 0\n }\n\n /**\n * For `batch` commands, returns the maximum time until the method reaches the operating limit (in ms)\n */\n async #getTimeToFreeBatch(requestId: string, params: any): Promise<number> {\n let maxWait = 0\n\n if (!params?.cmd || !Array.isArray(params.cmd)) {\n return maxWait\n }\n\n const batchMethods = params.cmd\n .map((row: string) => row.split('?')[0])\n .filter(Boolean)\n\n for (const methodName of batchMethods) {\n const waitTime = await this.getTimeToFree(requestId, `batch::${methodName}`, {})\n maxWait = Math.max(maxWait, waitTime)\n }\n\n return maxWait\n }\n\n /**\n * Updates operating time statistics for the method\n */\n async updateStats(requestId: string, method: string, data: PayloadTime): Promise<void> {\n this.#cleanupOldStats()\n\n // all in seconds\n const { operating, operating_reset_at } = data\n if (operating === undefined || operating === null) {\n return\n }\n\n if (!this.#methodStats.has(method)) {\n this.#methodStats.set(method, {\n operating: 0,\n operating_reset_at: 0,\n lastUpdated: Date.now()\n })\n }\n\n const stats = this.#methodStats.get(method)!\n\n stats.operating = operating * 1000\n stats.operating_reset_at = operating_reset_at * 1000\n stats.lastUpdated = Date.now()\n\n // Check for heavy requests\n const usagePercent = (stats.operating / this.#config.limitMs) * 100\n if (usagePercent > this.#config.heavyPercent) {\n this.#stats.heavyRequestCount++\n\n // log if close to the limit\n this.#logStat(requestId, method, usagePercent, stats.operating)\n }\n }\n\n /**\n * Clearing outdated operating limit data\n */\n #cleanupOldStats(): void {\n const now = Date.now()\n const maxAge = this.#config.windowMs + 10_000 // 10 seconds extra\n\n for (const [method, stats] of this.#methodStats.entries()) {\n if (now - stats.lastUpdated > maxAge) {\n this.#methodStats.delete(method)\n }\n }\n }\n\n async reset(): Promise<void> {\n this.#methodStats.clear()\n this.#stats = {\n heavyRequestCount: 0\n }\n }\n\n getStats(): {\n heavyRequestCount: number\n operatingStats: { [method: string]: number }\n } {\n const operatingStats: Record<string, number> = {}\n\n for (const [method, stats] of this.#methodStats.entries()) {\n operatingStats[method] = Number.parseFloat((stats.operating / 1000).toFixed(2))\n }\n\n return {\n ...this.#stats,\n operatingStats\n }\n }\n\n async setConfig(config: OperatingLimitConfig): Promise<void> {\n this.#config = config\n }\n\n // region Log ////\n #logStat(requestId: string, method: string, percent: number, operating: number) {\n this.getLogger().debug(`${this.getTitle()} detected limit for method ${method}`, {\n requestId,\n method,\n operating: {\n percent: Number.parseFloat(percent.toFixed(2)),\n current: Number.parseFloat((operating / 1000).toFixed(0)),\n max: Number.parseFloat((this.#config.limitMs / 1000).toFixed(0))\n }\n })\n }\n // endregion ////\n}\n"],"names":[],"mappings":"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"}