@tramvai/module-request-limiter 1.84.2 → 1.90.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -57,6 +57,9 @@ const provider = {
57
57
  };
58
58
  ```
59
59
 
60
+ The basic settings should work well for most applications, as there is a system built in to adapt and automatically change the parameters relative to the loads. As a result, change the settings after you have done a load test and know that the changes will definitely improve the situation
61
+
62
+
60
63
  ## Explanation
61
64
 
62
65
  After the server starts, request limiter can handle `options.limit` parallel connections.
@@ -1,4 +1,5 @@
1
- import type { Request, Response, NextFunction } from 'express';
1
+ /// <reference types="node" />
2
+ import type { FastifyRequest, FastifyReply, HookHandlerDoneFunction } from 'fastify';
2
3
  export interface RequestLimiterOptions {
3
4
  limit?: number;
4
5
  queue?: number;
@@ -9,9 +10,9 @@ export interface RequestLimiterOptions {
9
10
  };
10
11
  }
11
12
  export interface RequestLimiterRequest {
12
- req: Request;
13
- res: Response;
14
- next: NextFunction;
13
+ req: FastifyRequest;
14
+ res: FastifyReply;
15
+ next: HookHandlerDoneFunction;
15
16
  }
16
17
  export declare class RequestLimiter {
17
18
  private currentActive;
@@ -24,8 +25,12 @@ export declare class RequestLimiter {
24
25
  private maxEventLoopDelay;
25
26
  private eventLoopHistogram;
26
27
  constructor(options?: RequestLimiterOptions);
28
+ onResponse(): void;
27
29
  private nextTick;
28
30
  add(request: RequestLimiterRequest): void;
29
31
  private loop;
30
32
  private run;
31
33
  }
34
+ export declare const fastifyRequestsLimiter: import("fastify").FastifyPluginAsync<{
35
+ requestsLimiter: RequestLimiter;
36
+ }, import("http").Server>;
package/lib/server.es.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import { Module, provide, Scope } from '@tramvai/core';
3
- import { WEB_APP_LIMITER_TOKEN } from '@tramvai/tokens-server';
4
- import onFinished from 'on-finished';
3
+ import { WEB_FASTIFY_APP_LIMITER_TOKEN, WEB_FASTIFY_APP_TOKEN } from '@tramvai/tokens-server-private';
4
+ import fp from 'fastify-plugin';
5
5
  import { HttpError } from '@tinkoff/errors';
6
6
  import { monitorEventLoopDelay } from 'perf_hooks';
7
7
  import { createToken } from '@tinkoff/dippy';
@@ -80,7 +80,7 @@ class RequestLimiter {
80
80
  this.queue = new DoubleLinkedList();
81
81
  const { limit = DEFAULT_OPTIONS.limit, queue = DEFAULT_OPTIONS.queue, maxEventLoopDelay = DEFAULT_OPTIONS.maxEventLoopDelay, error = DEFAULT_OPTIONS.error, } = options;
82
82
  this.activeRequestLimit = limit;
83
- this.minimalActiveRequestLimit = Math.floor(limit / 2);
83
+ this.minimalActiveRequestLimit = Math.round(limit / 3);
84
84
  this.queueLimit = queue;
85
85
  this.error = error;
86
86
  this.maxEventLoopDelay = maxEventLoopDelay;
@@ -89,18 +89,22 @@ class RequestLimiter {
89
89
  const timer = setInterval(() => this.nextTick(), 1000);
90
90
  timer.unref();
91
91
  }
92
- // General idea is change limits ever second. Because if DDOS was happened we need some time to get problem with event loop. And better if we slowly adapt
92
+ onResponse() {
93
+ this.currentActive--;
94
+ this.loop();
95
+ }
96
+ // General idea is change limits every second. Because if DDOS was happened we need some time to get problem with event loop. And better if we slowly adapt
93
97
  nextTick() {
94
98
  this.eventLoopDelay = Math.max(0, this.eventLoopHistogram.mean / 1e6 - resolution);
95
99
  if (Number.isNaN(this.eventLoopDelay))
96
100
  this.eventLoopDelay = Infinity;
97
101
  this.eventLoopHistogram.reset();
98
- if (this.currentActive >= this.activeRequestLimit &&
99
- this.activeRequestLimit > this.minimalActiveRequestLimit) {
102
+ if (this.currentActive >= this.activeRequestLimit) {
100
103
  if (this.eventLoopDelay <= this.maxEventLoopDelay) {
101
104
  this.activeRequestLimit++;
105
+ // We need to have minimalActiveRequestLimit
102
106
  }
103
- else {
107
+ else if (this.activeRequestLimit > this.minimalActiveRequestLimit) {
104
108
  this.activeRequestLimit--;
105
109
  }
106
110
  }
@@ -122,16 +126,19 @@ class RequestLimiter {
122
126
  this.run(this.queue.pop());
123
127
  }
124
128
  }
125
- run({ req, res, next }) {
129
+ run({ next }) {
126
130
  this.currentActive++;
127
- // onFinished doesn't work OK in DEV mode. Just stuck with high load without any reasons
128
- onFinished(res, () => {
129
- this.currentActive--;
130
- this.loop();
131
- });
132
131
  next();
133
132
  }
134
133
  }
134
+ const fastifyRequestsLimiter = fp(async (fastify, { requestsLimiter }) => {
135
+ fastify.addHook('onRequest', (req, res, next) => {
136
+ requestsLimiter.add({ req, res, next });
137
+ });
138
+ fastify.addHook('onResponse', async () => {
139
+ requestsLimiter.onResponse();
140
+ });
141
+ });
135
142
 
136
143
  const REQUESTS_LIMITER_TOKEN = createToken('requestsLimiterToken');
137
144
  const REQUESTS_LIMITER_ACTIVATE_TOKEN = createToken('requestsLimiterActivateToken');
@@ -157,19 +164,18 @@ RequestLimiterModule = __decorate([
157
164
  },
158
165
  }),
159
166
  provide({
160
- provide: WEB_APP_LIMITER_TOKEN,
167
+ provide: WEB_FASTIFY_APP_LIMITER_TOKEN,
161
168
  multi: true,
162
- useFactory: ({ requestsLimiter, featureEnable }) => {
163
- return function addRequestsLimiterMiddleware(app) {
169
+ useFactory: ({ app, requestsLimiter, featureEnable }) => {
170
+ return async () => {
164
171
  if (featureEnable !== true) {
165
172
  return;
166
173
  }
167
- app.use((req, res, next) => {
168
- requestsLimiter.add({ req, res, next });
169
- });
174
+ await app.register(fastifyRequestsLimiter, { requestsLimiter });
170
175
  };
171
176
  },
172
177
  deps: {
178
+ app: WEB_FASTIFY_APP_TOKEN,
173
179
  requestsLimiter: REQUESTS_LIMITER_TOKEN,
174
180
  featureEnable: { token: REQUESTS_LIMITER_ACTIVATE_TOKEN, optional: true },
175
181
  },
@@ -178,4 +184,4 @@ RequestLimiterModule = __decorate([
178
184
  })
179
185
  ], RequestLimiterModule);
180
186
 
181
- export { REQUESTS_LIMITER_ACTIVATE_TOKEN, REQUESTS_LIMITER_OPTIONS_TOKEN, REQUESTS_LIMITER_TOKEN, RequestLimiter, RequestLimiterModule };
187
+ export { REQUESTS_LIMITER_ACTIVATE_TOKEN, REQUESTS_LIMITER_OPTIONS_TOKEN, REQUESTS_LIMITER_TOKEN, RequestLimiter, RequestLimiterModule, fastifyRequestsLimiter };
package/lib/server.js CHANGED
@@ -4,15 +4,15 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var tslib = require('tslib');
6
6
  var core = require('@tramvai/core');
7
- var tokensServer = require('@tramvai/tokens-server');
8
- var onFinished = require('on-finished');
7
+ var tokensServerPrivate = require('@tramvai/tokens-server-private');
8
+ var fp = require('fastify-plugin');
9
9
  var errors = require('@tinkoff/errors');
10
10
  var perf_hooks = require('perf_hooks');
11
11
  var dippy = require('@tinkoff/dippy');
12
12
 
13
13
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
14
 
15
- var onFinished__default = /*#__PURE__*/_interopDefaultLegacy(onFinished);
15
+ var fp__default = /*#__PURE__*/_interopDefaultLegacy(fp);
16
16
 
17
17
  class DoubleLinkedList {
18
18
  constructor() {
@@ -88,7 +88,7 @@ class RequestLimiter {
88
88
  this.queue = new DoubleLinkedList();
89
89
  const { limit = DEFAULT_OPTIONS.limit, queue = DEFAULT_OPTIONS.queue, maxEventLoopDelay = DEFAULT_OPTIONS.maxEventLoopDelay, error = DEFAULT_OPTIONS.error, } = options;
90
90
  this.activeRequestLimit = limit;
91
- this.minimalActiveRequestLimit = Math.floor(limit / 2);
91
+ this.minimalActiveRequestLimit = Math.round(limit / 3);
92
92
  this.queueLimit = queue;
93
93
  this.error = error;
94
94
  this.maxEventLoopDelay = maxEventLoopDelay;
@@ -97,18 +97,22 @@ class RequestLimiter {
97
97
  const timer = setInterval(() => this.nextTick(), 1000);
98
98
  timer.unref();
99
99
  }
100
- // General idea is change limits ever second. Because if DDOS was happened we need some time to get problem with event loop. And better if we slowly adapt
100
+ onResponse() {
101
+ this.currentActive--;
102
+ this.loop();
103
+ }
104
+ // General idea is change limits every second. Because if DDOS was happened we need some time to get problem with event loop. And better if we slowly adapt
101
105
  nextTick() {
102
106
  this.eventLoopDelay = Math.max(0, this.eventLoopHistogram.mean / 1e6 - resolution);
103
107
  if (Number.isNaN(this.eventLoopDelay))
104
108
  this.eventLoopDelay = Infinity;
105
109
  this.eventLoopHistogram.reset();
106
- if (this.currentActive >= this.activeRequestLimit &&
107
- this.activeRequestLimit > this.minimalActiveRequestLimit) {
110
+ if (this.currentActive >= this.activeRequestLimit) {
108
111
  if (this.eventLoopDelay <= this.maxEventLoopDelay) {
109
112
  this.activeRequestLimit++;
113
+ // We need to have minimalActiveRequestLimit
110
114
  }
111
- else {
115
+ else if (this.activeRequestLimit > this.minimalActiveRequestLimit) {
112
116
  this.activeRequestLimit--;
113
117
  }
114
118
  }
@@ -130,16 +134,19 @@ class RequestLimiter {
130
134
  this.run(this.queue.pop());
131
135
  }
132
136
  }
133
- run({ req, res, next }) {
137
+ run({ next }) {
134
138
  this.currentActive++;
135
- // onFinished doesn't work OK in DEV mode. Just stuck with high load without any reasons
136
- onFinished__default["default"](res, () => {
137
- this.currentActive--;
138
- this.loop();
139
- });
140
139
  next();
141
140
  }
142
141
  }
142
+ const fastifyRequestsLimiter = fp__default["default"](async (fastify, { requestsLimiter }) => {
143
+ fastify.addHook('onRequest', (req, res, next) => {
144
+ requestsLimiter.add({ req, res, next });
145
+ });
146
+ fastify.addHook('onResponse', async () => {
147
+ requestsLimiter.onResponse();
148
+ });
149
+ });
143
150
 
144
151
  const REQUESTS_LIMITER_TOKEN = dippy.createToken('requestsLimiterToken');
145
152
  const REQUESTS_LIMITER_ACTIVATE_TOKEN = dippy.createToken('requestsLimiterActivateToken');
@@ -165,19 +172,18 @@ exports.RequestLimiterModule = tslib.__decorate([
165
172
  },
166
173
  }),
167
174
  core.provide({
168
- provide: tokensServer.WEB_APP_LIMITER_TOKEN,
175
+ provide: tokensServerPrivate.WEB_FASTIFY_APP_LIMITER_TOKEN,
169
176
  multi: true,
170
- useFactory: ({ requestsLimiter, featureEnable }) => {
171
- return function addRequestsLimiterMiddleware(app) {
177
+ useFactory: ({ app, requestsLimiter, featureEnable }) => {
178
+ return async () => {
172
179
  if (featureEnable !== true) {
173
180
  return;
174
181
  }
175
- app.use((req, res, next) => {
176
- requestsLimiter.add({ req, res, next });
177
- });
182
+ await app.register(fastifyRequestsLimiter, { requestsLimiter });
178
183
  };
179
184
  },
180
185
  deps: {
186
+ app: tokensServerPrivate.WEB_FASTIFY_APP_TOKEN,
181
187
  requestsLimiter: REQUESTS_LIMITER_TOKEN,
182
188
  featureEnable: { token: REQUESTS_LIMITER_ACTIVATE_TOKEN, optional: true },
183
189
  },
@@ -190,3 +196,4 @@ exports.REQUESTS_LIMITER_ACTIVATE_TOKEN = REQUESTS_LIMITER_ACTIVATE_TOKEN;
190
196
  exports.REQUESTS_LIMITER_OPTIONS_TOKEN = REQUESTS_LIMITER_OPTIONS_TOKEN;
191
197
  exports.REQUESTS_LIMITER_TOKEN = REQUESTS_LIMITER_TOKEN;
192
198
  exports.RequestLimiter = RequestLimiter;
199
+ exports.fastifyRequestsLimiter = fastifyRequestsLimiter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-request-limiter",
3
- "version": "1.84.2",
3
+ "version": "1.90.1",
4
4
  "description": "Enable different rendering modes for pages",
5
5
  "main": "lib/server.js",
6
6
  "module": "lib/server.es.js",
@@ -25,16 +25,14 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@tinkoff/errors": "0.2.20",
28
- "on-finished": "^2.3.0"
29
- },
30
- "devDependencies": {
31
- "@types/express": "^4.17.9"
28
+ "fastify-plugin": "^3.0.1"
32
29
  },
30
+ "devDependencies": {},
33
31
  "peerDependencies": {
34
32
  "@tinkoff/dippy": "0.7.39",
35
- "@tramvai/core": "1.84.2",
36
- "@tramvai/tokens-server": "1.84.2",
37
- "express": "^4.17.1",
33
+ "@tramvai/core": "1.90.1",
34
+ "@tramvai/tokens-server-private": "1.90.1",
35
+ "fastify": "^3.0.0",
38
36
  "tslib": "^2.0.3"
39
37
  }
40
38
  }