@diia-inhouse/redis 3.1.55 → 3.1.59
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/dist/interfaces/store.d.ts +24 -0
- package/dist/services/store.d.ts +27 -0
- package/dist/services/store.js +27 -0
- package/package.json +9 -9
|
@@ -18,11 +18,35 @@ interface SetValueOptions {
|
|
|
18
18
|
*/
|
|
19
19
|
tags?: string[];
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Parsed reply of the `CL.THROTTLE` command.
|
|
23
|
+
*
|
|
24
|
+
* @see {@link https://www.dragonflydb.io/docs/command-reference/strings/cl.throttle | Dragonfly CL.THROTTLE}
|
|
25
|
+
*/
|
|
21
26
|
interface ThrottleResult {
|
|
27
|
+
/**
|
|
28
|
+
* Whether the action must be limited, i.e. the bucket had no room for the requested quantity.
|
|
29
|
+
*/
|
|
22
30
|
limited: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Total limit of the key, equals `maxBurst + 1`.
|
|
33
|
+
* Maps to the `X-RateLimit-Limit` header.
|
|
34
|
+
*/
|
|
23
35
|
totalLimit: number;
|
|
36
|
+
/**
|
|
37
|
+
* Remaining limit of the key.
|
|
38
|
+
* Maps to the `X-RateLimit-Remaining` header.
|
|
39
|
+
*/
|
|
24
40
|
remaining: number;
|
|
41
|
+
/**
|
|
42
|
+
* Number of seconds to wait before retrying if the action was limited, otherwise `-1`.
|
|
43
|
+
* Maps to the `Retry-After` header.
|
|
44
|
+
*/
|
|
25
45
|
retryAfterSec: number;
|
|
46
|
+
/**
|
|
47
|
+
* Number of seconds until the limit is fully restored.
|
|
48
|
+
* Maps to the `X-RateLimit-Reset` header.
|
|
49
|
+
*/
|
|
26
50
|
resetAfterSec: number;
|
|
27
51
|
}
|
|
28
52
|
type StoreStatusResult = {
|
package/dist/services/store.d.ts
CHANGED
|
@@ -38,6 +38,33 @@ declare class StoreService implements OnHealthCheck, OnDestroy {
|
|
|
38
38
|
hdel(key: string, ...fields: string[]): Promise<number>;
|
|
39
39
|
expire(key: string, seconds: number): Promise<number>;
|
|
40
40
|
bumpTags(tags: string[]): Promise<"OK" | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Applies a leaky bucket rate limit to the given key via the `CL.THROTTLE` command.
|
|
43
|
+
*
|
|
44
|
+
* The call is atomic: it consumes `quantity` tokens and reports the resulting bucket state in one round trip.
|
|
45
|
+
* Tokens are refilled continuously at `rate / periodSec`, so bursts are smoothed out
|
|
46
|
+
* and there are no edge cases at fixed-window boundaries.
|
|
47
|
+
*
|
|
48
|
+
* @param key - Identifier to rate limit against, e.g. a user id or an IP address.
|
|
49
|
+
* @param maxBurst - Extra tokens allowed on top of the steady rate, i.e. how large a burst may be.
|
|
50
|
+
* The bucket therefore holds `maxBurst + 1` tokens.
|
|
51
|
+
* @param rate - Number of tokens refilled per `periodSec`.
|
|
52
|
+
* @param periodSec - Length of the refill period in seconds.
|
|
53
|
+
* @param quantity - Number of tokens the call consumes.
|
|
54
|
+
* @returns Whether the action is limited plus the rate limit headers state.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* // 10 requests per minute, tolerating a burst of 5 extra requests
|
|
59
|
+
* const { limited, retryAfterSec } = await store.throttle(`login:${userId}`, 5, 10, 60)
|
|
60
|
+
* if (limited) {
|
|
61
|
+
* res.setHeader('Retry-After', String(retryAfterSec))
|
|
62
|
+
* res.writeHead(HttpStatusCode.TOO_MANY_REQUESTS)
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @see {@link https://www.dragonflydb.io/docs/command-reference/strings/cl.throttle | Dragonfly CL.THROTTLE}
|
|
67
|
+
*/
|
|
41
68
|
throttle(key: string, maxBurst: number, rate: number, periodSec: number, quantity?: number): Promise<ThrottleResult>;
|
|
42
69
|
flushDb(): Promise<"OK">;
|
|
43
70
|
private validate;
|
package/dist/services/store.js
CHANGED
|
@@ -138,6 +138,33 @@ var StoreService = class {
|
|
|
138
138
|
for (const tagKey of tags) tagsConfig[tagKey] = timestamp;
|
|
139
139
|
return await this.clientRW.set(this.tagsKey, JSON.stringify(tagsConfig));
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Applies a leaky bucket rate limit to the given key via the `CL.THROTTLE` command.
|
|
143
|
+
*
|
|
144
|
+
* The call is atomic: it consumes `quantity` tokens and reports the resulting bucket state in one round trip.
|
|
145
|
+
* Tokens are refilled continuously at `rate / periodSec`, so bursts are smoothed out
|
|
146
|
+
* and there are no edge cases at fixed-window boundaries.
|
|
147
|
+
*
|
|
148
|
+
* @param key - Identifier to rate limit against, e.g. a user id or an IP address.
|
|
149
|
+
* @param maxBurst - Extra tokens allowed on top of the steady rate, i.e. how large a burst may be.
|
|
150
|
+
* The bucket therefore holds `maxBurst + 1` tokens.
|
|
151
|
+
* @param rate - Number of tokens refilled per `periodSec`.
|
|
152
|
+
* @param periodSec - Length of the refill period in seconds.
|
|
153
|
+
* @param quantity - Number of tokens the call consumes.
|
|
154
|
+
* @returns Whether the action is limited plus the rate limit headers state.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* // 10 requests per minute, tolerating a burst of 5 extra requests
|
|
159
|
+
* const { limited, retryAfterSec } = await store.throttle(`login:${userId}`, 5, 10, 60)
|
|
160
|
+
* if (limited) {
|
|
161
|
+
* res.setHeader('Retry-After', String(retryAfterSec))
|
|
162
|
+
* res.writeHead(HttpStatusCode.TOO_MANY_REQUESTS)
|
|
163
|
+
* }
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* @see {@link https://www.dragonflydb.io/docs/command-reference/strings/cl.throttle | Dragonfly CL.THROTTLE}
|
|
167
|
+
*/
|
|
141
168
|
async throttle(key, maxBurst, rate, periodSec, quantity = 1) {
|
|
142
169
|
const result = await this.clientRW.call("CL.THROTTLE", key, maxBurst, rate, periodSec, quantity);
|
|
143
170
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diia-inhouse/redis",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.59",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Redis services - redlock, pubsub, cache and store",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
"@diia-inhouse/validators": ">=2.0.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@diia-inhouse/configs": "7.0
|
|
51
|
-
"@diia-inhouse/diia-logger": "4.4.
|
|
52
|
-
"@diia-inhouse/env": "3.3.
|
|
53
|
-
"@diia-inhouse/errors": "2.2.
|
|
54
|
-
"@diia-inhouse/oxc-config": "2.1.
|
|
55
|
-
"@diia-inhouse/test": "8.3.
|
|
56
|
-
"@diia-inhouse/types": "14.2.
|
|
57
|
-
"@diia-inhouse/validators": "2.2.
|
|
50
|
+
"@diia-inhouse/configs": "7.1.0",
|
|
51
|
+
"@diia-inhouse/diia-logger": "4.4.49",
|
|
52
|
+
"@diia-inhouse/env": "3.3.48",
|
|
53
|
+
"@diia-inhouse/errors": "2.2.18",
|
|
54
|
+
"@diia-inhouse/oxc-config": "2.1.3",
|
|
55
|
+
"@diia-inhouse/test": "8.3.5",
|
|
56
|
+
"@diia-inhouse/types": "14.2.6",
|
|
57
|
+
"@diia-inhouse/validators": "2.2.61",
|
|
58
58
|
"@types/node": "25.6.2",
|
|
59
59
|
"@vitest/coverage-v8": "4.1.5",
|
|
60
60
|
"@vitest/ui": "4.1.5",
|