[AB-209] adding cleanup job for old embedded messages, this job deletes the entries from the database and removes reactions

reducing thread count for listener executor and scheduling
fixing channel deletion listener not being part of a transaction
This commit is contained in:
Sheldan
2021-04-06 20:23:26 +02:00
parent 61d16d5985
commit e7b1fbe9d1
21 changed files with 280 additions and 10 deletions

View File

@@ -38,6 +38,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>metrics-int</artifactId>

View File

@@ -0,0 +1,31 @@
package dev.sheldan.abstracto.linkembed.job;
import dev.sheldan.abstracto.linkembed.service.MessageEmbedService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
@Slf4j
@DisallowConcurrentExecution
@Component
@PersistJobDataAfterExecution
public class LinkEmbedCleanupJob extends QuartzJobBean {
@Autowired
private MessageEmbedService messageEmbedService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
log.info("Executing link embed clean up job.");
messageEmbedService.cleanUpOldMessageEmbeds();
} catch (Exception exception) {
log.error("Link embed cleanup job failed.", exception);
}
}
}

View File

@@ -4,7 +4,12 @@ package dev.sheldan.abstracto.linkembed.repository;
import dev.sheldan.abstracto.linkembed.model.database.EmbeddedMessage;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.Instant;
import java.util.List;
public interface EmbeddedMessageRepository extends JpaRepository<EmbeddedMessage, Long> {
EmbeddedMessage findByEmbeddingMessageId(Long messageId);
List<EmbeddedMessage> findByCreatedLessThan(Instant date);
void deleteByEmbeddingMessageIdIn(List<Long> embeddedMessageId);
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.linkembed.service;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.listener.MessageEmbeddedModel;
@@ -9,22 +10,31 @@ import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.linkembed.model.MessageEmbedLink;
import dev.sheldan.abstracto.linkembed.model.database.EmbeddedMessage;
import dev.sheldan.abstracto.linkembed.service.management.MessageEmbedPostManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Component
@Slf4j
@@ -68,6 +78,15 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
@Autowired
private ReactionService reactionService;
@Autowired
private MessageService messageService;
@Autowired
private EmoteService emoteService;
@Value("${abstracto.feature.linkEmbed.removalDays}")
private Long embedRemovalDays;
@Override
public List<MessageEmbedLink> getLinksInMessage(String message) {
List<MessageEmbedLink> links = new ArrayList<>();
@@ -97,7 +116,6 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
linksToEmbed.forEach(messageEmbedLink ->
messageCache.getMessageFromCache(messageEmbedLink.getServerId(), messageEmbedLink.getChannelId(), messageEmbedLink.getMessageId())
.thenAccept(cachedMessage -> self.embedLink(cachedMessage, target, userEmbeddingUserInServerId, embeddingMessage)
).exceptionally(throwable -> {
log.error("Message embedding from cache failed for message {}.", messageEmbedLink.getMessageId(), throwable);
return null;
@@ -113,6 +131,60 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
);
}
@Override
public CompletableFuture<Void> cleanUpOldMessageEmbeds() {
Instant oldestDate = Instant.now().truncatedTo(ChronoUnit.DAYS).minus(embedRemovalDays, ChronoUnit.DAYS);
List<EmbeddedMessage> embeddedMessages = messageEmbedPostManagementService.getEmbeddedMessagesOlderThan(oldestDate);
if(embeddedMessages.isEmpty()) {
log.info("No embedded messages to clean up.");
return CompletableFuture.completedFuture(null);
}
log.info("Cleaning up {} embedded embeddedMessages", embeddedMessages.size());
List<ServerChannelMessage> serverChannelMessages = embeddedMessages.stream().map(embeddedMessage ->
ServerChannelMessage
.builder()
.serverId(embeddedMessage.getEmbeddedServer().getId())
.channelId(embeddedMessage.getEmbeddedChannel().getId())
.messageId(embeddedMessage.getEmbeddingMessageId())
.build()
)
.collect(Collectors.toList());
List<Long> embeddedMessagesHandled = embeddedMessages
.stream()
.map(EmbeddedMessage::getEmbeddingMessageId)
.collect(Collectors.toList());
List<CompletableFuture<Message>> messageFutures = messageService.retrieveMessages(serverChannelMessages);
CompletableFutureList<Message> future = new CompletableFutureList<>(messageFutures);
return future.getMainFuture()
.handle((unused, throwable) -> self.removeReactions(future.getObjects()))
.thenCompose(Function.identity())
// deleting the messages from db regardless of exceptions, at most the reaction remains
.whenComplete((unused, throwable) -> self.deleteEmbeddedMessages(embeddedMessagesHandled))
.exceptionally(throwable -> {
log.error("Failed to clean up embedded messages.", throwable);
return null;
});
}
@Transactional
public CompletableFuture<Void> removeReactions(List<Message> allMessages) {
List<CompletableFuture<Void>> removalFutures = new ArrayList<>();
Map<Long, List<Message>> groupedPerServer = allMessages
.stream()
.collect(Collectors.groupingBy(message -> message.getGuild().getIdLong()));
groupedPerServer.forEach((serverId, serverMessages) -> {
// we assume the emote remained the same
CompletableFutureList<Void> removalFuture = reactionService.removeReactionFromMessagesWithFutureWithFutureList(serverMessages, REMOVAL_EMOTE);
removalFutures.add(removalFuture.getMainFuture());
});
return FutureUtils.toSingleFutureGeneric(removalFutures);
}
@Transactional
public void deleteEmbeddedMessages(List<Long> embeddedMessagesToDelete) {
messageEmbedPostManagementService.deleteEmbeddedMessagesViaId(embeddedMessagesToDelete);
}
@Transactional
public CompletableFuture<Void> sendEmbeddingMessage(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, MessageEmbeddedModel messageEmbeddedModel) {
MessageToSend embed = templateService.renderEmbedTemplate(MESSAGE_EMBED_TEMPLATE, messageEmbeddedModel, target.getGuild().getIdLong());

View File

@@ -16,6 +16,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
@Component
@@ -76,4 +78,15 @@ public class MessageEmbedPostManagementServiceBean implements MessageEmbedPostMa
embeddedMessageRepository.delete(embeddedMessage);
}
@Override
public List<EmbeddedMessage> getEmbeddedMessagesOlderThan(Instant date) {
return embeddedMessageRepository.findByCreatedLessThan(date);
}
@Override
public void deleteEmbeddedMessagesViaId(List<Long> embeddingMessageId) {
log.info("Deleting {} embedded messages from db.", embeddingMessageId.size());
embeddedMessageRepository.deleteByEmbeddingMessageIdIn(embeddingMessageId);
}
}

View File

@@ -1,2 +1,5 @@
abstracto.featureFlags.linkEmbeds.featureName=linkEmbeds
abstracto.featureFlags.linkEmbeds.enabled=false
abstracto.feature.linkEmbed.removalDays=1

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../dbchangelog-3.8.xsd" >
<include file="link-embed-seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="linkEmbedCleanupJob.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,19 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="linkEmbed-cleanup-insert">
<insert tableName="scheduler_job">
<column name="name" value="linkEmbedCleanupJob"/>
<column name="group_name" value="linkEmbed"/>
<column name="clazz" value="dev.sheldan.abstracto.linkembed.job.LinkEmbedCleanupJob"/>
<column name="active" value="true"/>
<column name="cron_expression" value="0 0 0 * * ?"/>
<column name="recovery" value="false"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -7,4 +7,5 @@
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog-3.8.xsd" >
<include file="1.0-link-embed/collection.xml" relativeToChangelogFile="true"/>
<include file="1.2.8-link-embed/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -12,4 +12,5 @@ public interface MessageEmbedService {
List<MessageEmbedLink> getLinksInMessage(String message);
void embedLinks(List<MessageEmbedLink> linksToEmbed, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage);
CompletableFuture<Void> embedLink(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage);
CompletableFuture<Void> cleanUpOldMessageEmbeds();
}

View File

@@ -5,10 +5,14 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.linkembed.model.database.EmbeddedMessage;
import net.dv8tion.jda.api.entities.Message;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
public interface MessageEmbedPostManagementService {
void createMessageEmbed(CachedMessage embeddedMessage, Message messageContainingEmbed, AUserInAServer cause);
Optional<EmbeddedMessage> findEmbeddedPostByMessageId(Long messageId);
void deleteEmbeddedMessage(EmbeddedMessage embeddedMessage);
List<EmbeddedMessage> getEmbeddedMessagesOlderThan(Instant date);
void deleteEmbeddedMessagesViaId(List<Long> embeddingMessageId);
}

View File

@@ -27,7 +27,7 @@ public class ReminderJob extends QuartzJobBean {
log.info("Executing reminder job for reminder {}", reminderId);
reminderService.executeReminder(reminderId);
} catch (Exception e) {
log.error("Reminder job failed to execute.", e);
log.error("Reminder job failed.", e);
}
}

View File

@@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;
import javax.annotation.Nonnull;
@@ -32,8 +33,16 @@ public class AsyncAChannelDeletedListenerBean extends ListenerAdapter {
@Qualifier("aChannelDeletedExecutor")
private TaskExecutor channelDeletedExecutor;
@Autowired
private AsyncAChannelDeletedListenerBean self;
@Override
public void onTextChannelDelete(@Nonnull TextChannelDeleteEvent event) {
self.deleteChannelInDb(event);
}
@Transactional
public void deleteChannelInDb(TextChannelDeleteEvent event) {
channelManagementService.markAsDeleted(event.getChannel().getIdLong());
}

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
@@ -18,8 +19,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static dev.sheldan.abstracto.core.config.MetricConstants.DISCORD_API_INTERACTION_METRIC;
import static dev.sheldan.abstracto.core.config.MetricConstants.INTERACTION_TYPE;
@@ -40,6 +45,9 @@ public class MessageServiceBean implements MessageService {
@Autowired
private MetricService metricService;
@Autowired
private GuildService guildService;
public static final CounterMetric MESSAGE_SEND_METRIC = CounterMetric
.builder()
.name(DISCORD_API_INTERACTION_METRIC)
@@ -110,6 +118,26 @@ public class MessageServiceBean implements MessageService {
.thenCompose(o -> channelService.sendTextToChannel(text, o));
}
@Override
public List<CompletableFuture<Message>> retrieveMessages(List<ServerChannelMessage> messages) {
List<CompletableFuture<Message>> messageFutures = new ArrayList<>();
Map<Long, List<ServerChannelMessage>> serverMessages = messages
.stream()
.collect(Collectors.groupingBy(ServerChannelMessage::getServerId));
serverMessages.forEach((serverId, channelMessagesNonGrouped) -> {
Guild guild = guildService.getGuildById(serverId);
Map<Long, List<ServerChannelMessage>> channelMessages = channelMessagesNonGrouped
.stream()
.collect(Collectors.groupingBy(ServerChannelMessage::getChannelId));
channelMessages.forEach((channelId, serverChannelMessages) -> {
MessageChannel channel = guild.getTextChannelById(channelId);
serverChannelMessages.forEach(serverChannelMessage ->
messageFutures.add(channelService.retrieveMessageInChannel(channel, serverChannelMessage.getMessageId())));
});
});
return messageFutures;
}
@Override
public CompletableFuture<Message> sendTemplateToUser(User user, String template, Object model) {
String message = templateService.renderTemplate(template, model);

View File

@@ -11,6 +11,7 @@ import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
@@ -316,6 +317,15 @@ public class ReactionServiceBean implements ReactionService {
);
}
@Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId) {
Integer emoteId = emote.getId();
CompletableFuture<Message> messageFuture = channelService.retrieveMessageInChannel(serverId, channelId, messageId);
return messageFuture.thenCompose(message ->
self.removeReactionFromMessageWithFuture(emoteId, message)
);
}
@Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Member member) {
Integer emoteId = emote.getId();
@@ -374,6 +384,44 @@ public class ReactionServiceBean implements ReactionService {
return futures;
}
@Override
public List<CompletableFuture<Void>> removeReactionFromMessagesWithFuture(List<Message> messages, Integer emoteId) {
AEmote emote = emoteManagementService.loadEmote(emoteId);
return removeReactionFromMessagesWithFuture(messages, emote);
}
@Override
public List<CompletableFuture<Void>> removeReactionFromMessagesWithFuture(List<Message> messages, AEmote emote) {
List<CompletableFuture<Void>> removalFutures = new ArrayList<>();
messages.forEach(message -> removalFutures.add(removeReactionFromMessageWithFuture(emote, message)));
return removalFutures;
}
@Override
public CompletableFutureList<Void> removeReactionFromMessagesWithFutureWithFutureList(List<Message> messages, Integer emoteId) {
List<CompletableFuture<Void>> allFutures = removeReactionFromMessagesWithFuture(messages, emoteId);
return new CompletableFutureList<>(allFutures);
}
@Override
public CompletableFutureList<Void> removeReactionFromMessagesWithFutureWithFutureList(List<Message> messages, String emoteKey) {
List<CompletableFuture<Void>> allFutures = removeReactionFromMessagesWithFuture(messages, emoteKey);
return new CompletableFutureList<>(allFutures);
}
@Override
public List<CompletableFuture<Void>> removeReactionFromMessagesWithFuture(List<Message> messages, String emoteKey) {
AEmote emote = emoteService.getEmoteOrDefaultEmote(emoteKey, messages.get(0).getGuild().getIdLong());
return removeReactionFromMessagesWithFuture(messages, emote);
}
@Override
public CompletableFutureList<Void> removeReactionFromMessagesWithFutureWithFutureList(List<Message> messages, AEmote emote) {
List<CompletableFuture<Void>> removalFutures = new ArrayList<>();
messages.forEach(message -> removalFutures.add(removeReactionFromMessageWithFuture(emote, message)));
return new CompletableFutureList<>(removalFutures);
}
@PostConstruct
public void postConstruct() {
metricService.registerCounter(REACTION_ADDED_METRIC, "Reactions added");

View File

@@ -1,8 +1,3 @@
abstracto.listener.default.maxPoolSize=5
abstracto.listener.default.maxPoolSize=2
abstracto.listener.default.corePoolSize=1
abstracto.listener.default.keepAliveSeconds=60
abstracto.listener.joinListener.maxPoolSize=5
abstracto.listener.joinListener.corePoolSize=1
abstracto.listener.joinListener.keepAliveSeconds=60

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
@@ -11,6 +12,7 @@ import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
import net.dv8tion.jda.api.requests.restaction.MessageAction;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public interface MessageService {
@@ -22,6 +24,7 @@ public interface MessageService {
void updateStatusMessage(MessageChannel channel, Long messageId, MessageToSend messageToSend);
CompletableFuture<Message> sendMessageToUser(AUserInAServer userInAServer, String text);
CompletableFuture<Message> sendSimpleTemplateToUser(Long userId, String templateKey);
List<CompletableFuture<Message>> retrieveMessages(List<ServerChannelMessage> messages);
CompletableFuture<Message> sendTemplateToUser(User user, String template, Object model);
CompletableFuture<Void> sendEmbedToUser(User user, String template, Object model);
CompletableFuture<Message> sendEmbedToUserWithMessage(User user, String template, Object model);

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import net.dv8tion.jda.api.entities.*;
import java.util.List;
@@ -42,6 +43,7 @@ public interface ReactionService {
CompletableFuture<Void> clearReactionFromMessageWithFuture(Integer emoteId, Message message);
CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Long userId);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Member member);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Member member);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Member member);
@@ -49,4 +51,10 @@ public interface ReactionService {
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Long userId);
CompletableFuture<Void> clearReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
List<CompletableFuture<Void>> addReactionsToMessageWithFuture(List<String> emoteKeys, Long serverId, Message message);
List<CompletableFuture<Void>> removeReactionFromMessagesWithFuture(List<Message> messages, Integer emoteId);
List<CompletableFuture<Void>> removeReactionFromMessagesWithFuture(List<Message> messages, AEmote emote);
CompletableFutureList<Void> removeReactionFromMessagesWithFutureWithFutureList(List<Message> messages, Integer emoteId);
CompletableFutureList<Void> removeReactionFromMessagesWithFutureWithFutureList(List<Message> messages, String emoteKey);
List<CompletableFuture<Void>> removeReactionFromMessagesWithFuture(List<Message> messages, String emoteKey);
CompletableFutureList<Void> removeReactionFromMessagesWithFutureWithFutureList(List<Message> messages, AEmote emote);
}

View File

@@ -40,7 +40,11 @@ public class CompletableFutureList<T> {
if(!future.isCompletedExceptionally()) {
result.add(future.join());
} else {
log.warn("Future completed with exception {}.", future.join());
try {
future.join();
} catch (Exception exception) {
log.warn("Future completed with exception.", exception);
}
}
});
return result;

View File

@@ -5,7 +5,7 @@ spring.quartz.jdbc.initialize-schema=never
spring.quartz.properties.org.quartz.scheduler.instanceName=quartz-abstracto-app
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
spring.quartz.properties.org.quartz.scheduler.instanceIdGenerator.class=dev.sheldan.abstracto.scheduling.service.IdGenerationService
spring.quartz.properties.org.quartz.threadPool.threadCount=20
spring.quartz.properties.org.quartz.threadPool.threadCount=3
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
spring.quartz.properties.org.quartz.jobStore.useProperties=true