MCPcopy Create free account
hub / github.com/InterviewReady/Low-Level-Design / TimerWheel

Class TimerWheel

rate-limiter/src/main/java/TimerWheel.java:8–76  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

6import java.util.concurrent.*;
7
8public class TimerWheel {
9 private final int timeOutPeriod;
10 private final int capacityPerSlot;
11 private final TimeUnit timeUnit;
12 private final ArrayBlockingQueue<Request>[] slots;
13 private final Map<String, Integer> reverseIndex;
14 private final Timer timer;
15 private final ExecutorService[] threads;
16
17 public TimerWheel(final TimeUnit timeUnit,
18 final int timeOutPeriod,
19 final int capacityPerSlot,
20 final Timer timer) {
21 this.timeUnit = timeUnit;
22 this.timeOutPeriod = timeOutPeriod;
23 this.capacityPerSlot = capacityPerSlot;
24 if (this.timeOutPeriod > 1000) {
25 throw new IllegalArgumentException();
26 }
27 this.slots = new ArrayBlockingQueue[this.timeOutPeriod];
28 this.threads = new ExecutorService[this.timeOutPeriod];
29 this.reverseIndex = new ConcurrentHashMap<>();
30 for (int i = 0; i < slots.length; i++) {
31 slots[i] = new ArrayBlockingQueue<>(capacityPerSlot);
32 threads[i] = Executors.newSingleThreadExecutor();
33 }
34 this.timer = timer;
35 final long timePerSlot = TimeUnit.MILLISECONDS.convert(1, timeUnit);
36 Executors.newSingleThreadScheduledExecutor()
37 .scheduleAtFixedRate(this::flushRequests,
38 timePerSlot - (this.timer.getCurrentTimeInMillis() % timePerSlot),
39 timePerSlot, TimeUnit.MILLISECONDS);
40 }
41
42 public Future<?> flushRequests() {
43 final int currentSlot = getCurrentSlot();
44 return threads[currentSlot].submit(() -> {
45 for (final Request request : slots[currentSlot]) {
46 if (timer.getCurrentTime(timeUnit) - request.getStartTime() >= timeOutPeriod) {
47 slots[currentSlot].remove(request);
48 reverseIndex.remove(request.getRequestId());
49 }
50 }
51 });
52 }
53
54 public Future<?> addRequest(final Request request) {
55 final int currentSlot = getCurrentSlot();
56 return threads[currentSlot].submit(() -> {
57 if (slots[currentSlot].size() >= capacityPerSlot) {
58 throw new RateLimitExceededException();
59 }
60 slots[currentSlot].add(request);
61 reverseIndex.put(request.getRequestId(), currentSlot);
62 });
63 }
64
65 public Future<?> evict(final String requestId) {

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected