1 package io.getunleash.util;
2
3 import static java.lang.Integer.max;
4
5 import java.net.HttpURLConnection;
6 import java.net.URL;
7 import java.util.concurrent.atomic.AtomicInteger;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10
11 public class Throttler {
12 private static final Logger LOGGER = LoggerFactory.getLogger(Throttler.class);
13 private final int maxSkips;
14
15 private final int intervalLength;
16 private final AtomicInteger skips = new AtomicInteger(0);
17 private final AtomicInteger failures = new AtomicInteger(0);
18
19 private final URL target;
20
21 public Throttler(int intervalLengthSeconds, int longestAcceptableIntervalSeconds, URL target) {
22 this.maxSkips = max(longestAcceptableIntervalSeconds / max(intervalLengthSeconds, 1), 1);
23 this.target = target;
24 this.intervalLength = intervalLengthSeconds;
25 }
26
27
32 public void decrementFailureCountAndResetSkips() {
33 if (failures.get() > 0) {
34 skips.set(Math.max(failures.decrementAndGet(), 0));
35 }
36 }
37
38
43 public void increaseSkipCount() {
44 skips.set(Math.min(failures.incrementAndGet(), maxSkips));
45 }
46
47
52 public void maximizeSkips() {
53 skips.set(maxSkips);
54 failures.incrementAndGet();
55 }
56
57 public boolean performAction() {
58 return skips.get() <= 0;
59 }
60
61 public void skipped() {
62 skips.decrementAndGet();
63 }
64
65 public void handleHttpErrorCodes(int responseCode) {
66 if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED
67 || responseCode == HttpURLConnection.HTTP_FORBIDDEN) {
68 maximizeSkips();
69 LOGGER.error(
70 "Client was not authorized to talk to the Unleash API at {}. Backing off to {} times our poll interval (of {} seconds) to avoid overloading server",
71 this.target,
72 maxSkips,
73 this.intervalLength);
74 }
75 if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
76 maximizeSkips();
77 LOGGER.error(
78 "Server said that the endpoint at {} does not exist. Backing off to {} times our poll interval (of {} seconds) to avoid overloading server",
79 this.target,
80 maxSkips,
81 this.intervalLength);
82 } else if (responseCode == 429) {
83 increaseSkipCount();
84 LOGGER.info(
85 "RATE LIMITED for the {}. time. Further backing off. Current backoff at {} times our interval (of {} seconds)",
86 failures.get(),
87 skips.get(),
88 this.intervalLength);
89 } else if (responseCode >= HttpURLConnection.HTTP_INTERNAL_ERROR) {
90 increaseSkipCount();
91 LOGGER.info(
92 "Server failed with a {} status code. Backing off. Current backoff at {} times our poll interval (of {} seconds)",
93 responseCode,
94 skips.get(),
95 this.intervalLength);
96 }
97 }
98
99 public int getSkips() {
100 return this.skips.get();
101 }
102
103 public int getFailures() {
104 return this.failures.get();
105 }
106 }
107