From 95a639a7338eebdc29dcb58a1900ffdeec035faa Mon Sep 17 00:00:00 2001 From: Sheldan <5037282+Sheldan@users.noreply.github.com> Date: Wed, 26 May 2021 21:41:04 +0200 Subject: [PATCH] [AB-272] improving leaderboard performance fixing leaderboard returning wrong pages making template cache update duration longer --- .../command/LeaderBoardCommand.java | 15 ++-- .../abstracto/experience/command/Rank.java | 5 +- .../converter/LeaderBoardModelConverter.java | 68 +++++++++---------- .../service/AUserExperienceServiceBean.java | 5 +- .../UserExperienceManagementServiceBean.java | 4 +- .../command/LeaderBoardCommandTest.java | 4 +- .../experience/command/RankTest.java | 2 +- .../LeaderBoardModelConverterTest.java | 43 +++++------- .../AUserExperienceServiceBeanTest.java | 2 +- .../model/template/LeaderBoardEntryModel.java | 10 +-- .../UserExperienceManagementService.java | 6 +- .../core/service/MemberServiceBean.java | 14 ++++ .../config/FreemarkerConfiguration.java | 2 + .../core/models/database/AServer.java | 2 +- .../core/models/database/AUserInAServer.java | 4 +- .../abstracto/core/service/MemberService.java | 2 + 16 files changed, 98 insertions(+), 90 deletions(-) diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/LeaderBoardCommand.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/LeaderBoardCommand.java index cd7875362..2d4e296c8 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/LeaderBoardCommand.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/LeaderBoardCommand.java @@ -33,7 +33,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; /** * Shows the experience gain information of the top 10 users in the server, or if a page number is provided as a parameter, only the members which are on this page. @@ -69,18 +68,18 @@ public class LeaderBoardCommand extends AbstractConditionableCommand { AServer server = serverManagementService.loadServer(commandContext.getGuild()); LeaderBoard leaderBoard = userExperienceService.findLeaderBoardData(server, page); LeaderBoardModel leaderBoardModel = (LeaderBoardModel) ContextConverter.slimFromCommandContext(commandContext, LeaderBoardModel.class); - List> futures = new ArrayList<>(); - List> completableFutures = converter.fromLeaderBoard(leaderBoard); - futures.addAll(completableFutures); + List futures = new ArrayList<>(); + CompletableFuture> completableFutures = converter.fromLeaderBoard(leaderBoard); + futures.add(completableFutures); log.info("Rendering leaderboard for page {} in server {} for user {}.", page, commandContext.getAuthor().getId(), commandContext.getGuild().getId()); AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor()); LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer); - CompletableFuture userRankFuture = converter.fromLeaderBoardEntry(userRank); + CompletableFuture> userRankFuture = converter.fromLeaderBoardEntry(Arrays.asList(userRank)); futures.add(userRankFuture); - return FutureUtils.toSingleFutureGeneric(futures).thenCompose(aVoid -> { - List finalModels = completableFutures.stream().map(CompletableFuture::join).collect(Collectors.toList()); + return FutureUtils.toSingleFuture(futures).thenCompose(aVoid -> { + List finalModels = completableFutures.join(); leaderBoardModel.setUserExperiences(finalModels); - leaderBoardModel.setUserExecuting(userRankFuture.join()); + leaderBoardModel.setUserExecuting(userRankFuture.join().get(0)); MessageToSend messageToSend = templateService.renderEmbedTemplate(LEADER_BOARD_POST_EMBED_TEMPLATE, leaderBoardModel, commandContext.getGuild().getIdLong()); return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel())); }).thenApply(aVoid -> CommandResult.fromIgnored()); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/Rank.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/Rank.java index aa063050e..17d8c5aa6 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/Rank.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/Rank.java @@ -29,6 +29,7 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -70,13 +71,13 @@ public class Rank extends AbstractConditionableCommand { Member parameter = !parameters.isEmpty() ? (Member) parameters.get(0) : commandContext.getAuthor(); AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(parameter); LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer); - CompletableFuture future = converter.fromLeaderBoardEntry(userRank); + CompletableFuture> future = converter.fromLeaderBoardEntry(Arrays.asList(userRank)); RankModel rankModel = RankModel .builder() .member(parameter) .build(); return future.thenCompose(leaderBoardEntryModel -> - self.renderAndSendRank(commandContext, rankModel, leaderBoardEntryModel) + self.renderAndSendRank(commandContext, rankModel, leaderBoardEntryModel.get(0)) ).thenApply(result -> CommandResult.fromIgnored()); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverter.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverter.java index 20f72a4e2..077c1dae8 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverter.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverter.java @@ -1,6 +1,5 @@ package dev.sheldan.abstracto.experience.converter; -import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.experience.model.LeaderBoard; import dev.sheldan.abstracto.experience.model.LeaderBoardEntry; @@ -11,11 +10,14 @@ import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Member; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.stream.Collectors; /** * Converter used to convert from {@link LeaderBoard leaderBoard} to a list of {@link LeaderBoardEntryModel leaderBoardEntryModels} @@ -40,40 +42,36 @@ public class LeaderBoardModelConverter { * @return The list of {@link LeaderBoardEntryModel leaderboarEntryModels} which contain the fully fledged information provided to the * leader board template */ - public List> fromLeaderBoard(LeaderBoard leaderBoard) { - List> models = new ArrayList<>(); + public CompletableFuture> fromLeaderBoard(LeaderBoard leaderBoard) { log.debug("Converting {} entries to a list of leaderboard entries.", leaderBoard.getEntries().size()); - leaderBoard.getEntries().forEach(leaderBoardEntry -> { - CompletableFuture entry = fromLeaderBoardEntry(leaderBoardEntry); - models.add(entry); + return fromLeaderBoardEntry(leaderBoard.getEntries()); + } + + public CompletableFuture> fromLeaderBoardEntry(List leaderBoardEntries) { + List userIds = new ArrayList<>(); + Long serverId = leaderBoardEntries.get(0).getExperience().getServer().getId(); + Map models = leaderBoardEntries + .stream() + .map(leaderBoardEntry -> { + AUserExperience experience = leaderBoardEntry.getExperience(); + Long userId = experience.getUser().getUserReference().getId(); + userIds.add(userId); + return LeaderBoardEntryModel + .builder() + .userId(userId) + .experience(experience.getExperience()) + .messageCount(experience.getMessageCount()) + .level(experience.getLevelOrDefault()) + .rank(leaderBoardEntry.getRank()) + .build(); + }) + .collect(Collectors.toMap(LeaderBoardEntryModel::getUserId, Function.identity())); + return memberService.getMembersInServerAsync(serverId, userIds).thenApply(members -> { + members.forEach(member -> models.get(member.getIdLong()).setMember(member)); + return new ArrayList<>(models.values()) + .stream() + .sorted(Comparator.comparing(LeaderBoardEntryModel::getRank)). + collect(Collectors.toList()); }); - return models; - } - - /** - * Converts the given {@link LeaderBoardEntry entry} to a {@link LeaderBoardEntryModel model}, which provides a reference to the - * {@link Member member} object of the given {@link AUserInAServer user} for convenience in the template - * @param leaderBoardEntry The {@link LeaderBoardEntry entry} to be converted - * @return The {@link LeaderBoardEntryModel model} accompanied with the {@link Member member} reference, might be null, if the - * user left the guild - */ - public CompletableFuture fromLeaderBoardEntry(LeaderBoardEntry leaderBoardEntry) { - AUserInAServer entryUser = leaderBoardEntry.getExperience().getUser(); - Long userInServerId = leaderBoardEntry.getExperience().getUser().getUserInServerId(); - Integer rank = leaderBoardEntry.getRank(); - return memberService.getMemberInServerAsync(entryUser.getServerReference().getId(), entryUser.getUserReference().getId()) - .thenApply(member -> self.buildLeaderBoardModel(userInServerId, member, rank)) - .exceptionally(throwable -> self.buildLeaderBoardModel(userInServerId, null, rank)); - } - - @Transactional - public LeaderBoardEntryModel buildLeaderBoardModel(Long userInServerId, Member member, Integer rank) { - AUserExperience experience = userExperienceManagementService.findByUserInServerId(userInServerId); - return LeaderBoardEntryModel - .builder() - .experience(experience) - .member(member) - .rank(rank) - .build(); } } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java index aec2ac998..ba477804f 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java @@ -548,12 +548,13 @@ public class AUserExperienceServiceBean implements AUserExperienceService { page--; int pageSize = 10; log.debug("Loading leaderboard page {} for server {}.", page, server.getId()); - List experiences = userExperienceManagementService.findLeaderBoardUsersPaginated(server, page * pageSize, (page + 1) * pageSize); + List experiences = userExperienceManagementService.findLeaderBoardUsersPaginated(server, page, pageSize); List entries = new ArrayList<>(); log.debug("Found {} experiences.", experiences.size()); + int pageOffset = page * pageSize; for (int i = 0; i < experiences.size(); i++) { AUserExperience userExperience = experiences.get(i); - entries.add(LeaderBoardEntry.builder().experience(userExperience).rank((page * pageSize) + i + 1).build()); + entries.add(LeaderBoardEntry.builder().experience(userExperience).rank(pageOffset + i + 1).build()); } return LeaderBoard.builder().entries(entries).build(); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java index 50eab3e63..332ac4226 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java @@ -64,8 +64,8 @@ public class UserExperienceManagementServiceBean implements UserExperienceManage } @Override - public List findLeaderBoardUsersPaginated(AServer aServer, Integer start, Integer end) { - return repository.findTop10ByUser_ServerReferenceOrderByExperienceDesc(aServer, PageRequest.of(start, end)); + public List findLeaderBoardUsersPaginated(AServer aServer, Integer page, Integer size) { + return repository.findTop10ByUser_ServerReferenceOrderByExperienceDesc(aServer, PageRequest.of(page, size)); } @Override diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/command/LeaderBoardCommandTest.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/command/LeaderBoardCommandTest.java index d30b2b8a2..7e3d4130c 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/command/LeaderBoardCommandTest.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/command/LeaderBoardCommandTest.java @@ -74,11 +74,11 @@ public class LeaderBoardCommandTest { AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class); when(userInServerManagementService.loadOrCreateUser(context.getAuthor())).thenReturn(userInAServer); when(userExperienceService.findLeaderBoardData(server, expectedPage)).thenReturn(leaderBoard); - when(converter.fromLeaderBoard(leaderBoard)).thenReturn(new ArrayList<>()); + when(converter.fromLeaderBoard(leaderBoard)).thenReturn(CompletableFuture.completedFuture(null)); LeaderBoardEntry executingUserRank = Mockito.mock(LeaderBoardEntry.class); when(userExperienceService.getRankOfUserInServer(userInAServer)).thenReturn(executingUserRank); LeaderBoardEntryModel leaderBoardEntryModel = Mockito.mock(LeaderBoardEntryModel.class); - when(converter.fromLeaderBoardEntry(executingUserRank)).thenReturn(CompletableFuture.completedFuture(leaderBoardEntryModel)); + when(converter.fromLeaderBoardEntry(Arrays.asList(executingUserRank))).thenReturn(CompletableFuture.completedFuture(Arrays.asList(leaderBoardEntryModel))); MessageToSend messageToSend = Mockito.mock(MessageToSend.class); when(templateService.renderEmbedTemplate(eq(LeaderBoardCommand.LEADER_BOARD_POST_EMBED_TEMPLATE), any(LeaderBoardModel.class), eq(SERVER_ID))).thenReturn(messageToSend); CompletableFuture result = testUnit.executeAsync(context); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/command/RankTest.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/command/RankTest.java index 93abfdb10..5907aead4 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/command/RankTest.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/command/RankTest.java @@ -73,7 +73,7 @@ public class RankTest { when(userInServerManagementService.loadOrCreateUser(context.getAuthor())).thenReturn(aUserInAServer); when(userExperienceService.getRankOfUserInServer(aUserInAServer)).thenReturn(leaderBoardEntry); LeaderBoardEntryModel leaderBoardEntryModel = Mockito.mock(LeaderBoardEntryModel.class); - when(converter.fromLeaderBoardEntry(leaderBoardEntry)).thenReturn(CompletableFuture.completedFuture(leaderBoardEntryModel)); + when(converter.fromLeaderBoardEntry(Arrays.asList(leaderBoardEntry))).thenReturn(CompletableFuture.completedFuture(Arrays.asList(leaderBoardEntryModel))); when(self.renderAndSendRank(eq(context), any(RankModel.class), eq(leaderBoardEntryModel))).thenReturn(CompletableFuture.completedFuture(null)); CompletableFuture result = testUnit.executeAsync(context); CommandTestUtilities.checkSuccessfulCompletionAsync(result); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverterTest.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverterTest.java index 0085d2789..e083b943d 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverterTest.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverterTest.java @@ -6,9 +6,11 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.experience.model.LeaderBoard; import dev.sheldan.abstracto.experience.model.LeaderBoardEntry; +import dev.sheldan.abstracto.experience.model.database.AExperienceLevel; import dev.sheldan.abstracto.experience.model.database.AUserExperience; import dev.sheldan.abstracto.experience.model.template.LeaderBoardEntryModel; import net.dv8tion.jda.api.entities.Member; +import org.hibernate.event.spi.ClearEventListener; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +42,9 @@ public class LeaderBoardModelConverterTest { private static final Long USER_ID_2 = 6L; private static final Long USER_IN_SERVER_ID = 7L; private static final Long USER_IN_SERVER_ID_2 = 8L; + private static final Long EXPERIENCE = 9L; + private static final Long MESSAGES = 10L; + private static final Integer LEVEL = 54; @Test public void testFromLeaderBoard() { @@ -47,49 +52,33 @@ public class LeaderBoardModelConverterTest { LeaderBoardEntry entry = getEntry(firstRank, USER_ID, USER_IN_SERVER_ID); Integer secondRank = 2; - LeaderBoardEntryModel firstEntryModel = Mockito.mock(LeaderBoardEntryModel.class); - LeaderBoardEntryModel secondEntryModel = Mockito.mock(LeaderBoardEntryModel.class); LeaderBoardEntry entry2 = getEntry(secondRank, USER_ID_2, USER_IN_SERVER_ID_2); List entries = Arrays.asList(entry, entry2); LeaderBoard leaderBoard = Mockito.mock(LeaderBoard.class); when(leaderBoard.getEntries()).thenReturn(entries); Member member = Mockito.mock(Member.class); - when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(member)); - when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID_2)).thenReturn(CompletableFuture.completedFuture(member)); - when(self.buildLeaderBoardModel(USER_IN_SERVER_ID, member, firstRank)).thenReturn(firstEntryModel); - when(self.buildLeaderBoardModel(USER_IN_SERVER_ID_2, member, secondRank)).thenReturn(secondEntryModel); - List> leaderBoardEntryModels = testUnit.fromLeaderBoard(leaderBoard); - LeaderBoardEntryModel firstEntry = leaderBoardEntryModels.get(0).join(); - Assert.assertEquals(firstEntryModel, firstEntry); - LeaderBoardEntryModel secondEntry = leaderBoardEntryModels.get(1).join(); - Assert.assertEquals(secondEntryModel, secondEntry); - Assert.assertEquals(entries.size(), leaderBoardEntryModels.size()); - } - - @Test - public void testFromEntry() { - Integer rank = 2; - LeaderBoardEntry entry = getEntry(rank, USER_ID, USER_IN_SERVER_ID); - Member member = Mockito.mock(Member.class); - LeaderBoardEntryModel entryModelMock = Mockito.mock(LeaderBoardEntryModel.class); - when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(member)); - when(self.buildLeaderBoardModel(USER_IN_SERVER_ID, member, rank)).thenReturn(entryModelMock); - CompletableFuture leaderBoardEntryModel = testUnit.fromLeaderBoardEntry(entry); - LeaderBoardEntryModel entryModel = leaderBoardEntryModel.join(); - Assert.assertEquals(entryModelMock, entryModel); + when(member.getIdLong()).thenReturn(USER_ID); + when(memberService.getMembersInServerAsync(SERVER_ID, Arrays.asList(USER_ID, USER_ID_2))).thenReturn(CompletableFuture.completedFuture(Arrays.asList(member))); + CompletableFuture> leaderBoardEntryModels = testUnit.fromLeaderBoard(leaderBoard); + LeaderBoardEntryModel firstEntry = leaderBoardEntryModels.join().get(0); + Assert.assertEquals(USER_ID, firstEntry.getUserId()); + LeaderBoardEntryModel secondEntry = leaderBoardEntryModels.join().get(1); + Assert.assertEquals(USER_ID_2, secondEntry.getUserId()); + Assert.assertEquals(entries.size(), leaderBoardEntryModels.join().size()); } private LeaderBoardEntry getEntry(Integer rank, Long userId, Long userInServerId) { AUserExperience experience = Mockito.mock(AUserExperience.class); + when(experience.getMessageCount()).thenReturn(MESSAGES); + when(experience.getExperience()).thenReturn(EXPERIENCE); AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class); when(experience.getUser()).thenReturn(userInAServer); AUser user = Mockito.mock(AUser.class); when(userInAServer.getUserReference()).thenReturn(user); - when(userInAServer.getUserInServerId()).thenReturn(userInServerId); when(user.getId()).thenReturn(userId); AServer server = Mockito.mock(AServer.class); + when(experience.getServer()).thenReturn(server); when(server.getId()).thenReturn(SERVER_ID); - when(userInAServer.getServerReference()).thenReturn(server); LeaderBoardEntry entry = Mockito.mock(LeaderBoardEntry.class); when(entry.getRank()).thenReturn(rank); when(entry.getExperience()).thenReturn(experience); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBeanTest.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBeanTest.java index b4c885d36..0af2f0620 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBeanTest.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/test/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBeanTest.java @@ -759,7 +759,7 @@ public class AUserExperienceServiceBeanTest { when(userExperience2.getExperience()).thenReturn(MID_EXP); when(userExperience2.getCurrentLevel()).thenReturn(level1); when(userExperience2.getUser()).thenReturn(aUserInAServer2); - when(userExperienceManagementService.findLeaderBoardUsersPaginated(server, (page - 1) * pageSize, page * pageSize)).thenReturn(experiences); + when(userExperienceManagementService.findLeaderBoardUsersPaginated(server, page - 1, pageSize)).thenReturn(experiences); LeaderBoard leaderBoardData = testUnit.findLeaderBoardData(server, page); page--; List entries = leaderBoardData.getEntries(); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LeaderBoardEntryModel.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LeaderBoardEntryModel.java index 816e5d4e1..dab042dc5 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LeaderBoardEntryModel.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LeaderBoardEntryModel.java @@ -3,6 +3,7 @@ package dev.sheldan.abstracto.experience.model.template; import dev.sheldan.abstracto.experience.model.database.AUserExperience; import lombok.Builder; import lombok.Getter; +import lombok.Setter; import net.dv8tion.jda.api.entities.Member; import java.io.Serializable; @@ -15,13 +16,14 @@ import java.io.Serializable; @Getter @Builder public class LeaderBoardEntryModel implements Serializable { - /** - * The {@link AUserExperience experience} for this particular user in the server - */ - private AUserExperience experience; + private Long experience; + private Long messageCount; + private Long userId; + private Integer level; /** * The {@link Member member} associated wit this user experience, might be null if the user left he server. */ + @Setter private transient Member member; /** * The position this {@link dev.sheldan.abstracto.core.models.database.AUserInAServer user} in this server has, ordered by experience {@link AUserExperience#experience} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java index e6947916b..6498bd96e 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java @@ -52,11 +52,11 @@ public interface UserExperienceManagementService { /** * Retrieves a list of {@link AUserExperience} ordered by {@link AUserExperience#experience} and only returns the positions between {@code start} and @{code end}. * @param server The {@link AServer} to retrieve the users for - * @param start The start index in the complete ordered list to return the {@link AUserExperience} elements for - * @param end The end index for which to return a sublist of {@link AUserExperience} elements for + * @param page The page to retrieve + * @param size The size of each page * @return A list desc ordered by {@link AUserExperience#experience} only containing the elements between {@code start} and @{code end} */ - List findLeaderBoardUsersPaginated(AServer server, Integer start, Integer end); + List findLeaderBoardUsersPaginated(AServer server, Integer page, Integer size); /** * Returns the {@link LeaderBoardEntryResult} of the given {@link AUserExperience}. diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MemberServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MemberServiceBean.java index 1416baf08..d592ea2f4 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MemberServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MemberServiceBean.java @@ -15,6 +15,7 @@ import net.dv8tion.jda.api.entities.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -79,6 +80,19 @@ public class MemberServiceBean implements MemberService { } } + @Override + public CompletableFuture> getMembersInServerAsync(Long serverId, List memberIds) { + log.debug("Retrieving member {} in server {} from cache.", memberIds, serverId); + Guild guildById = guildService.getGuildById(serverId); + CompletableFuture> future = new CompletableFuture<>(); + if(guildById != null) { + guildById.retrieveMembersByIds(memberIds).onSuccess(future::complete).onError(future::completeExceptionally); + } else { + throw new GuildNotFoundException(serverId); + } + return future; + } + @Override public CompletableFuture retrieveMemberInServer(ServerUser serverUser) { return getMemberInServerAsync(serverUser.getServerId(), serverUser.getUserId()); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/templating/config/FreemarkerConfiguration.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/templating/config/FreemarkerConfiguration.java index 67a5600a1..1fc30d15a 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/templating/config/FreemarkerConfiguration.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/templating/config/FreemarkerConfiguration.java @@ -49,6 +49,8 @@ public class FreemarkerConfiguration { Configuration configuration = factory.createConfiguration(); configuration.setSharedVariable("fmtDuration", durationMethod); configuration.setSharedVariable("formatDate", instantMethod); + // 10 minutes template cache + configuration.setTemplateUpdateDelayMilliseconds(600000); List macrosToLoad = macroManagementService.loadAllMacros().stream() .map(AutoLoadMacro::getKey).collect(Collectors.toList()); configuration.setAutoIncludes(macrosToLoad); diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java index a43624ea0..6f8f5729a 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java @@ -36,7 +36,7 @@ public class AServer implements SnowFlake, Serializable { @Transient private boolean fake; - @OneToOne(mappedBy = "server", cascade = CascadeType.ALL) + @OneToOne(mappedBy = "server", cascade = CascadeType.ALL, fetch = FetchType.LAZY) @PrimaryKeyJoinColumn private AllowedMention allowedMention; diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java index 553fe1451..354ead6ba 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java @@ -21,11 +21,11 @@ public class AUserInAServer implements Serializable { @Column(name = "user_in_server_id", nullable = false) private Long userInServerId; - @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private AUser userReference; - @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY) @JoinColumn(name = "server_id", nullable = false) private AServer serverReference; diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MemberService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MemberService.java index 22111b9ae..09b92f766 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MemberService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MemberService.java @@ -9,6 +9,7 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; +import java.util.List; import java.util.concurrent.CompletableFuture; public interface MemberService { @@ -16,6 +17,7 @@ public interface MemberService { CompletableFuture getServerChannelUserAsync(Long serverId, Long channelId, Long userId); Member getMemberInServer(Long serverId, Long memberId); CompletableFuture getMemberInServerAsync(Long serverId, Long memberId); + CompletableFuture> getMembersInServerAsync(Long serverId, List memberIds); CompletableFuture retrieveMemberInServer(ServerUser serverUser); CompletableFuture retrieveUserById(Long userId); boolean isUserInGuild(AUserInAServer aUserInAServer);