From 0dec0e37886ce35edd500767f454d321059605ce Mon Sep 17 00:00:00 2001 From: Athou Date: Wed, 3 Jul 2024 14:16:09 +0200 Subject: [PATCH] fix a race condition where a feed could be refreshed before it was created --- .../backend/dao/FeedSubscriptionDAO.java | 30 +++++++++++++++++++ .../service/FeedSubscriptionService.java | 19 ++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/commafeed-server/src/main/java/com/commafeed/backend/dao/FeedSubscriptionDAO.java b/commafeed-server/src/main/java/com/commafeed/backend/dao/FeedSubscriptionDAO.java index 32122507..26ea8cd0 100644 --- a/commafeed-server/src/main/java/com/commafeed/backend/dao/FeedSubscriptionDAO.java +++ b/commafeed-server/src/main/java/com/commafeed/backend/dao/FeedSubscriptionDAO.java @@ -2,9 +2,16 @@ package com.commafeed.backend.dao; import java.util.List; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; import org.hibernate.SessionFactory; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.event.service.spi.EventListenerRegistry; +import org.hibernate.event.spi.EventType; +import org.hibernate.event.spi.PostInsertEvent; +import org.hibernate.event.spi.PostInsertEventListener; +import org.hibernate.persister.entity.EntityPersister; import com.commafeed.backend.model.AbstractModel; import com.commafeed.backend.model.Feed; @@ -24,9 +31,32 @@ public class FeedSubscriptionDAO extends GenericDAO { private final QFeedSubscription sub = QFeedSubscription.feedSubscription; + private final SessionFactory sessionFactory; + @Inject public FeedSubscriptionDAO(SessionFactory sessionFactory) { super(sessionFactory); + this.sessionFactory = sessionFactory; + } + + public void onPostCommitInsert(Consumer consumer) { + sessionFactory.unwrap(SessionFactoryImplementor.class) + .getServiceRegistry() + .getService(EventListenerRegistry.class) + .getEventListenerGroup(EventType.POST_COMMIT_INSERT) + .appendListener(new PostInsertEventListener() { + @Override + public void onPostInsert(PostInsertEvent event) { + if (event.getEntity() instanceof FeedSubscription s) { + consumer.accept(s); + } + } + + @Override + public boolean requiresPostCommitHandling(EntityPersister persister) { + return true; + } + }); } public FeedSubscription findById(User user, Long id) { diff --git a/commafeed-server/src/main/java/com/commafeed/backend/service/FeedSubscriptionService.java b/commafeed-server/src/main/java/com/commafeed/backend/service/FeedSubscriptionService.java index 2bd11e56..444b9f34 100644 --- a/commafeed-server/src/main/java/com/commafeed/backend/service/FeedSubscriptionService.java +++ b/commafeed-server/src/main/java/com/commafeed/backend/service/FeedSubscriptionService.java @@ -23,11 +23,9 @@ import com.commafeed.frontend.model.UnreadCount; import jakarta.inject.Inject; import jakarta.inject.Singleton; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j -@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @Singleton public class FeedSubscriptionService { @@ -39,6 +37,22 @@ public class FeedSubscriptionService { private final CacheService cache; private final CommaFeedConfiguration config; + @Inject + public FeedSubscriptionService(FeedDAO feedDAO, FeedEntryStatusDAO feedEntryStatusDAO, FeedSubscriptionDAO feedSubscriptionDAO, + FeedService feedService, FeedRefreshEngine feedRefreshEngine, CacheService cache, CommaFeedConfiguration config) { + this.feedDAO = feedDAO; + this.feedEntryStatusDAO = feedEntryStatusDAO; + this.feedSubscriptionDAO = feedSubscriptionDAO; + this.feedService = feedService; + this.feedRefreshEngine = feedRefreshEngine; + this.cache = cache; + this.config = config; + + // automatically refresh feeds after they are subscribed to + // we need to use this hook because the feed needs to have been persisted because the queue processing is asynchronous + feedSubscriptionDAO.onPostCommitInsert(sub -> feedRefreshEngine.refreshImmediately(sub.getFeed())); + } + public long subscribe(User user, String url, String title) { return subscribe(user, url, title, null, 0); } @@ -83,7 +97,6 @@ public class FeedSubscriptionService { sub.setTitle(FeedUtils.truncate(title, 128)); feedSubscriptionDAO.saveOrUpdate(sub); - feedRefreshEngine.refreshImmediately(feed); cache.invalidateUserRootCategory(user); return sub.getId(); }