{"version":3,"file":"adaptive-delayer.mjs","sources":["../../../../../src/core/http/limiters/adaptive-delayer.ts"],"sourcesContent":["import type { AdaptiveConfig, ILimiter } from '../../../types/limiters'\nimport type { OperatingLimiter } from './operating-limiter'\nimport type { LoggerInterface } from '../../../types/logger'\nimport { LoggerFactory } from '../../../logger'\n\n/**\n * Adaptive delayer\n *\n * @todo docs\n */\nexport class AdaptiveDelayer implements ILimiter {\n  #config: AdaptiveConfig\n  #operatingLimiter: OperatingLimiter\n  #stats = {\n    adaptiveDelays: 0,\n    totalAdaptiveDelay: 0\n  }\n\n  private _logger: LoggerInterface\n\n  getTitle(): string {\n    return 'adaptiveDelayer'\n  }\n\n  constructor(config: AdaptiveConfig, operatingLimiter: OperatingLimiter) {\n    this._logger = LoggerFactory.createNullLogger()\n    this.#config = config\n    this.#operatingLimiter = operatingLimiter\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  async canProceed(_requestId: string, _method: string, _params?: any): Promise<boolean> {\n    return true // Adaptive delay doesn't block, only delays\n  }\n\n  /**\n   * Returns an adaptive delay based on previous experience\n   */\n  async waitIfNeeded(requestId: string, method: string, params?: any): Promise<number> {\n    if (!this.#config.enabled) {\n      return 0\n    }\n\n    const delay = this.#calculateDelay(requestId, method, params)\n    if (delay > 0) {\n      this.incrementAdaptiveDelays()\n      this.#stats.totalAdaptiveDelay += delay\n    }\n\n    return delay\n  }\n\n  /**\n   * Calculates adaptive delay based on previous experience\n   */\n  #calculateDelay(requestId: string, method: string, params?: any): number {\n    if (method === 'batch') {\n      return this.#calculateBatchDelay(requestId, params)\n    }\n\n    const stats = this.#operatingLimiter.getMethodStat(method)\n\n    if (typeof stats === 'undefined') {\n      return 0\n    }\n\n    const usagePercent = (stats.operating / this.#operatingLimiter.limitMs) * 100\n\n    if (usagePercent > this.#config.thresholdPercent) {\n      let adaptiveDelay = 0\n\n      // Calculate based on previous delay or default\n      const now = Date.now()\n      if (stats.operating_reset_at > now) {\n        adaptiveDelay += (stats.operating_reset_at - now) * this.#config.coefficient\n      } else {\n        adaptiveDelay += 7_000 // 7 seconds by default\n      }\n\n      const waitDelay = Number.parseInt(Math.min(adaptiveDelay, this.#config.maxDelay).toFixed(0))\n\n      this.#logStat(requestId, method, usagePercent, adaptiveDelay, waitDelay)\n      return waitDelay\n    }\n\n    return 0\n  }\n\n  /**\n   * For `batch`, applies adaptive delay based on previous experience from commands\n   */\n  #calculateBatchDelay(requestId: string, params: any): number {\n    let maxDelay = 0\n\n    if (!params?.cmd || !Array.isArray(params.cmd)) {\n      return maxDelay\n    }\n\n    const batchMethods = params.cmd\n      .map((row: string) => row.split('?')[0])\n      .filter(Boolean)\n\n    const batchMethodsUnique = [...new Set(batchMethods)]\n\n    for (const methodName of batchMethodsUnique) {\n      const delay = this.#calculateDelay(requestId, `batch::${methodName}`, {})\n      maxDelay = Math.max(maxDelay, delay)\n    }\n\n    return maxDelay\n  }\n\n  async updateStats(_requestId: string, _method: string, _data: any): Promise<void> {\n    // Adaptive delayer updates based on operating limiter\n  }\n\n  async reset(): Promise<void> {\n    this.#stats = {\n      adaptiveDelays: 0,\n      totalAdaptiveDelay: 0\n    }\n  }\n\n  getStats(): {\n    adaptiveDelays: number\n    totalAdaptiveDelay: number\n    adaptiveDelayAvg: number\n  } {\n    return {\n      ...this.#stats,\n      adaptiveDelayAvg: this.#stats.adaptiveDelays > 0\n        ? this.#stats.totalAdaptiveDelay / this.#stats.adaptiveDelays\n        : 0\n    }\n  }\n\n  async setConfig(config: AdaptiveConfig): Promise<void> {\n    this.#config = config\n  }\n\n  incrementAdaptiveDelays(): void {\n    this.#stats.adaptiveDelays++\n  }\n\n  // region Log ////\n  #logStat(requestId: string, method: string, percent: number, adaptiveDelay: number, waitDelay: number) {\n    this.getLogger().debug(`${this.getTitle()} state for method ${method}`, {\n      requestId,\n      method,\n      percent: Number.parseFloat(percent.toFixed(2)),\n      delays: {\n        calculated: adaptiveDelay,\n        actual: waitDelay\n      }\n    })\n  }\n  // endregion ////\n}\n"],"names":[],"mappings":"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"}