Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0513d96
Run configlet create
andreatanky Sep 8, 2025
bfe4dbb
Update rate limiter implementation
andreatanky Sep 12, 2025
81c6728
Add instructions
andreatanky Sep 12, 2025
ae33983
Add instructions
andreatanky Sep 12, 2025
4f5abf3
Merge branch 'main' into add-rate-limiter-exercise
andreatanky Sep 12, 2025
4f0c836
Remove interfaces
andreatanky Sep 14, 2025
a697b81
Update instructions
andreatanky Sep 14, 2025
8092b9a
Merge branch 'add-rate-limiter-exercise' of github.com:andreatanky/ja…
andreatanky Sep 14, 2025
87fc175
Add github handle to authors array
andreatanky Sep 14, 2025
4479173
Add blurb
andreatanky Sep 14, 2025
d9520b7
Merge branch 'main' into add-rate-limiter-exercise
andreatanky Sep 14, 2025
63ea8a3
Update long to use Duration
andreatanky Sep 18, 2025
9f6a8bf
Update advance method to use Duration in tests
andreatanky Sep 19, 2025
a616a10
Remove unused advanceNanos method
andreatanky Sep 19, 2025
9f077d0
Add non string tests
andreatanky Sep 19, 2025
89ec426
Break down test cases
andreatanky Sep 19, 2025
9657afc
Update to one sentence per line
andreatanky Sep 20, 2025
98d6103
Update WindowState long type to use Instant
andreatanky Sep 20, 2025
43610f1
Remove advanceNanos
andreatanky Sep 20, 2025
a61ef4d
Merge branch 'main' into add-rate-limiter-exercise
andreatanky Sep 20, 2025
50b280c
Update naming of key to clientId
andreatanky Sep 22, 2025
5971500
Merge branch 'add-rate-limiter-exercise' of github.com:andreatanky/ja…
andreatanky Sep 22, 2025
69c1ba9
Update exercises/practice/rate-limiter/.docs/instructions.md
andreatanky Sep 22, 2025
6f27c12
Update exercises/practice/rate-limiter/.docs/instructions.md
andreatanky Sep 22, 2025
368d7ea
Remove custom object test
andreatanky Sep 22, 2025
87c3d3c
Update exercises/practice/rate-limiter/src/test/java/RateLimiterTest.…
andreatanky Sep 22, 2025
d451060
Add tiny clock advances in tests
andreatanky Sep 22, 2025
67baa14
Update difficulty to 4
andreatanky Sep 22, 2025
aba22d5
Add generic types to prereq
andreatanky Sep 22, 2025
715f181
Update exercises/practice/rate-limiter/src/main/java/TimeSource.java
andreatanky Sep 22, 2025
efadb02
Update exercises/practice/rate-limiter/.meta/config.json
andreatanky Sep 22, 2025
8dc4059
Update UUID test to include mixture of clock advance duration
andreatanky Sep 22, 2025
b6dea08
Merge branch 'add-rate-limiter-exercise' of github.com:andreatanky/ja…
andreatanky Sep 22, 2025
ae8d97e
Merge branch 'main' into add-rate-limiter-exercise
andreatanky Sep 22, 2025
60736bb
Shift exercise in config.json
andreatanky Sep 23, 2025
25a2876
Merge branch 'add-rate-limiter-exercise' of github.com:andreatanky/ja…
andreatanky Sep 23, 2025
d418567
Copy gradle wrapper
andreatanky Sep 23, 2025
baf39d2
Update exercises/practice/rate-limiter/.docs/instructions.md
andreatanky Sep 23, 2025
7907757
Update exercises/practice/rate-limiter/.docs/instructions.md
andreatanky Sep 23, 2025
6100e11
Add display name annotation
andreatanky Sep 23, 2025
4d9b1bf
Merge branch 'add-rate-limiter-exercise' of github.com:andreatanky/ja…
andreatanky Sep 23, 2025
6ccd97a
Run configlet to format config.json
andreatanky Sep 23, 2025
b839de1
Reformat files
andreatanky Sep 23, 2025
a8a6048
Update exercises/practice/rate-limiter/src/test/java/RateLimiterTest.…
andreatanky Sep 24, 2025
6b8e310
Update exercises/practice/rate-limiter/src/test/java/RateLimiterTest.…
andreatanky Sep 24, 2025
b3667bc
Update exercises/practice/rate-limiter/src/test/java/RateLimiterTest.…
andreatanky Sep 24, 2025
0b158f7
Update exercises/practice/rate-limiter/src/main/java/RateLimiter.java
andreatanky Sep 24, 2025
d5b13e9
Merge branch 'main' into add-rate-limiter-exercise
andreatanky Sep 24, 2025
5e4cc5c
Update config.json
andreatanky Sep 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,16 @@
],
"difficulty": 4
},
{
"slug": "rate-limiter",
"name": "Rate Limiter",
"uuid": "b4b0c60e-4ce1-488e-948f-bcb6821c773c",
"practices": [],
"prerequisites": [
"generic-types"
],
"difficulty": 4
},
{
"slug": "rotational-cipher",
"name": "Rotational Cipher",
Expand Down
23 changes: 23 additions & 0 deletions exercises/practice/rate-limiter/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Instructions

Your task is to build a fixed‑window rate limiter.

Imagine a single server connected to one or more clients.
A client sends a request, the server does some work, and returns a response.
But processing takes time.
If a client sends too many requests too quickly, the server can become overwhelmed — everything slows down or fails.

A rate limiter is a small component that decides whether to allow or reject a request based on how frequently that client has been making requests.
Different strategies exist; in this exercise you’ll implement a fixed‑window rate limiter.

Fixed‑window rate limiting groups time into equal‑length windows (for example, every 10 seconds) and allows up to a certain number of requests within each window for each client.
Once the window resets, the allowance refreshes for the next window.
Each client is tracked separately, so another client can make requests within that same period.

For example, consider a rate limiter configured to limit 2 requests per 10 seconds per client.
Lets say a client sends a request:

- Being its first request, the request is permitted.
- A second request within 10 seconds after the first one is also permitted.
- However, further requests after that would be denied _until_ at least 10 seconds has elapsed since the first request.
- If a second client sends its first request within 10 seconds with of the first client's first request, it would also be permitted, _regardless_ of whether the first client has sent a second request.
21 changes: 21 additions & 0 deletions exercises/practice/rate-limiter/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"authors": [
"andreatanky"
],
"files": {
"solution": [
"src/main/java/RateLimiter.java"
],
"test": [
"src/test/java/RateLimiterTest.java"
],
"example": [
".meta/src/reference/java/RateLimiter.java",
".meta/src/reference/java/TimeSource.java"
],
"editor": [
"src/main/java/TimeSource.java"
]
},
"blurb": "Practice stateful logic and time handling by implementing a fixed-window rate limiter"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

public class RateLimiter<K> {

private static final class WindowState {
Instant windowStart;
int usedCount;

WindowState(Instant windowStart, int usedCount) {
this.windowStart = windowStart;
this.usedCount = usedCount;
}
}

private final int limit;
private final Duration windowSize;
private final TimeSource timeSource;
private final Map<K, WindowState> states = new HashMap<>();

public RateLimiter(int limit, Duration windowSize, TimeSource timeSource) {
this.limit = limit;
this.windowSize = windowSize;
this.timeSource = timeSource;
}

public boolean allow(K clientId) {
Instant now = timeSource.now();

WindowState s = states.get(clientId);
if (s == null) {
s = new WindowState(now, 0);
states.put(clientId, s);
}

if (!now.isBefore(s.windowStart.plus(windowSize))) {
s.windowStart = now;
s.usedCount = 0;
}

if (s.usedCount < limit) {
s.usedCount += 1;
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import java.time.Duration;
import java.time.Instant;

public class TimeSource {
private Instant now;

public TimeSource(Instant start) {
this.now = start;
}

public Instant now() {
return now;
}

public void advance(Duration d) {
this.now = this.now.plus(d);
}
}
25 changes: 25 additions & 0 deletions exercises/practice/rate-limiter/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins {
id "java"
}

repositories {
mavenCentral()
}

dependencies {
testImplementation platform("org.junit:junit-bom:5.10.0")
testImplementation "org.junit.jupiter:junit-jupiter"
testImplementation "org.assertj:assertj-core:3.25.1"

testRuntimeOnly "org.junit.platform:junit-platform-launcher"
}

test {
useJUnitPlatform()

testLogging {
exceptionFormat = "full"
showStandardStreams = true
events = ["passed", "failed", "skipped"]
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading