UNPKG

@bitrix24/b24jssdk

Version:

Bitrix24 REST API JavaScript SDK

172 lines (169 loc) 5.03 kB
/** * @package @bitrix24/b24jssdk * @version 1.1.0 * @copyright (c) 2026 Bitrix24 * @license MIT * @see https://github.com/bitrix24/b24jssdk * @see https://bitrix24.github.io/b24jssdk/ */ import { LoggerFactory } from '../../../logger/logger-factory.mjs'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); class OperatingLimiter { static { __name(this, "OperatingLimiter"); } #config; #methodStats = /* @__PURE__ */ new Map(); #stats = { /** Heavy requests */ heavyRequestCount: 0 }; _logger; getTitle() { return "operatingLimiter"; } constructor(config) { this._logger = LoggerFactory.createNullLogger(); this.#config = config; } // region Logger //// setLogger(logger) { this._logger = logger; } getLogger() { return this._logger; } // endregion //// get limitMs() { return this.#config.limitMs; } getMethodStat(method) { const stats = this.#methodStats.get(method); if (!stats) { return void 0; } return stats; } async canProceed(requestId, method, params) { const timeToFree = await this.getTimeToFree(requestId, method, params); return timeToFree === 0; } async waitIfNeeded(requestId, method, params) { return this.getTimeToFree(requestId, method, params); } /** * Returns the time until the method's operating limit is released (in ms) * The analysis is based on the previous function call. * It's important to understand that we're talking about locks of up to 10 minutes. * This is a fairly strict lock based on the limit: * - not reached - no lock * - reached - lock until the unlock time + 1 second */ async getTimeToFree(requestId, method, params, _error) { this.#cleanupOldStats(); if (method === "batch") { return this.#getTimeToFreeBatch(requestId, params); } const stats = this.#methodStats.get(method); if (!stats) { return 0; } const limitWithBuffer = Math.max(1e3, this.#config.limitMs - 5e3); if (stats.operating >= limitWithBuffer) { const now = Date.now(); if (stats.operating_reset_at > now) { return stats.operating_reset_at - now + 1e3; } return 5e3; } return 0; } /** * For `batch` commands, returns the maximum time until the method reaches the operating limit (in ms) */ async #getTimeToFreeBatch(requestId, params) { let maxWait = 0; if (!params?.cmd || !Array.isArray(params.cmd)) { return maxWait; } const batchMethods = params.cmd.map((row) => row.split("?")[0]).filter(Boolean); for (const methodName of batchMethods) { const waitTime = await this.getTimeToFree(requestId, `batch::${methodName}`, {}); maxWait = Math.max(maxWait, waitTime); } return maxWait; } /** * Updates operating time statistics for the method */ async updateStats(requestId, method, data) { this.#cleanupOldStats(); const { operating, operating_reset_at } = data; if (operating === void 0 || operating === null) { return; } if (!this.#methodStats.has(method)) { this.#methodStats.set(method, { operating: 0, operating_reset_at: 0, lastUpdated: Date.now() }); } const stats = this.#methodStats.get(method); stats.operating = operating * 1e3; stats.operating_reset_at = operating_reset_at * 1e3; stats.lastUpdated = Date.now(); const usagePercent = stats.operating / this.#config.limitMs * 100; if (usagePercent > this.#config.heavyPercent) { this.#stats.heavyRequestCount++; this.#logStat(requestId, method, usagePercent, stats.operating); } } /** * Clearing outdated operating limit data */ #cleanupOldStats() { const now = Date.now(); const maxAge = this.#config.windowMs + 1e4; for (const [method, stats] of this.#methodStats.entries()) { if (now - stats.lastUpdated > maxAge) { this.#methodStats.delete(method); } } } async reset() { this.#methodStats.clear(); this.#stats = { heavyRequestCount: 0 }; } getStats() { const operatingStats = {}; for (const [method, stats] of this.#methodStats.entries()) { operatingStats[method] = Number.parseFloat((stats.operating / 1e3).toFixed(2)); } return { ...this.#stats, operatingStats }; } async setConfig(config) { this.#config = config; } // region Log //// #logStat(requestId, method, percent, operating) { this.getLogger().debug(`${this.getTitle()} detected limit for method ${method}`, { requestId, method, operating: { percent: Number.parseFloat(percent.toFixed(2)), current: Number.parseFloat((operating / 1e3).toFixed(0)), max: Number.parseFloat((this.#config.limitMs / 1e3).toFixed(0)) } }); } // endregion //// } export { OperatingLimiter }; //# sourceMappingURL=operating-limiter.mjs.map