diff --git a/src/main/java/com/deahtstroke/rivenbot/config/BungieConfiguration.java b/src/main/java/com/deahtstroke/rivenbot/config/BungieConfiguration.java index fd59236..7589606 100644 --- a/src/main/java/com/deahtstroke/rivenbot/config/BungieConfiguration.java +++ b/src/main/java/com/deahtstroke/rivenbot/config/BungieConfiguration.java @@ -108,8 +108,11 @@ public BungieClient defaultBungieClient(WebClient.Builder builder) { @Bean public WebClient pgcrWebClient(WebClient.Builder builder) { // Don't keep alive connections with Bungie.net + HttpClient httpClient = HttpClient.create() + .keepAlive(false); return builder .baseUrl(this.statsBaseUrl) + .clientConnector(new ReactorClientHttpConnector(httpClient)) .defaultHeader(API_KEY_HEADER_NAME, this.key) .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/java/com/deahtstroke/rivenbot/handler/ApplicationCommandSource.java b/src/main/java/com/deahtstroke/rivenbot/handler/ApplicationCommandSource.java index 49110bd..8063384 100644 --- a/src/main/java/com/deahtstroke/rivenbot/handler/ApplicationCommandSource.java +++ b/src/main/java/com/deahtstroke/rivenbot/handler/ApplicationCommandSource.java @@ -8,6 +8,7 @@ * Implementation of this interface are responsible for creating the human-readable message that * will be sent through Discord to respond to a particular slash-command */ +@FunctionalInterface public interface ApplicationCommandSource { /** diff --git a/src/main/java/com/deahtstroke/rivenbot/service/PGCRService.java b/src/main/java/com/deahtstroke/rivenbot/service/PGCRService.java index 5aa9d65..da2c497 100644 --- a/src/main/java/com/deahtstroke/rivenbot/service/PGCRService.java +++ b/src/main/java/com/deahtstroke/rivenbot/service/PGCRService.java @@ -1,5 +1,6 @@ package com.deahtstroke.rivenbot.service; +import com.deahtstroke.rivenbot.config.BungieConfiguration; import com.deahtstroke.rivenbot.dto.destiny.BungieResponse; import com.deahtstroke.rivenbot.dto.destiny.PostGameCarnageReport; import com.deahtstroke.rivenbot.entity.PGCRDetails; @@ -8,8 +9,10 @@ import com.deahtstroke.rivenbot.repository.PGCRRepository; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import io.github.resilience4j.reactor.ratelimiter.operator.RateLimiterOperator; import java.util.concurrent.atomic.AtomicInteger; import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.BodyExtractors; @@ -24,7 +27,7 @@ public class PGCRService { private static final String PGCR_ENDPOINT_URL = "/Destiny2/Stats/PostGameCarnageReport/{activityId}/"; - private static final Integer PGCR_SIZE_LIMIT_BYTES = 16_000; + private static final Integer PGCR_SIZE_LIMIT_BYTES = 32_000; private final WebClient webClient; private final PGCRMapper pgcrMapper; @@ -62,7 +65,7 @@ public Mono retrievePGCR(Long activityInstanceId) { clientResponse -> clientResponse.body(BodyExtractors.toDataBuffers())) .concatMap(dataBuffer -> { int chunkSize = dataBuffer.readableByteCount(); - if (chunkSize + currentSize.get() > PGCR_SIZE_LIMIT_BYTES) { + if (dataBuffer.readableByteCount() + currentSize.get() > PGCR_SIZE_LIMIT_BYTES) { return Mono.error(new PGCRSizeLimitException( "PGCR with Id [%s] exceeded the size limit of 16 KBs".formatted( activityInstanceId))); @@ -71,6 +74,7 @@ public Mono retrievePGCR(Long activityInstanceId) { return Mono.just(dataBuffer); } }) + .transformDeferred(RateLimiterOperator.of(BungieConfiguration.PGCR_RATE_LIMITER)) .collectList() .flatMap(buffers -> DataBufferUtils.join(Flux.fromIterable(buffers))) .flatMap(buffer -> { @@ -86,9 +90,9 @@ public Mono retrievePGCR(Long activityInstanceId) { .onErrorResume(PGCRSizeLimitException.class, ex -> Mono.just( BungieResponse.of(PostGameCarnageReport.EMPTY_RESPONSE))) .flatMap(response -> pgcrRepository.save( - pgcrMapper.dtoToEntity(response.getResponse(), activityInstanceId))); + pgcrMapper.dtoToEntity(response.getResponse(), activityInstanceId))) + .doOnDiscard(DataBuffer.class, DataBufferUtils::release); } }); } - }