Skip to content

Commit 53c31af

Browse files
add throttling guide
1 parent 1c5b422 commit 53c31af

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

malta.config.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@
3232
},
3333
{
3434
"title": "Rate limiting",
35-
"pages": [["Token bucket", "/rate-limit/token-bucket"]]
35+
"pages": [
36+
["Token bucket", "/rate-limit/token-bucket"],
37+
["Throttling", "/rate-limit/throttling"]
38+
]
3639
},
3740
{
3841
"title": "Community",

pages/rate-limit/throttling.md

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
title: "Throttling"
3+
---
4+
5+
# Throttling
6+
7+
After each failed attempt, the user has to wait longer before their next attempt.
8+
9+
## Memory storage
10+
11+
```ts
12+
export class Throttler<_Key> {
13+
public timeoutSeconds: number[];
14+
15+
private storage = new Map<_Key, ThrottlingCounter>();
16+
17+
constructor(timeoutSeconds: number[]) {
18+
this.timeoutSeconds = timeoutSeconds;
19+
}
20+
21+
public consume(key: _Key): boolean {
22+
let counter = this.storage.get(key) ?? null;
23+
const now = Date.now();
24+
if (counter === null) {
25+
counter = {
26+
timeout: 0,
27+
updatedAt: now
28+
};
29+
this.storage.set(key, counter);
30+
return true;
31+
}
32+
const allowed = now - counter.updatedAt >= this.timeoutSeconds[counter.timeout] * 1000;
33+
if (!allowed) {
34+
return false;
35+
}
36+
counter.updatedAt = now;
37+
counter.timeout = Math.min(counter.timeout + 1, this.timeoutSeconds.length - 1);
38+
this.storage.set(key, counter);
39+
return true;
40+
}
41+
42+
public reset(key: _Key): void {
43+
this.storage.delete(key);
44+
}
45+
}
46+
```
47+
48+
Here, on each failed sign in attempt, the lockout gets extended with a max of 5 minutes.
49+
50+
```ts
51+
const throttler = new Throttler([0, 1, 2, 4, 8, 16, 30, 60, 180, 300]);
52+
53+
if (!throttler.consume(userId)) {
54+
throw new Error("Too many requests");
55+
}
56+
if (!verifyPassword(password)) {
57+
throw new Error("Invalid password");
58+
}
59+
throttler.reset(user.id);
60+
```

0 commit comments

Comments
 (0)