package com.dbg.cloud.acheron.plugins.ratelimiting.service; import com.dbg.cloud.acheron.plugins.ratelimiting.RateLimit; import com.dbg.cloud.acheron.plugins.ratelimiting.store.RateLimitStore; import lombok.AllArgsConstructor; import lombok.NonNull; import org.springframework.stereotype.Service; import java.util.Optional; import java.util.UUID; @Service @AllArgsConstructor final class RateLimitServiceImpl implements RateLimitService { private final RateLimitStore rateLimitStore; @Override public RateLimit consumeRate(final @NonNull String routeId, final @NonNull Long limitRequestsPerWindow, final int windowInSeconds) { // Route limits final long currentRequestsInWindow = rateLimitStore.countConsumptionOfRoute(routeId); final long remainingInWindow = limitRequestsPerWindow - currentRequestsInWindow; if (remainingInWindow > 0) { // Register request rateLimitStore.addConsumptionToRoute(routeId, windowInSeconds); } // Calculate reset time final Optional<Long> earliestEntry = rateLimitStore.retrieveTimeOfEarliestConsumptionOfRoute(routeId); final long reset = calculateResetTime(earliestEntry, windowInSeconds); return new RateLimit(limitRequestsPerWindow, Math.max(remainingInWindow - 1, 0), windowInSeconds, reset); } @Override public RateLimit consumeRate(final String routeId, final @NonNull UUID consumerId, final @NonNull Long limitRequestsPerWindow, final int windowInSeconds) { // Consumer limits final long currentRequestsInWindow = rateLimitStore.countConsumptionOfConsumer(routeId, consumerId); final long remainingInWindow = limitRequestsPerWindow - currentRequestsInWindow; if (remainingInWindow > 0) { // Register request rateLimitStore.addConsumptionToConsumer(routeId, consumerId, windowInSeconds); } // Calculate reset time final Optional<Long> earliestEntry = rateLimitStore.retrieveTimeOfEarliestConsumptionOfConsumer( routeId, consumerId); final long reset = calculateResetTime(earliestEntry, windowInSeconds); return new RateLimit(limitRequestsPerWindow, Math.max(remainingInWindow - 1, 0), windowInSeconds, Math.abs(reset)); } private long calculateResetTime(final Optional<Long> earliestEntry, int windowInSeconds) { final long reset; if (earliestEntry.isPresent()) { final long elapsed = System.currentTimeMillis() - earliestEntry.get(); reset = Math.max((windowInSeconds * 1000) - elapsed, 0); } else { reset = 0; } double resetInSec = Math.ceil(reset / 1000.0); return (long) resetInSec; } }