1 | import { __awaiter, __generator } from "tslib";
|
2 | import { isThrottlingError } from "@aws-sdk/service-error-classification";
|
3 | var DefaultRateLimiter = (function () {
|
4 | function DefaultRateLimiter(options) {
|
5 | var _a, _b, _c, _d, _e;
|
6 | this.currentCapacity = 0;
|
7 | this.enabled = false;
|
8 | this.lastMaxRate = 0;
|
9 | this.measuredTxRate = 0;
|
10 | this.requestCount = 0;
|
11 | this.lastTimestamp = 0;
|
12 | this.timeWindow = 0;
|
13 | this.beta = (_a = options === null || options === void 0 ? void 0 : options.beta) !== null && _a !== void 0 ? _a : 0.7;
|
14 | this.minCapacity = (_b = options === null || options === void 0 ? void 0 : options.minCapacity) !== null && _b !== void 0 ? _b : 1;
|
15 | this.minFillRate = (_c = options === null || options === void 0 ? void 0 : options.minFillRate) !== null && _c !== void 0 ? _c : 0.5;
|
16 | this.scaleConstant = (_d = options === null || options === void 0 ? void 0 : options.scaleConstant) !== null && _d !== void 0 ? _d : 0.4;
|
17 | this.smooth = (_e = options === null || options === void 0 ? void 0 : options.smooth) !== null && _e !== void 0 ? _e : 0.8;
|
18 | var currentTimeInSeconds = this.getCurrentTimeInSeconds();
|
19 | this.lastThrottleTime = currentTimeInSeconds;
|
20 | this.lastTxRateBucket = Math.floor(this.getCurrentTimeInSeconds());
|
21 | this.fillRate = this.minFillRate;
|
22 | this.maxCapacity = this.minCapacity;
|
23 | }
|
24 | DefaultRateLimiter.prototype.getCurrentTimeInSeconds = function () {
|
25 | return Date.now() / 1000;
|
26 | };
|
27 | DefaultRateLimiter.prototype.getSendToken = function () {
|
28 | return __awaiter(this, void 0, void 0, function () {
|
29 | return __generator(this, function (_a) {
|
30 | return [2, this.acquireTokenBucket(1)];
|
31 | });
|
32 | });
|
33 | };
|
34 | DefaultRateLimiter.prototype.acquireTokenBucket = function (amount) {
|
35 | return __awaiter(this, void 0, void 0, function () {
|
36 | var delay_1;
|
37 | return __generator(this, function (_a) {
|
38 | switch (_a.label) {
|
39 | case 0:
|
40 | if (!this.enabled) {
|
41 | return [2];
|
42 | }
|
43 | this.refillTokenBucket();
|
44 | if (!(amount > this.currentCapacity)) return [3, 2];
|
45 | delay_1 = ((amount - this.currentCapacity) / this.fillRate) * 1000;
|
46 | return [4, new Promise(function (resolve) { return setTimeout(resolve, delay_1); })];
|
47 | case 1:
|
48 | _a.sent();
|
49 | _a.label = 2;
|
50 | case 2:
|
51 | this.currentCapacity = this.currentCapacity - amount;
|
52 | return [2];
|
53 | }
|
54 | });
|
55 | });
|
56 | };
|
57 | DefaultRateLimiter.prototype.refillTokenBucket = function () {
|
58 | var timestamp = this.getCurrentTimeInSeconds();
|
59 | if (!this.lastTimestamp) {
|
60 | this.lastTimestamp = timestamp;
|
61 | return;
|
62 | }
|
63 | var fillAmount = (timestamp - this.lastTimestamp) * this.fillRate;
|
64 | this.currentCapacity = Math.min(this.maxCapacity, this.currentCapacity + fillAmount);
|
65 | this.lastTimestamp = timestamp;
|
66 | };
|
67 | DefaultRateLimiter.prototype.updateClientSendingRate = function (response) {
|
68 | var calculatedRate;
|
69 | this.updateMeasuredRate();
|
70 | if (isThrottlingError(response)) {
|
71 | var rateToUse = !this.enabled ? this.measuredTxRate : Math.min(this.measuredTxRate, this.fillRate);
|
72 | this.lastMaxRate = rateToUse;
|
73 | this.calculateTimeWindow();
|
74 | this.lastThrottleTime = this.getCurrentTimeInSeconds();
|
75 | calculatedRate = this.cubicThrottle(rateToUse);
|
76 | this.enableTokenBucket();
|
77 | }
|
78 | else {
|
79 | this.calculateTimeWindow();
|
80 | calculatedRate = this.cubicSuccess(this.getCurrentTimeInSeconds());
|
81 | }
|
82 | var newRate = Math.min(calculatedRate, 2 * this.measuredTxRate);
|
83 | this.updateTokenBucketRate(newRate);
|
84 | };
|
85 | DefaultRateLimiter.prototype.calculateTimeWindow = function () {
|
86 | this.timeWindow = this.getPrecise(Math.pow((this.lastMaxRate * (1 - this.beta)) / this.scaleConstant, 1 / 3));
|
87 | };
|
88 | DefaultRateLimiter.prototype.cubicThrottle = function (rateToUse) {
|
89 | return this.getPrecise(rateToUse * this.beta);
|
90 | };
|
91 | DefaultRateLimiter.prototype.cubicSuccess = function (timestamp) {
|
92 | return this.getPrecise(this.scaleConstant * Math.pow(timestamp - this.lastThrottleTime - this.timeWindow, 3) + this.lastMaxRate);
|
93 | };
|
94 | DefaultRateLimiter.prototype.enableTokenBucket = function () {
|
95 | this.enabled = true;
|
96 | };
|
97 | DefaultRateLimiter.prototype.updateTokenBucketRate = function (newRate) {
|
98 | this.refillTokenBucket();
|
99 | this.fillRate = Math.max(newRate, this.minFillRate);
|
100 | this.maxCapacity = Math.max(newRate, this.minCapacity);
|
101 | this.currentCapacity = Math.min(this.currentCapacity, this.maxCapacity);
|
102 | };
|
103 | DefaultRateLimiter.prototype.updateMeasuredRate = function () {
|
104 | var t = this.getCurrentTimeInSeconds();
|
105 | var timeBucket = Math.floor(t * 2) / 2;
|
106 | this.requestCount++;
|
107 | if (timeBucket > this.lastTxRateBucket) {
|
108 | var currentRate = this.requestCount / (timeBucket - this.lastTxRateBucket);
|
109 | this.measuredTxRate = this.getPrecise(currentRate * this.smooth + this.measuredTxRate * (1 - this.smooth));
|
110 | this.requestCount = 0;
|
111 | this.lastTxRateBucket = timeBucket;
|
112 | }
|
113 | };
|
114 | DefaultRateLimiter.prototype.getPrecise = function (num) {
|
115 | return parseFloat(num.toFixed(8));
|
116 | };
|
117 | return DefaultRateLimiter;
|
118 | }());
|
119 | export { DefaultRateLimiter };
|