From b36bdae5de523e46d3203a70d040b99836486f04 Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 27 Jun 2024 13:00:45 +0100 Subject: [PATCH] add guild, user and scoresaber account caching --- pom.xml | 9 +++--- .../cc/fascinated/bat/BatApplication.java | 3 +- .../bat/command/impl/BotStatsCommand.java | 15 ++++++++-- .../fascinated/bat/service/GuildService.java | 30 +++++++++++++------ .../bat/service/ScoreSaberService.java | 16 +++++++++- .../fascinated/bat/service/UserService.java | 22 ++++++++++---- 6 files changed, 71 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index caf12f6..8f1af76 100644 --- a/pom.xml +++ b/pom.xml @@ -85,10 +85,6 @@ org.springframework.boot spring-boot-starter-websocket - - org.springframework.boot - spring-boot-starter-cache - @@ -114,6 +110,11 @@ httpclient5 5.3.1 + + net.jodah + expiringmap + 0.5.11 + diff --git a/src/main/java/cc/fascinated/bat/BatApplication.java b/src/main/java/cc/fascinated/bat/BatApplication.java index 36e3b53..8782999 100644 --- a/src/main/java/cc/fascinated/bat/BatApplication.java +++ b/src/main/java/cc/fascinated/bat/BatApplication.java @@ -7,7 +7,6 @@ import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cache.annotation.EnableCaching; import org.springframework.scheduling.annotation.EnableScheduling; import java.io.File; @@ -15,7 +14,7 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.Objects; -@EnableScheduling @EnableCaching +@EnableScheduling @SpringBootApplication @Log4j2(topic = "Ember") public class BatApplication { diff --git a/src/main/java/cc/fascinated/bat/command/impl/BotStatsCommand.java b/src/main/java/cc/fascinated/bat/command/impl/BotStatsCommand.java index 79ef472..412eb9f 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/BotStatsCommand.java +++ b/src/main/java/cc/fascinated/bat/command/impl/BotStatsCommand.java @@ -5,11 +5,14 @@ import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.model.BatUser; import cc.fascinated.bat.service.DiscordService; +import cc.fascinated.bat.service.GuildService; +import cc.fascinated.bat.service.UserService; import lombok.NonNull; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** @@ -17,8 +20,14 @@ import org.springframework.stereotype.Component; */ @Component public class BotStatsCommand extends BatCommand { - public BotStatsCommand() { + private final GuildService guildService; + private final UserService userService; + + @Autowired + public BotStatsCommand(@NonNull GuildService guildService, @NonNull UserService userService) { super("botstats", "Shows the bot statistics"); + this.guildService = guildService; + this.userService = userService; } @Override @@ -29,7 +38,9 @@ public class BotStatsCommand extends BatCommand { "**Bot Statistics**\n" + "➜ Guilds: %s\n".formatted(jda.getGuilds().size()) + "➜ Users: %s\n".formatted(jda.getUsers().size()) + - "➜ Gateway Ping: %sms".formatted(jda.getGatewayPing()) + "➜ Gateway Ping: %sms\n".formatted(jda.getGatewayPing()) + + "➜ Cached Guilds: %s\n".formatted(guildService.getGuilds().size()) + + "➜ Cached Users: %s".formatted(userService.getUsers().size()) ).build()).queue(); } } diff --git a/src/main/java/cc/fascinated/bat/service/GuildService.java b/src/main/java/cc/fascinated/bat/service/GuildService.java index 6a8fe1d..cd2fa8a 100644 --- a/src/main/java/cc/fascinated/bat/service/GuildService.java +++ b/src/main/java/cc/fascinated/bat/service/GuildService.java @@ -2,25 +2,29 @@ package cc.fascinated.bat.service; import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.repository.GuildRepository; +import lombok.Getter; import lombok.NonNull; import lombok.extern.log4j.Log4j2; +import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.events.guild.GuildJoinEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CachePut; -import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; -import java.util.List; -import java.util.Optional; +import java.util.*; /** * @author Fascinated (fascinated7) */ -@Service @Log4j2 +@Service @Log4j2 @Getter @DependsOn("discordService") public class GuildService extends ListenerAdapter { + /** + * The cached guilds + */ + private final Map guilds = new HashMap<>(); + /** * The guild repository to use */ @@ -38,12 +42,17 @@ public class GuildService extends ListenerAdapter { * @param id The ID of the guild * @return The guild */ - @Cacheable(cacheNames = "guilds", key = "#id") public BatGuild getGuild(@NonNull String id) { + if (guilds.containsKey(id)) { + return guilds.get(id); + } long start = System.currentTimeMillis(); Optional optionalGuild = guildRepository.findById(id); + if (optionalGuild.isPresent()) { - return optionalGuild.get(); + BatGuild guild = optionalGuild.get(); + guilds.put(id, guild); + return guild; } BatGuild guild = guildRepository.save(new BatGuild(id)); log.info("Created guild \"{}\" in {}ms", id, System.currentTimeMillis() - start); @@ -55,7 +64,6 @@ public class GuildService extends ListenerAdapter { * * @param guild The guild to save */ - @CachePut(cacheNames = "guilds", key = "#guild.id") public void saveGuild(@NonNull BatGuild guild) { guildRepository.save(guild); } @@ -66,7 +74,11 @@ public class GuildService extends ListenerAdapter { * @return all guilds */ public List getAllGuilds() { - return guildRepository.findAll(); + List guilds = new ArrayList<>(); + for (Guild guild : DiscordService.JDA.getGuilds()) { + guilds.add(getGuild(guild.getId())); + } + return guilds; } @Override diff --git a/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java b/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java index 479012e..c4c1635 100644 --- a/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java +++ b/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java @@ -12,8 +12,8 @@ import com.google.gson.JsonObject; import lombok.NonNull; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; +import net.jodah.expiringmap.ExpiringMap; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; @@ -24,6 +24,8 @@ import org.springframework.web.socket.handler.TextWebSocketHandler; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; @Service @Log4j2(topic = "ScoreSaber Service") public class ScoreSaberService extends TextWebSocketHandler { @@ -31,6 +33,13 @@ public class ScoreSaberService extends TextWebSocketHandler { private static final String GET_PLAYER_ENDPOINT = SCORESABER_API + "player/%s/full"; private static final String GET_PLAYER_SCORES_ENDPOINT = SCORESABER_API + "player/%s/scores?limit=100&sort=%s&page=%s&withMetadata=true"; + /** + * The cached accounts. + */ + private final Map cachedAccounts = ExpiringMap.builder() + .expiration(5, TimeUnit.MINUTES) + .build(); + @Autowired public ScoreSaberService() { connectWebSocket(); @@ -45,6 +54,10 @@ public class ScoreSaberService extends TextWebSocketHandler { * @throws cc.fascinated.bat.exception.RateLimitException If the ScoreSaber rate limit is reached. */ public ScoreSaberAccountToken getAccount(String id) { + if (cachedAccounts.containsKey(id)) { + return cachedAccounts.get(id); + } + ScoreSaberAccountToken account = WebRequest.getAsEntity(String.format(GET_PLAYER_ENDPOINT, id), ScoreSaberAccountToken.class); if (account == null) { // Check if the account doesn't exist. log.info("Account with id '{}' not found.", id); @@ -56,6 +69,7 @@ public class ScoreSaberService extends TextWebSocketHandler { if (account.isInactive()) { throw new BadRequestException("Account with id '%s' is inactive.".formatted(id)); } + cachedAccounts.put(id, account); return account; } diff --git a/src/main/java/cc/fascinated/bat/service/UserService.java b/src/main/java/cc/fascinated/bat/service/UserService.java index 6630c34..18438c6 100644 --- a/src/main/java/cc/fascinated/bat/service/UserService.java +++ b/src/main/java/cc/fascinated/bat/service/UserService.java @@ -2,22 +2,28 @@ package cc.fascinated.bat.service; import cc.fascinated.bat.model.BatUser; import cc.fascinated.bat.repository.UserRepository; +import lombok.Getter; import lombok.NonNull; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CachePut; -import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; /** * @author Fascinated (fascinated7) */ -@Service @Log4j2 +@Service @Log4j2 @Getter @DependsOn("discordService") public class UserService { + /** + * The cached users + */ + private final Map users = new HashMap<>(); + /** * The user repository to use */ @@ -34,12 +40,17 @@ public class UserService { * @param id The ID of the user * @return The user */ - @Cacheable(cacheNames = "users", key = "#id") public BatUser getUser(@NonNull String id) { + if (users.containsKey(id)) { + return users.get(id); + } + long start = System.currentTimeMillis(); Optional optionalUser = userRepository.findById(id); if (optionalUser.isPresent()) { - return optionalUser.get(); + BatUser user = optionalUser.get(); + users.put(id, user); + return user; } BatUser user = userRepository.save(new BatUser(id)); log.info("Created user \"{}\" in {}ms", id, System.currentTimeMillis() - start); @@ -51,7 +62,6 @@ public class UserService { * * @param user The user to save */ - @CachePut(cacheNames = "users", key = "#user.id") public void saveUser(@NonNull BatUser user) { userRepository.save(user); }