Switch all Guava Cache uses to Caffeine

This commit is contained in:
Andrew Steinborn
2023-01-01 19:05:13 -05:00
parent aaa24752ac
commit 7ed422b4ed
5 changed files with 21 additions and 34 deletions

View File

@@ -17,8 +17,8 @@
package com.velocitypowered.proxy.network.netty; package com.velocitypowered.proxy.network.netty;
import com.google.common.cache.Cache; import com.github.benmanes.caffeine.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.netty.resolver.AddressResolver; import io.netty.resolver.AddressResolver;
@@ -62,7 +62,7 @@ public final class SeparatePoolInetNameResolver extends InetNameResolver {
.setDaemon(true) .setDaemon(true)
.build()); .build());
this.delegate = new DefaultNameResolver(executor); this.delegate = new DefaultNameResolver(executor);
this.cache = CacheBuilder.newBuilder() this.cache = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS) .expireAfterWrite(30, TimeUnit.SECONDS)
.build(); .build();
} }

View File

@@ -20,8 +20,8 @@ package com.velocitypowered.proxy.protocol.netty;
import static com.velocitypowered.api.event.query.ProxyQueryEvent.QueryType.BASIC; import static com.velocitypowered.api.event.query.ProxyQueryEvent.QueryType.BASIC;
import static com.velocitypowered.api.event.query.ProxyQueryEvent.QueryType.FULL; import static com.velocitypowered.api.event.query.ProxyQueryEvent.QueryType.FULL;
import com.google.common.cache.Cache; import com.github.benmanes.caffeine.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.velocitypowered.api.event.query.ProxyQueryEvent; import com.velocitypowered.api.event.query.ProxyQueryEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
@@ -73,7 +73,7 @@ public class GameSpyQueryHandler extends SimpleChannelInboundHandler<DatagramPac
"hostip" "hostip"
); );
private final Cache<InetAddress, Integer> sessions = CacheBuilder.newBuilder() private final Cache<InetAddress, Integer> sessions = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS) .expireAfterWrite(30, TimeUnit.SECONDS)
.build(); .build();
private final SecureRandom random; private final SecureRandom random;

View File

@@ -17,35 +17,33 @@
package com.velocitypowered.proxy.util.ratelimit; package com.velocitypowered.proxy.util.ratelimit;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Ticker;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Ticker;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* A simple rate-limiter based on a Guava {@link Cache}. * A simple rate-limiter based on a Caffeine {@link Cache}.
*/ */
public class GuavaCacheRatelimiter implements Ratelimiter { public class CaffeineCacheRatelimiter implements Ratelimiter {
private final Cache<InetAddress, Long> expiringCache; private final Cache<InetAddress, Long> expiringCache;
private final long timeoutNanos; private final long timeoutNanos;
GuavaCacheRatelimiter(long time, TimeUnit unit) { CaffeineCacheRatelimiter(long time, TimeUnit unit) {
this(time, unit, Ticker.systemTicker()); this(time, unit, Ticker.systemTicker());
} }
@VisibleForTesting @VisibleForTesting
GuavaCacheRatelimiter(long time, TimeUnit unit, Ticker ticker) { CaffeineCacheRatelimiter(long time, TimeUnit unit, Ticker ticker) {
Preconditions.checkNotNull(unit, "unit"); Preconditions.checkNotNull(unit, "unit");
Preconditions.checkNotNull(ticker, "ticker"); Preconditions.checkNotNull(ticker, "ticker");
this.timeoutNanos = unit.toNanos(time); this.timeoutNanos = unit.toNanos(time);
this.expiringCache = CacheBuilder.newBuilder() this.expiringCache = Caffeine.newBuilder()
.ticker(ticker) .ticker(ticker)
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
.expireAfterWrite(time, unit) .expireAfterWrite(time, unit)
.build(); .build();
} }
@@ -60,13 +58,7 @@ public class GuavaCacheRatelimiter implements Ratelimiter {
public boolean attempt(InetAddress address) { public boolean attempt(InetAddress address) {
Preconditions.checkNotNull(address, "address"); Preconditions.checkNotNull(address, "address");
long expectedNewValue = System.nanoTime() + timeoutNanos; long expectedNewValue = System.nanoTime() + timeoutNanos;
long last; long last = expiringCache.get(address, (address1) -> expectedNewValue);
try {
last = expiringCache.get(address, () -> expectedNewValue);
} catch (ExecutionException e) {
// It should be impossible for this to fail.
throw new AssertionError(e);
}
return expectedNewValue == last; return expectedNewValue == last;
} }
} }

View File

@@ -29,7 +29,7 @@ public final class Ratelimiters {
} }
public static Ratelimiter createWithMilliseconds(long ms) { public static Ratelimiter createWithMilliseconds(long ms) {
return ms <= 0 ? NoopCacheRatelimiter.INSTANCE : new GuavaCacheRatelimiter(ms, return ms <= 0 ? NoopCacheRatelimiter.INSTANCE : new CaffeineCacheRatelimiter(ms,
TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS);
} }
} }

View File

@@ -20,17 +20,17 @@ package com.velocitypowered.proxy.util.ratelimit;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import com.google.common.base.Ticker; import com.github.benmanes.caffeine.cache.Ticker;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
class GuavaCacheRatelimiterTest { class CaffeineCacheRatelimiterTest {
@Test @Test
void attemptZero() { void attemptZero() {
Ratelimiter noRatelimiter = new GuavaCacheRatelimiter(0, TimeUnit.MILLISECONDS); Ratelimiter noRatelimiter = new CaffeineCacheRatelimiter(0, TimeUnit.MILLISECONDS);
assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress())); assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress()));
assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress())); assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress()));
} }
@@ -39,13 +39,8 @@ class GuavaCacheRatelimiterTest {
void attemptOne() { void attemptOne() {
long base = System.nanoTime(); long base = System.nanoTime();
AtomicLong extra = new AtomicLong(); AtomicLong extra = new AtomicLong();
Ticker testTicker = new Ticker() { Ticker testTicker = () -> base + extra.get();
@Override Ratelimiter ratelimiter = new CaffeineCacheRatelimiter(1000, TimeUnit.MILLISECONDS, testTicker);
public long read() {
return base + extra.get();
}
};
Ratelimiter ratelimiter = new GuavaCacheRatelimiter(1000, TimeUnit.MILLISECONDS, testTicker);
assertTrue(ratelimiter.attempt(InetAddress.getLoopbackAddress())); assertTrue(ratelimiter.attempt(InetAddress.getLoopbackAddress()));
assertFalse(ratelimiter.attempt(InetAddress.getLoopbackAddress())); assertFalse(ratelimiter.attempt(InetAddress.getLoopbackAddress()));
extra.addAndGet(TimeUnit.SECONDS.toNanos(2)); extra.addAndGet(TimeUnit.SECONDS.toNanos(2));