feat(search): add NlSearchRateLimiter with Bucket4j/Caffeine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package org.raddatz.familienarchiv.search;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import io.github.bucket4j.Bandwidth;
|
||||
import io.github.bucket4j.Bucket;
|
||||
import org.raddatz.familienarchiv.exception.DomainException;
|
||||
import org.raddatz.familienarchiv.exception.ErrorCode;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
public class NlSearchRateLimiter {
|
||||
|
||||
private final LoadingCache<String, Bucket> byUser;
|
||||
private final int maxRequestsPerMinute;
|
||||
|
||||
public NlSearchRateLimiter(NlSearchRateLimitProperties props) {
|
||||
this.maxRequestsPerMinute = props.getMaxRequestsPerMinute();
|
||||
this.byUser = Caffeine.newBuilder()
|
||||
.expireAfterAccess(1, TimeUnit.MINUTES)
|
||||
.build(key -> newBucket(maxRequestsPerMinute));
|
||||
}
|
||||
|
||||
public void checkAndConsume(String userKey) {
|
||||
if (!byUser.get(userKey).tryConsume(1)) {
|
||||
throw DomainException.tooManyRequests(ErrorCode.SMART_SEARCH_RATE_LIMITED,
|
||||
"NL search rate limit exceeded for user: " + userKey, 60L);
|
||||
}
|
||||
}
|
||||
|
||||
void resetForTest() {
|
||||
byUser.invalidateAll();
|
||||
}
|
||||
|
||||
private static Bucket newBucket(int limit) {
|
||||
return Bucket.builder()
|
||||
.addLimit(Bandwidth.builder()
|
||||
.capacity(limit)
|
||||
.refillGreedy(limit, Duration.ofMinutes(1))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user