mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-09 02:44:00 +00:00
[AB-10] attempt to fix the race condition when creating a starboard post
fixes #10
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package dev.sheldan.abstracto.starboard.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
|
||||
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
|
||||
@@ -42,7 +43,12 @@ public class StarAddedListener extends StarboardListener implements AsyncReactio
|
||||
log.info("User {} in server {} reacted with star to put a message {} from channel {} on starboard.",
|
||||
model.getUserReacting().getUserId(), model.getServerId(), model.getMessage().getMessageId(), model.getMessage().getChannelId());
|
||||
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(model.getMessage(), aEmote);
|
||||
handleStarboardPostChange(model.getMessage(), reactionOptional.orElse(null), model.getUserReacting(), true);
|
||||
try {
|
||||
handleStarboardPostChange(model.getMessage(), reactionOptional.orElse(null), model.getUserReacting(), true);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Starboard post change failed in guild {} for message {} in channel {}", serverId, model.getMessage().getChannelId(), model.getMessage().getMessageId(), e);
|
||||
throw new AbstractoRunTimeException(e);
|
||||
}
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
} else {
|
||||
return DefaultListenerResult.IGNORED;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.sheldan.abstracto.starboard.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionRemovedListener;
|
||||
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
|
||||
@@ -43,7 +44,12 @@ public class StarRemovedListener extends StarboardListener implements AsyncReact
|
||||
log.info("User {} in server {} removed star reaction from message {} on starboard.",
|
||||
userRemoving.getUserId(), model.getServerId(), model.getMessage().getMessageId());
|
||||
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(model.getMessage(), aEmote);
|
||||
handleStarboardPostChange(model.getMessage(), reactionOptional.orElse(null), userRemoving, false);
|
||||
try {
|
||||
handleStarboardPostChange(model.getMessage(), reactionOptional.orElse(null), userRemoving, false);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Starboard post change failed in guild {} for message {} in channel {}", guildId, model.getMessage().getChannelId(), model.getMessage().getMessageId(), e);
|
||||
throw new AbstractoRunTimeException(e);
|
||||
}
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
} else {
|
||||
return DefaultListenerResult.IGNORED;
|
||||
|
||||
@@ -9,6 +9,7 @@ import dev.sheldan.abstracto.core.models.cache.CachedReactions;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.core.service.EmoteService;
|
||||
import dev.sheldan.abstracto.core.service.LockService;
|
||||
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.starboard.config.StarboardFeatureConfig;
|
||||
@@ -27,6 +28,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@@ -63,6 +65,8 @@ public abstract class StarboardListener {
|
||||
public static final String STARBOARD_POSTS = "starboard.posts";
|
||||
public static final String STAR_ACTION = "action";
|
||||
|
||||
public static final Semaphore STARBOARD_SEMAPHORE = new Semaphore(1, true);
|
||||
|
||||
protected static final CounterMetric STARBOARD_STARS_THRESHOLD_REACHED = CounterMetric
|
||||
.builder()
|
||||
.name(STARBOARD_POSTS)
|
||||
@@ -75,16 +79,16 @@ public abstract class StarboardListener {
|
||||
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "threshold.below")))
|
||||
.build();
|
||||
|
||||
protected void handleStarboardPostChange(CachedMessage message, CachedReactions reaction, ServerUser userReacting, boolean adding) {
|
||||
protected void handleStarboardPostChange(CachedMessage message, CachedReactions reaction, ServerUser userReacting, boolean adding) throws InterruptedException {
|
||||
Long starMaxDays = configService.getLongValueOrConfigDefault(StarboardFeatureConfig.STAR_MAX_DAYS_CONFIG_KEY, message.getServerId());
|
||||
if(message.getTimeCreated().isBefore(Instant.now().minus(starMaxDays, ChronoUnit.DAYS))) {
|
||||
log.info("Post {} in channel {} in guild {} is beyond the configured max day amount of {} - ignoring.", message.getMessageId(), message.getChannelId(), message.getServerId(), starMaxDays);
|
||||
return;
|
||||
}
|
||||
Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(message.getMessageId());
|
||||
boolean starboardPostExists = starboardPostOptional.isPresent();
|
||||
if(starboardPostExists && starboardPostOptional.get().isIgnored()) {
|
||||
log.info("Starboard post {} for message {} in server {} is ignored. Doing nothing.", starboardPostOptional.get().getId(), message.getMessageId(), message.getServerId());
|
||||
Optional<StarboardPost> firstOptional = starboardPostManagementService.findByMessageId(message.getMessageId());
|
||||
boolean starboardPostExists = firstOptional.isPresent();
|
||||
if(starboardPostExists && firstOptional.get().isIgnored()) {
|
||||
log.info("Starboard post {} for message {} in server {} is ignored. Doing nothing.", firstOptional.get().getId(), message.getMessageId(), message.getServerId());
|
||||
return;
|
||||
}
|
||||
if(reaction != null) {
|
||||
@@ -93,29 +97,42 @@ public abstract class StarboardListener {
|
||||
Long starMinimum = getFromConfig(FIRST_LEVEL_THRESHOLD_KEY, message.getServerId());
|
||||
AUserInAServer userAddingReaction = userInServerManagementService.loadOrCreateUser(userReacting);
|
||||
if (userExceptAuthor.size() >= starMinimum) {
|
||||
log.info("Post reached starboard minimum. Message {} in channel {} in server {} will be starred/updated.",
|
||||
message.getMessageId(), message.getChannelId(), message.getServerId());
|
||||
if(starboardPostExists) {
|
||||
updateStarboardPost(message, userAddingReaction, adding, starboardPostOptional.get(), userExceptAuthor);
|
||||
} else {
|
||||
metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_REACHED);
|
||||
log.info("Creating starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId());
|
||||
starboardService.createStarboardPost(message, userExceptAuthor, userAddingReaction, author).exceptionally(throwable -> {
|
||||
log.error("Failed to persist starboard post for message {}.", message.getMessageId(), throwable);
|
||||
return null;
|
||||
});
|
||||
try {
|
||||
STARBOARD_SEMAPHORE.acquire();
|
||||
Optional<StarboardPost> secondOptional = starboardPostManagementService.findByMessageId(message.getMessageId());
|
||||
log.info("Post reached starboard minimum. Message {} in channel {} in server {} will be starred/updated.",
|
||||
message.getMessageId(), message.getChannelId(), message.getServerId());
|
||||
if(secondOptional.isPresent()) {
|
||||
updateStarboardPost(message, userAddingReaction, adding, secondOptional.get(), userExceptAuthor);
|
||||
STARBOARD_SEMAPHORE.release();
|
||||
} else {
|
||||
metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_REACHED);
|
||||
log.info("Creating starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId());
|
||||
starboardService.createStarboardPost(message, userExceptAuthor, userAddingReaction, author).exceptionally(throwable -> {
|
||||
log.error("Failed to persist starboard post for message {}.", message.getMessageId(), throwable);
|
||||
STARBOARD_SEMAPHORE.release();
|
||||
return null;
|
||||
}).thenAccept(unused -> {
|
||||
log.info("Releasing starboard post lock for message {}.", message.getMessageId());
|
||||
STARBOARD_SEMAPHORE.release();
|
||||
});
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
log.error("Failed to create starboard post for message {}.", message.getMessageId(), throwable);
|
||||
STARBOARD_SEMAPHORE.release();
|
||||
throw throwable;
|
||||
}
|
||||
} else {
|
||||
if(starboardPostExists) {
|
||||
metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_FELL);
|
||||
log.info("Removing starboard post for message {} in channel {} in server {}. It fell under the threshold {}", message.getMessageId(), message.getChannelId(), message.getServerId(), starMinimum);
|
||||
starboardPostOptional.ifPresent(starboardPost -> completelyRemoveStarboardPost(starboardPost, userReacting));
|
||||
firstOptional.ifPresent(starboardPost -> completelyRemoveStarboardPost(starboardPost, userReacting));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(starboardPostExists) {
|
||||
log.info("Removing starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId());
|
||||
starboardPostOptional.ifPresent(starboardPost -> completelyRemoveStarboardPost(starboardPost, userReacting));
|
||||
firstOptional.ifPresent(starboardPost -> completelyRemoveStarboardPost(starboardPost, userReacting));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user