"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultRateLimiter = void 0; const service_error_classification_1 = require("@aws-sdk/service-error-classification"); class DefaultRateLimiter { constructor(options) { var _a, _b, _c, _d, _e; this.currentCapacity = 0; this.enabled = false; this.lastMaxRate = 0; this.measuredTxRate = 0; this.requestCount = 0; this.lastTimestamp = 0; this.timeWindow = 0; this.beta = (_a = options === null || options === void 0 ? void 0 : options.beta) !== null && _a !== void 0 ? _a : 0.7; this.minCapacity = (_b = options === null || options === void 0 ? void 0 : options.minCapacity) !== null && _b !== void 0 ? _b : 1; this.minFillRate = (_c = options === null || options === void 0 ? void 0 : options.minFillRate) !== null && _c !== void 0 ? _c : 0.5; this.scaleConstant = (_d = options === null || options === void 0 ? void 0 : options.scaleConstant) !== null && _d !== void 0 ? _d : 0.4; this.smooth = (_e = options === null || options === void 0 ? void 0 : options.smooth) !== null && _e !== void 0 ? _e : 0.8; const currentTimeInSeconds = this.getCurrentTimeInSeconds(); this.lastThrottleTime = currentTimeInSeconds; this.lastTxRateBucket = Math.floor(this.getCurrentTimeInSeconds()); this.fillRate = this.minFillRate; this.maxCapacity = this.minCapacity; } getCurrentTimeInSeconds() { return Date.now() / 1000; } async getSendToken() { return this.acquireTokenBucket(1); } async acquireTokenBucket(amount) { if (!this.enabled) { return; } this.refillTokenBucket(); if (amount > this.currentCapacity) { const delay = ((amount - this.currentCapacity) / this.fillRate) * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); } this.currentCapacity = this.currentCapacity - amount; } refillTokenBucket() { const timestamp = this.getCurrentTimeInSeconds(); if (!this.lastTimestamp) { this.lastTimestamp = timestamp; return; } const fillAmount = (timestamp - this.lastTimestamp) * this.fillRate; this.currentCapacity = Math.min(this.maxCapacity, this.currentCapacity + fillAmount); this.lastTimestamp = timestamp; } updateClientSendingRate(response) { let calculatedRate; this.updateMeasuredRate(); if ((0, service_error_classification_1.isThrottlingError)(response)) { const rateToUse = !this.enabled ? this.measuredTxRate : Math.min(this.measuredTxRate, this.fillRate); this.lastMaxRate = rateToUse; this.calculateTimeWindow(); this.lastThrottleTime = this.getCurrentTimeInSeconds(); calculatedRate = this.cubicThrottle(rateToUse); this.enableTokenBucket(); } else { this.calculateTimeWindow(); calculatedRate = this.cubicSuccess(this.getCurrentTimeInSeconds()); } const newRate = Math.min(calculatedRate, 2 * this.measuredTxRate); this.updateTokenBucketRate(newRate); } calculateTimeWindow() { this.timeWindow = this.getPrecise(Math.pow((this.lastMaxRate * (1 - this.beta)) / this.scaleConstant, 1 / 3)); } cubicThrottle(rateToUse) { return this.getPrecise(rateToUse * this.beta); } cubicSuccess(timestamp) { return this.getPrecise(this.scaleConstant * Math.pow(timestamp - this.lastThrottleTime - this.timeWindow, 3) + this.lastMaxRate); } enableTokenBucket() { this.enabled = true; } updateTokenBucketRate(newRate) { this.refillTokenBucket(); this.fillRate = Math.max(newRate, this.minFillRate); this.maxCapacity = Math.max(newRate, this.minCapacity); this.currentCapacity = Math.min(this.currentCapacity, this.maxCapacity); } updateMeasuredRate() { const t = this.getCurrentTimeInSeconds(); const timeBucket = Math.floor(t * 2) / 2; this.requestCount++; if (timeBucket > this.lastTxRateBucket) { const currentRate = this.requestCount / (timeBucket - this.lastTxRateBucket); this.measuredTxRate = this.getPrecise(currentRate * this.smooth + this.measuredTxRate * (1 - this.smooth)); this.requestCount = 0; this.lastTxRateBucket = timeBucket; } } getPrecise(num) { return parseFloat(num.toFixed(8)); } } exports.DefaultRateLimiter = DefaultRateLimiter;