[AB-352] adding caching to the loading of members for warnings/user notes/mutes. this becomes relevant as members which left the guild are not cached by jda, which means, we retry it again

improved models for templates to simplify the objects passed
This commit is contained in:
Sheldan
2021-12-24 23:41:27 +01:00
parent 9230a13218
commit ee01a3f07c
8 changed files with 133 additions and 77 deletions

View File

@@ -1,8 +1,9 @@
package dev.sheldan.abstracto.moderation.converter; package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.FutureMemberPair; import dev.sheldan.abstracto.core.models.FutureMemberPair;
import dev.sheldan.abstracto.core.models.MemberDisplayModel;
import dev.sheldan.abstracto.core.models.ServerSpecificId; import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.core.utils.FutureUtils;
@@ -36,17 +37,32 @@ public class MuteEntryConverter {
public CompletableFuture<List<MuteEntry>> fromMutes(List<Mute> mutes) { public CompletableFuture<List<MuteEntry>> fromMutes(List<Mute> mutes) {
Map<ServerSpecificId, FutureMemberPair> loadedMutes = new HashMap<>(); Map<ServerSpecificId, FutureMemberPair> loadedMutes = new HashMap<>();
List<CompletableFuture<Member>> allFutures = new ArrayList<>(); List<CompletableFuture<Member>> allFutures = new ArrayList<>();
Map<Long, CompletableFuture<Member>> memberCaching = new HashMap<>();
mutes.forEach(mute -> { mutes.forEach(mute -> {
CompletableFuture<Member> mutingMemberFuture = memberService.getMemberInServerAsync(mute.getMutingUser()); AUserInAServer mutingUser = mute.getMutingUser();
CompletableFuture<Member> mutedMemberFuture = memberService.getMemberInServerAsync(mute.getMutedUser()); AUserInAServer mutedUser = mute.getMutedUser();
CompletableFuture<Member> mutedFuture;
if(memberCaching.containsKey(mutedUser.getUserInServerId())) {
mutedFuture = memberCaching.get(mutedUser.getUserInServerId());
} else {
mutedFuture = memberService.getMemberInServerAsync(mutedUser);
memberCaching.put(mutedUser.getUserInServerId(), mutedFuture);
}
CompletableFuture<Member> mutingFuture;
if(memberCaching.containsKey(mutingUser.getUserInServerId())) {
mutingFuture = memberCaching.get(mutingUser.getUserInServerId());
} else {
mutingFuture = memberService.getMemberInServerAsync(mutingUser);
memberCaching.put(mutingUser.getUserInServerId(), mutingFuture);
}
FutureMemberPair futurePair = FutureMemberPair FutureMemberPair futurePair = FutureMemberPair
.builder() .builder()
.firstMember(mutingMemberFuture) .firstMember(mutingFuture)
.secondMember(mutedMemberFuture) .secondMember(mutedFuture)
.build(); .build();
loadedMutes.put(mute.getMuteId(), futurePair); loadedMutes.put(mute.getMuteId(), futurePair);
allFutures.add(mutingMemberFuture); allFutures.add(mutingFuture);
allFutures.add(mutedMemberFuture); allFutures.add(mutedFuture);
}); });
CompletableFuture<List<MuteEntry>> future = new CompletableFuture<>(); CompletableFuture<List<MuteEntry>> future = new CompletableFuture<>();
FutureUtils.toSingleFutureGeneric(allFutures) FutureUtils.toSingleFutureGeneric(allFutures)
@@ -67,23 +83,28 @@ public class MuteEntryConverter {
FutureMemberPair memberPair = loadedMuteInfo.get(muteInfo); FutureMemberPair memberPair = loadedMuteInfo.get(muteInfo);
Mute mute = muteManagementService.findMute(muteInfo.getId(), muteInfo.getServerId()); Mute mute = muteManagementService.findMute(muteInfo.getId(), muteInfo.getServerId());
Member mutedMember = !memberPair.getSecondMember().isCompletedExceptionally() ? memberPair.getSecondMember().join() : null; Member mutedMember = !memberPair.getSecondMember().isCompletedExceptionally() ? memberPair.getSecondMember().join() : null;
MemberDisplayModel mutedUser = MemberDisplayModel MemberDisplay mutedUser = MemberDisplay
.builder() .builder()
.member(mutedMember) .memberMention(mutedMember != null ? mutedMember.getAsMention() : null)
.userId(mute.getMutedUser().getUserReference().getId()) .userId(mute.getMutedUser().getUserReference().getId())
.serverId(mute.getServer().getId())
.build(); .build();
Member mutingMember = !memberPair.getFirstMember().isCompletedExceptionally() ? memberPair.getFirstMember().join() : null; Member mutingMember = !memberPair.getFirstMember().isCompletedExceptionally() ? memberPair.getFirstMember().join() : null;
MemberDisplayModel mutingUser = MemberDisplayModel MemberDisplay mutingUser = MemberDisplay
.builder() .builder()
.member(mutingMember) .memberMention(mutingMember != null ? mutingMember.getAsMention() : null)
.userId(mute.getMutingUser().getUserReference().getId()) .userId(mute.getMutingUser().getUserReference().getId())
.build(); .build();
MuteEntry entry = MuteEntry MuteEntry entry = MuteEntry
.builder() .builder()
.mutedUser(mutedUser) .mutedUser(mutedUser)
.mutingUser(mutingUser) .mutingUser(mutingUser)
.mute(mute) .muteId(mute.getMuteId().getId())
.serverId(mute.getMuteId().getServerId())
.reason(mute.getReason())
.muteDate(mute.getMuteDate())
.muteEnded(mute.getMuteEnded())
.muteDuration(Duration.between(mute.getMuteDate(), mute.getMuteTargetDate())) .muteDuration(Duration.between(mute.getMuteDate(), mute.getMuteTargetDate()))
.build(); .build();
entries.add(entry); entries.add(entry);

View File

@@ -1,7 +1,8 @@
package dev.sheldan.abstracto.moderation.converter; package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.ServerSpecificId; import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.core.utils.FutureUtils;
@@ -37,18 +38,30 @@ public class UserNotesConverter {
public CompletableFuture<List<NoteEntryModel>> fromNotes(List<UserNote> userNotes){ public CompletableFuture<List<NoteEntryModel>> fromNotes(List<UserNote> userNotes){
List<CompletableFuture<Member>> memberFutures = new ArrayList<>(); List<CompletableFuture<Member>> memberFutures = new ArrayList<>();
HashMap<ServerSpecificId, CompletableFuture<Member>> noteMemberMap = new HashMap<>(); HashMap<ServerSpecificId, CompletableFuture<Member>> noteMemberMap = new HashMap<>();
Map<Long, CompletableFuture<Member>> memberCaching = new HashMap<>();
userNotes.forEach(userNote -> { userNotes.forEach(userNote -> {
CompletableFuture<Member> memberFuture = memberService.getMemberInServerAsync(userNote.getUser()); AUserInAServer noteUser = userNote.getUser();
memberFutures.add(memberFuture); CompletableFuture<Member> noteFuture;
noteMemberMap.put(userNote.getUserNoteId(), memberFuture); if(memberCaching.containsKey(noteUser.getUserInServerId())) {
noteFuture = memberCaching.get(noteUser.getUserInServerId());
} else {
noteFuture = memberService.getMemberInServerAsync(noteUser);
memberCaching.put(noteUser.getUserInServerId(), noteFuture);
}
memberFutures.add(noteFuture);
noteMemberMap.put(userNote.getUserNoteId(), noteFuture);
}); });
if(userNotes.isEmpty()) { if(userNotes.isEmpty()) {
memberFutures.add(CompletableFuture.completedFuture(null)); memberFutures.add(CompletableFuture.completedFuture(null));
} }
CompletableFuture<List<NoteEntryModel>> future = new CompletableFuture<>();
return FutureUtils.toSingleFutureGeneric(memberFutures).thenApply(aVoid -> FutureUtils.toSingleFutureGeneric(memberFutures)
self.loadFullNotes(noteMemberMap) .whenComplete((unused, throwable) -> future.complete(self.loadFullNotes(noteMemberMap)))
); .exceptionally(throwable -> {
future.completeExceptionally(throwable);
return null;
});
return future;
} }
@Transactional @Transactional
@@ -58,15 +71,19 @@ public class UserNotesConverter {
CompletableFuture<Member> memberFuture = futureHashMap.get(serverSpecificId); CompletableFuture<Member> memberFuture = futureHashMap.get(serverSpecificId);
Member member = !memberFuture.isCompletedExceptionally() ? memberFuture.join() : null; Member member = !memberFuture.isCompletedExceptionally() ? memberFuture.join() : null;
UserNote note = userNoteManagementService.loadNote(serverSpecificId.getServerId(), serverSpecificId.getId()); UserNote note = userNoteManagementService.loadNote(serverSpecificId.getServerId(), serverSpecificId.getId());
FullUserInServer fullUser = FullUserInServer MemberDisplay display = MemberDisplay
.builder() .builder()
.member(member) .userId(note.getUser().getUserReference().getId())
.aUserInAServer(note.getUser()) .serverId(note.getServer().getId())
.memberMention(member != null ? member.getAsMention() : null)
.build(); .build();
NoteEntryModel entryModel = NoteEntryModel NoteEntryModel entryModel = NoteEntryModel
.builder() .builder()
.note(note) .member(display)
.fullUser(fullUser) .serverId(serverSpecificId.getServerId())
.note(note.getNote())
.noteId(note.getUserNoteId().getId())
.created(note.getCreated())
.build(); .build();
entryModels.add(entryModel); entryModels.add(entryModel);
}); });

View File

@@ -1,8 +1,9 @@
package dev.sheldan.abstracto.moderation.converter; package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.FutureMemberPair; import dev.sheldan.abstracto.core.models.FutureMemberPair;
import dev.sheldan.abstracto.core.models.MemberDisplayModel;
import dev.sheldan.abstracto.core.models.ServerSpecificId; import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.core.utils.FutureUtils;
@@ -35,21 +36,36 @@ public class WarnEntryConverter {
public CompletableFuture<List<WarnEntry>> fromWarnings(List<Warning> warnings) { public CompletableFuture<List<WarnEntry>> fromWarnings(List<Warning> warnings) {
Map<ServerSpecificId, FutureMemberPair> loadedWarnings = new HashMap<>(); Map<ServerSpecificId, FutureMemberPair> loadedWarnings = new HashMap<>();
List<CompletableFuture<Member>> allFutures = new ArrayList<>(); List<CompletableFuture<Member>> allFutures = new ArrayList<>();
Map<Long, CompletableFuture<Member>> memberCaching = new HashMap<>();
warnings.forEach(warning -> { warnings.forEach(warning -> {
CompletableFuture<Member> warningMemberFuture = memberService.getMemberInServerAsync(warning.getWarningUser()); AUserInAServer warningUser = warning.getWarningUser();
CompletableFuture<Member> warnedMemberFuture = memberService.getMemberInServerAsync(warning.getWarnedUser()); AUserInAServer warnedUser = warning.getWarnedUser();
FutureMemberPair futurePair = FutureMemberPair.builder().firstMember(warningMemberFuture).secondMember(warnedMemberFuture).build(); CompletableFuture<Member> warnedFuture;
if(memberCaching.containsKey(warnedUser.getUserInServerId())) {
warnedFuture = memberCaching.get(warnedUser.getUserInServerId());
} else {
warnedFuture = memberService.getMemberInServerAsync(warnedUser);
memberCaching.put(warnedUser.getUserInServerId(), warnedFuture);
}
CompletableFuture<Member> warningFuture;
if(memberCaching.containsKey(warningUser.getUserInServerId())) {
warningFuture = memberCaching.get(warningUser.getUserInServerId());
} else {
warningFuture = memberService.getMemberInServerAsync(warningUser);
memberCaching.put(warningUser.getUserInServerId(), warningFuture);
}
FutureMemberPair futurePair = FutureMemberPair.builder().firstMember(warningFuture).secondMember(warnedFuture).build();
loadedWarnings.put(warning.getWarnId(), futurePair); loadedWarnings.put(warning.getWarnId(), futurePair);
allFutures.add(warningMemberFuture); allFutures.add(warningFuture);
allFutures.add(warnedMemberFuture); allFutures.add(warnedFuture);
}); });
CompletableFuture<List<WarnEntry>> future = new CompletableFuture<>(); CompletableFuture<List<WarnEntry>> future = new CompletableFuture<>();
FutureUtils.toSingleFutureGeneric(allFutures) FutureUtils.toSingleFutureGeneric(allFutures)
.whenComplete((unused, throwable) -> future.complete(self.loadFullWarnEntries(loadedWarnings))) .whenComplete((unused, throwable) -> future.complete(self.loadFullWarnEntries(loadedWarnings)))
.exceptionally(throwable -> { .exceptionally(throwable -> {
future.completeExceptionally(throwable); future.completeExceptionally(throwable);
return null; return null;
}); });
return future; return future;
} }
@@ -62,23 +78,28 @@ public class WarnEntryConverter {
Warning warn = warnManagementService.findById(warning.getId(), warning.getServerId()); Warning warn = warnManagementService.findById(warning.getId(), warning.getServerId());
FutureMemberPair memberPair = loadedWarnInfo.get(warning); FutureMemberPair memberPair = loadedWarnInfo.get(warning);
Member warnedMember = !memberPair.getSecondMember().isCompletedExceptionally() ? memberPair.getSecondMember().join() : null; Member warnedMember = !memberPair.getSecondMember().isCompletedExceptionally() ? memberPair.getSecondMember().join() : null;
MemberDisplayModel warnedUser = MemberDisplayModel MemberDisplay warnedUser = MemberDisplay
.builder() .builder()
.member(warnedMember) .memberMention(warnedMember != null ? warnedMember.getAsMention() : null)
.userId(warn.getWarnedUser().getUserReference().getId()) .userId(warn.getWarnedUser().getUserReference().getId())
.build(); .build();
Member warningMember = !memberPair.getFirstMember().isCompletedExceptionally() ? memberPair.getFirstMember().join() : null; Member warningMember = !memberPair.getFirstMember().isCompletedExceptionally() ? memberPair.getFirstMember().join() : null;
MemberDisplayModel warningUser = MemberDisplayModel MemberDisplay warningUser = MemberDisplay
.builder() .builder()
.member(warningMember) .memberMention(warningMember != null ? warningMember.getAsMention() : null)
.userId(warn.getWarningUser().getUserReference().getId()) .userId(warn.getWarningUser().getUserReference().getId())
.build(); .build();
WarnEntry entry = WarnEntry WarnEntry entry = WarnEntry
.builder() .builder()
.warnedUser(warnedUser) .warnedUser(warnedUser)
.warningUser(warningUser) .warningUser(warningUser)
.warning(warn) .reason(warn.getReason())
.decayDate(warn.getDecayDate())
.decayed(warn.getDecayed())
.warnId(warn.getWarnId().getId())
.warnDate(warn.getWarnDate())
.serverId(warn.getWarnId().getServerId())
.build(); .build();
entries.add(entry); entries.add(entry);
}); });

View File

@@ -82,11 +82,9 @@ public class UserNotesConverterTest {
List<NoteEntryModel> models = testUnit.loadFullNotes(map); List<NoteEntryModel> models = testUnit.loadFullNotes(map);
Assert.assertEquals(2, models.size()); Assert.assertEquals(2, models.size());
NoteEntryModel firstEntry = models.get(0); NoteEntryModel firstEntry = models.get(0);
Assert.assertEquals(member, firstEntry.getFullUser().getMember()); Assert.assertEquals(member, firstEntry.getMember());
Assert.assertEquals(userInAServer, firstEntry.getFullUser().getAUserInAServer());
NoteEntryModel secondEntry = models.get(1); NoteEntryModel secondEntry = models.get(1);
Assert.assertEquals(member, secondEntry.getFullUser().getMember()); Assert.assertEquals(member, secondEntry.getMember());
Assert.assertEquals(userInAServer, secondEntry.getFullUser().getAUserInAServer());
} }
} }

View File

@@ -111,18 +111,14 @@ public class WarnEntryConverterTest {
List<WarnEntry> models = testUnit.loadFullWarnEntries(map); List<WarnEntry> models = testUnit.loadFullWarnEntries(map);
Assert.assertEquals(2, models.size()); Assert.assertEquals(2, models.size());
WarnEntry firstEntry = models.get(0); WarnEntry firstEntry = models.get(0);
Assert.assertEquals(warningMember, firstEntry.getWarningUser().getMember());
Assert.assertEquals(warnedMember, firstEntry.getWarnedUser().getMember());
Assert.assertEquals(USER_ID_1, firstEntry.getWarnedUser().getUserId()); Assert.assertEquals(USER_ID_1, firstEntry.getWarnedUser().getUserId());
Assert.assertEquals(USER_ID_2, firstEntry.getWarningUser().getUserId()); Assert.assertEquals(USER_ID_2, firstEntry.getWarningUser().getUserId());
Assert.assertEquals(WARN_ID_1, firstEntry.getWarning().getWarnId().getId()); Assert.assertEquals(WARN_ID_1, firstEntry.getWarnId());
Assert.assertEquals(SERVER_ID, firstEntry.getWarning().getWarnId().getServerId()); Assert.assertEquals(SERVER_ID, firstEntry.getServerId());
WarnEntry secondEntry = models.get(1); WarnEntry secondEntry = models.get(1);
Assert.assertEquals(warningMember, secondEntry.getWarningUser().getMember());
Assert.assertEquals(warnedMember, secondEntry.getWarnedUser().getMember());
Assert.assertEquals(USER_ID_1, secondEntry.getWarnedUser().getUserId()); Assert.assertEquals(USER_ID_1, secondEntry.getWarnedUser().getUserId());
Assert.assertEquals(USER_ID_2, secondEntry.getWarningUser().getUserId()); Assert.assertEquals(USER_ID_2, secondEntry.getWarningUser().getUserId());
Assert.assertEquals(WARN_ID_2, secondEntry.getWarning().getWarnId().getId()); Assert.assertEquals(WARN_ID_2, secondEntry.getWarnId());
Assert.assertEquals(SERVER_ID, secondEntry.getWarning().getWarnId().getServerId()); Assert.assertEquals(SERVER_ID, secondEntry.getServerId());
} }
} }

View File

@@ -1,28 +1,23 @@
package dev.sheldan.abstracto.moderation.model.template.command; package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.MemberDisplayModel; import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.moderation.model.database.Mute;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.time.Duration; import java.time.Duration;
import java.time.Instant;
@Getter @Getter
@Setter @Setter
@Builder @Builder
public class MuteEntry { public class MuteEntry {
/** private Long muteId;
* The {@link Mute} of this entry private Long serverId;
*/ private String reason;
private Mute mute; private Instant muteDate;
/** private Boolean muteEnded;
* The {@link MemberDisplayModel} containing information about the user being muted. The member property is null if the user left the server private MemberDisplay mutedUser;
*/ private MemberDisplay mutingUser;
private MemberDisplayModel mutedUser;
/**
* The {@link MemberDisplayModel} containing information about the user muting. The member property is null if the user left the server
*/
private MemberDisplayModel mutingUser;
private Duration muteDuration; private Duration muteDuration;
} }

View File

@@ -1,15 +1,19 @@
package dev.sheldan.abstracto.moderation.model.template.command; package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.FullUserInServer; import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.moderation.model.database.UserNote;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.time.Instant;
@Getter @Getter
@Setter @Setter
@Builder @Builder
public class NoteEntryModel { public class NoteEntryModel {
private UserNote note; private String note;
private FullUserInServer fullUser; private Long noteId;
private Instant created;
private MemberDisplay member;
private Long serverId;
} }

View File

@@ -1,11 +1,13 @@
package dev.sheldan.abstracto.moderation.model.template.command; package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.MemberDisplayModel; import dev.sheldan.abstracto.core.models.MemberDisplayModel;
import dev.sheldan.abstracto.moderation.model.database.Warning; import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.time.Instant;
/** /**
* A single warning containing the full user instead of only the warning object when the warnings command is executed. * A single warning containing the full user instead of only the warning object when the warnings command is executed.
* The template is: "warnings_warn_entry" * The template is: "warnings_warn_entry"
@@ -14,16 +16,18 @@ import lombok.Setter;
@Setter @Setter
@Builder @Builder
public class WarnEntry { public class WarnEntry {
/** private String reason;
* The {@link Warning} of this entry private Long warnId;
*/ private Long serverId;
private Warning warning; private Boolean decayed;
private Instant warnDate;
private Instant decayDate;
/** /**
* The {@link MemberDisplayModel} containing information about the user being warned. The member property is null if the user left the server * The {@link MemberDisplayModel} containing information about the user being warned. The member property is null if the user left the server
*/ */
private MemberDisplayModel warnedUser; private MemberDisplay warnedUser;
/** /**
* The {@link MemberDisplayModel} containing information about the user warning. The member property is null if the user left the server * The {@link MemberDisplayModel} containing information about the user warning. The member property is null if the user left the server
*/ */
private MemberDisplayModel warningUser; private MemberDisplay warningUser;
} }