mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
remove redis as caching is no longer needed now
This commit is contained in:
@@ -11,8 +11,7 @@ const shownMeters: Record<string, string> = {
|
|||||||
"com.commafeed.backend.feed.FeedRefreshEngine.refill": "Feed queue refill rate",
|
"com.commafeed.backend.feed.FeedRefreshEngine.refill": "Feed queue refill rate",
|
||||||
"com.commafeed.backend.feed.FeedRefreshWorker.feedFetched": "Feed fetching rate",
|
"com.commafeed.backend.feed.FeedRefreshWorker.feedFetched": "Feed fetching rate",
|
||||||
"com.commafeed.backend.feed.FeedRefreshUpdater.feedUpdated": "Feed update rate",
|
"com.commafeed.backend.feed.FeedRefreshUpdater.feedUpdated": "Feed update rate",
|
||||||
"com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheHit": "Entry cache hit rate",
|
"com.commafeed.backend.feed.FeedRefreshUpdater.entryInserted": "Entries inserted",
|
||||||
"com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheMiss": "Entry cache miss rate",
|
|
||||||
"com.commafeed.backend.service.db.DatabaseCleaningService.entriesDeleted": "Entries deleted",
|
"com.commafeed.backend.service.db.DatabaseCleaningService.entriesDeleted": "Entries deleted",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ TODO
|
|||||||
|
|
||||||
MVP:
|
MVP:
|
||||||
|
|
||||||
- remove redis, not useful anymore
|
|
||||||
- update readme
|
- update readme
|
||||||
- update docker readme
|
- update docker readme
|
||||||
- update release notes (+ mention h2 migration has been removed, upgrade to last 4.x is required)
|
- update release notes (+ mention h2 migration has been removed, upgrade to last 4.x is required)
|
||||||
|
|||||||
@@ -349,11 +349,6 @@
|
|||||||
<artifactId>passay</artifactId>
|
<artifactId>passay</artifactId>
|
||||||
<version>1.6.4</version>
|
<version>1.6.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>redis.clients</groupId>
|
|
||||||
<artifactId>jedis</artifactId>
|
|
||||||
<version>5.1.4</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.rometools</groupId>
|
<groupId>com.rometools</groupId>
|
||||||
|
|||||||
@@ -12,12 +12,6 @@ import io.smallrye.config.WithDefault;
|
|||||||
import jakarta.validation.constraints.Min;
|
import jakarta.validation.constraints.Min;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.Positive;
|
import jakarta.validation.constraints.Positive;
|
||||||
import redis.clients.jedis.DefaultJedisClientConfig;
|
|
||||||
import redis.clients.jedis.HostAndPort;
|
|
||||||
import redis.clients.jedis.JedisClientConfig;
|
|
||||||
import redis.clients.jedis.JedisPool;
|
|
||||||
import redis.clients.jedis.JedisPoolConfig;
|
|
||||||
import redis.clients.jedis.Protocol;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CommaFeed configuration
|
* CommaFeed configuration
|
||||||
@@ -92,11 +86,6 @@ public interface CommaFeedConfiguration {
|
|||||||
*/
|
*/
|
||||||
Websocket websocket();
|
Websocket websocket();
|
||||||
|
|
||||||
/**
|
|
||||||
* Redis settings to enable caching. This is only really useful on instances with a lot of users.
|
|
||||||
*/
|
|
||||||
Redis redis();
|
|
||||||
|
|
||||||
interface FeedRefresh {
|
interface FeedRefresh {
|
||||||
/**
|
/**
|
||||||
* Amount of time CommaFeed will wait before refreshing the same feed.
|
* Amount of time CommaFeed will wait before refreshing the same feed.
|
||||||
@@ -230,47 +219,4 @@ public interface CommaFeedConfiguration {
|
|||||||
Duration treeReloadInterval();
|
Duration treeReloadInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Redis {
|
|
||||||
|
|
||||||
Optional<String> host();
|
|
||||||
|
|
||||||
@WithDefault("" + Protocol.DEFAULT_PORT)
|
|
||||||
int port();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Username is only required when using Redis ACLs
|
|
||||||
*/
|
|
||||||
Optional<String> username();
|
|
||||||
|
|
||||||
Optional<String> password();
|
|
||||||
|
|
||||||
@WithDefault("" + Protocol.DEFAULT_TIMEOUT)
|
|
||||||
int timeout();
|
|
||||||
|
|
||||||
@WithDefault("" + Protocol.DEFAULT_DATABASE)
|
|
||||||
int database();
|
|
||||||
|
|
||||||
@WithDefault("500")
|
|
||||||
int maxTotal();
|
|
||||||
|
|
||||||
default JedisPool build() {
|
|
||||||
Optional<String> host = host();
|
|
||||||
if (host.isEmpty()) {
|
|
||||||
throw new IllegalStateException("Redis host is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
JedisPoolConfig poolConfig = new JedisPoolConfig();
|
|
||||||
poolConfig.setMaxTotal(maxTotal());
|
|
||||||
|
|
||||||
JedisClientConfig clientConfig = DefaultJedisClientConfig.builder()
|
|
||||||
.user(username().orElse(null))
|
|
||||||
.password(password().orElse(null))
|
|
||||||
.timeoutMillis(timeout())
|
|
||||||
.database(database())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return new JedisPool(poolConfig, new HostAndPort(host.get(), port()), clientConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package com.commafeed;
|
package com.commafeed;
|
||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.commafeed.CommaFeedConfiguration.Redis;
|
|
||||||
import com.commafeed.backend.cache.CacheService;
|
|
||||||
import com.commafeed.backend.cache.NoopCacheService;
|
|
||||||
import com.commafeed.backend.cache.RedisCacheService;
|
|
||||||
|
|
||||||
import jakarta.enterprise.inject.Produces;
|
import jakarta.enterprise.inject.Produces;
|
||||||
import jakarta.inject.Singleton;
|
import jakarta.inject.Singleton;
|
||||||
@@ -12,17 +8,6 @@ import jakarta.inject.Singleton;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class CommaFeedProducers {
|
public class CommaFeedProducers {
|
||||||
|
|
||||||
@Produces
|
|
||||||
@Singleton
|
|
||||||
public CacheService cacheService(CommaFeedConfiguration config) {
|
|
||||||
Redis redis = config.redis();
|
|
||||||
if (redis.host().isEmpty()) {
|
|
||||||
return new NoopCacheService();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RedisCacheService(redis.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Produces
|
@Produces
|
||||||
@Singleton
|
@Singleton
|
||||||
public MetricRegistry metricRegistry() {
|
public MetricRegistry metricRegistry() {
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
package com.commafeed.backend.cache;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.commafeed.backend.Digests;
|
|
||||||
import com.commafeed.backend.feed.parser.FeedParserResult.Entry;
|
|
||||||
import com.commafeed.backend.model.Feed;
|
|
||||||
import com.commafeed.backend.model.FeedSubscription;
|
|
||||||
import com.commafeed.backend.model.User;
|
|
||||||
import com.commafeed.frontend.model.Category;
|
|
||||||
import com.commafeed.frontend.model.UnreadCount;
|
|
||||||
|
|
||||||
public abstract class CacheService {
|
|
||||||
|
|
||||||
// feed entries for faster refresh
|
|
||||||
public abstract Set<String> getLastEntries(Feed feed);
|
|
||||||
|
|
||||||
public abstract void setLastEntries(Feed feed, List<String> entries);
|
|
||||||
|
|
||||||
public String buildUniqueEntryKey(Entry entry) {
|
|
||||||
return Digests.sha1Hex(entry.guid() + entry.url());
|
|
||||||
}
|
|
||||||
|
|
||||||
// user categories
|
|
||||||
public abstract Category getUserRootCategory(User user);
|
|
||||||
|
|
||||||
public abstract void setUserRootCategory(User user, Category category);
|
|
||||||
|
|
||||||
public abstract void invalidateUserRootCategory(User... users);
|
|
||||||
|
|
||||||
// unread count
|
|
||||||
public abstract UnreadCount getUnreadCount(FeedSubscription sub);
|
|
||||||
|
|
||||||
public abstract void setUnreadCount(FeedSubscription sub, UnreadCount count);
|
|
||||||
|
|
||||||
public abstract void invalidateUnreadCount(FeedSubscription... subs);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package com.commafeed.backend.cache;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.commafeed.backend.model.Feed;
|
|
||||||
import com.commafeed.backend.model.FeedSubscription;
|
|
||||||
import com.commafeed.backend.model.User;
|
|
||||||
import com.commafeed.frontend.model.Category;
|
|
||||||
import com.commafeed.frontend.model.UnreadCount;
|
|
||||||
|
|
||||||
public class NoopCacheService extends CacheService {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getLastEntries(Feed feed) {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLastEntries(Feed feed, List<String> entries) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UnreadCount getUnreadCount(FeedSubscription sub) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUnreadCount(FeedSubscription sub, UnreadCount count) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invalidateUnreadCount(FeedSubscription... subs) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Category getUserRootCategory(User user) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUserRootCategory(User user, Category category) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invalidateUserRootCategory(User... users) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
package com.commafeed.backend.cache;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import com.commafeed.backend.model.Feed;
|
|
||||||
import com.commafeed.backend.model.FeedSubscription;
|
|
||||||
import com.commafeed.backend.model.Models;
|
|
||||||
import com.commafeed.backend.model.User;
|
|
||||||
import com.commafeed.frontend.model.Category;
|
|
||||||
import com.commafeed.frontend.model.UnreadCount;
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import redis.clients.jedis.Jedis;
|
|
||||||
import redis.clients.jedis.JedisPool;
|
|
||||||
import redis.clients.jedis.Pipeline;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class RedisCacheService extends CacheService {
|
|
||||||
|
|
||||||
private static final ObjectMapper MAPPER = new ObjectMapper().registerModule(new JavaTimeModule());
|
|
||||||
|
|
||||||
private final JedisPool pool;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getLastEntries(Feed feed) {
|
|
||||||
try (Jedis jedis = pool.getResource()) {
|
|
||||||
String key = buildRedisEntryKey(feed);
|
|
||||||
return jedis.smembers(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLastEntries(Feed feed, List<String> entries) {
|
|
||||||
try (Jedis jedis = pool.getResource()) {
|
|
||||||
String key = buildRedisEntryKey(feed);
|
|
||||||
|
|
||||||
Pipeline pipe = jedis.pipelined();
|
|
||||||
pipe.del(key);
|
|
||||||
for (String entry : entries) {
|
|
||||||
pipe.sadd(key, entry);
|
|
||||||
}
|
|
||||||
pipe.expire(key, (int) TimeUnit.DAYS.toSeconds(7));
|
|
||||||
pipe.sync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Category getUserRootCategory(User user) {
|
|
||||||
Category cat = null;
|
|
||||||
try (Jedis jedis = pool.getResource()) {
|
|
||||||
String key = buildRedisUserRootCategoryKey(user);
|
|
||||||
String json = jedis.get(key);
|
|
||||||
if (json != null) {
|
|
||||||
cat = MAPPER.readValue(json, Category.class);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
return cat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUserRootCategory(User user, Category category) {
|
|
||||||
try (Jedis jedis = pool.getResource()) {
|
|
||||||
String key = buildRedisUserRootCategoryKey(user);
|
|
||||||
|
|
||||||
Pipeline pipe = jedis.pipelined();
|
|
||||||
pipe.del(key);
|
|
||||||
pipe.set(key, MAPPER.writeValueAsString(category));
|
|
||||||
pipe.expire(key, (int) TimeUnit.MINUTES.toSeconds(30));
|
|
||||||
pipe.sync();
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
log.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UnreadCount getUnreadCount(FeedSubscription sub) {
|
|
||||||
UnreadCount count = null;
|
|
||||||
try (Jedis jedis = pool.getResource()) {
|
|
||||||
String key = buildRedisUnreadCountKey(sub);
|
|
||||||
String json = jedis.get(key);
|
|
||||||
if (json != null) {
|
|
||||||
count = MAPPER.readValue(json, UnreadCount.class);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUnreadCount(FeedSubscription sub, UnreadCount count) {
|
|
||||||
try (Jedis jedis = pool.getResource()) {
|
|
||||||
String key = buildRedisUnreadCountKey(sub);
|
|
||||||
|
|
||||||
Pipeline pipe = jedis.pipelined();
|
|
||||||
pipe.del(key);
|
|
||||||
pipe.set(key, MAPPER.writeValueAsString(count));
|
|
||||||
pipe.expire(key, (int) TimeUnit.MINUTES.toSeconds(30));
|
|
||||||
pipe.sync();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invalidateUserRootCategory(User... users) {
|
|
||||||
try (Jedis jedis = pool.getResource()) {
|
|
||||||
Pipeline pipe = jedis.pipelined();
|
|
||||||
if (users != null) {
|
|
||||||
for (User user : users) {
|
|
||||||
String key = buildRedisUserRootCategoryKey(user);
|
|
||||||
pipe.del(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pipe.sync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invalidateUnreadCount(FeedSubscription... subs) {
|
|
||||||
try (Jedis jedis = pool.getResource()) {
|
|
||||||
Pipeline pipe = jedis.pipelined();
|
|
||||||
if (subs != null) {
|
|
||||||
for (FeedSubscription sub : subs) {
|
|
||||||
String key = buildRedisUnreadCountKey(sub);
|
|
||||||
pipe.del(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pipe.sync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildRedisEntryKey(Feed feed) {
|
|
||||||
return "f:" + Models.getId(feed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildRedisUserRootCategoryKey(User user) {
|
|
||||||
return "c:" + Models.getId(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildRedisUnreadCountKey(FeedSubscription sub) {
|
|
||||||
return "u:" + Models.getId(sub);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.commafeed.backend.feed;
|
package com.commafeed.backend.feed;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -16,7 +15,6 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import com.codahale.metrics.Meter;
|
import com.codahale.metrics.Meter;
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.commafeed.backend.Digests;
|
import com.commafeed.backend.Digests;
|
||||||
import com.commafeed.backend.cache.CacheService;
|
|
||||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||||
import com.commafeed.backend.dao.UnitOfWork;
|
import com.commafeed.backend.dao.UnitOfWork;
|
||||||
import com.commafeed.backend.feed.parser.FeedParserResult.Content;
|
import com.commafeed.backend.feed.parser.FeedParserResult.Content;
|
||||||
@@ -25,7 +23,6 @@ import com.commafeed.backend.model.Feed;
|
|||||||
import com.commafeed.backend.model.FeedEntry;
|
import com.commafeed.backend.model.FeedEntry;
|
||||||
import com.commafeed.backend.model.FeedSubscription;
|
import com.commafeed.backend.model.FeedSubscription;
|
||||||
import com.commafeed.backend.model.Models;
|
import com.commafeed.backend.model.Models;
|
||||||
import com.commafeed.backend.model.User;
|
|
||||||
import com.commafeed.backend.service.FeedEntryService;
|
import com.commafeed.backend.service.FeedEntryService;
|
||||||
import com.commafeed.backend.service.FeedService;
|
import com.commafeed.backend.service.FeedService;
|
||||||
import com.commafeed.frontend.ws.WebSocketMessageBuilder;
|
import com.commafeed.frontend.ws.WebSocketMessageBuilder;
|
||||||
@@ -47,29 +44,23 @@ public class FeedRefreshUpdater {
|
|||||||
private final FeedService feedService;
|
private final FeedService feedService;
|
||||||
private final FeedEntryService feedEntryService;
|
private final FeedEntryService feedEntryService;
|
||||||
private final FeedSubscriptionDAO feedSubscriptionDAO;
|
private final FeedSubscriptionDAO feedSubscriptionDAO;
|
||||||
private final CacheService cache;
|
|
||||||
private final WebSocketSessions webSocketSessions;
|
private final WebSocketSessions webSocketSessions;
|
||||||
|
|
||||||
private final Striped<Lock> locks;
|
private final Striped<Lock> locks;
|
||||||
|
|
||||||
private final Meter entryCacheMiss;
|
|
||||||
private final Meter entryCacheHit;
|
|
||||||
private final Meter feedUpdated;
|
private final Meter feedUpdated;
|
||||||
private final Meter entryInserted;
|
private final Meter entryInserted;
|
||||||
|
|
||||||
public FeedRefreshUpdater(UnitOfWork unitOfWork, FeedService feedService, FeedEntryService feedEntryService, MetricRegistry metrics,
|
public FeedRefreshUpdater(UnitOfWork unitOfWork, FeedService feedService, FeedEntryService feedEntryService, MetricRegistry metrics,
|
||||||
FeedSubscriptionDAO feedSubscriptionDAO, CacheService cache, WebSocketSessions webSocketSessions) {
|
FeedSubscriptionDAO feedSubscriptionDAO, WebSocketSessions webSocketSessions) {
|
||||||
this.unitOfWork = unitOfWork;
|
this.unitOfWork = unitOfWork;
|
||||||
this.feedService = feedService;
|
this.feedService = feedService;
|
||||||
this.feedEntryService = feedEntryService;
|
this.feedEntryService = feedEntryService;
|
||||||
this.feedSubscriptionDAO = feedSubscriptionDAO;
|
this.feedSubscriptionDAO = feedSubscriptionDAO;
|
||||||
this.cache = cache;
|
|
||||||
this.webSocketSessions = webSocketSessions;
|
this.webSocketSessions = webSocketSessions;
|
||||||
|
|
||||||
locks = Striped.lazyWeakLock(100000);
|
locks = Striped.lazyWeakLock(100000);
|
||||||
|
|
||||||
entryCacheMiss = metrics.meter(MetricRegistry.name(getClass(), "entryCacheMiss"));
|
|
||||||
entryCacheHit = metrics.meter(MetricRegistry.name(getClass(), "entryCacheHit"));
|
|
||||||
feedUpdated = metrics.meter(MetricRegistry.name(getClass(), "feedUpdated"));
|
feedUpdated = metrics.meter(MetricRegistry.name(getClass(), "feedUpdated"));
|
||||||
entryInserted = metrics.meter(MetricRegistry.name(getClass(), "entryInserted"));
|
entryInserted = metrics.meter(MetricRegistry.name(getClass(), "entryInserted"));
|
||||||
}
|
}
|
||||||
@@ -139,14 +130,8 @@ public class FeedRefreshUpdater {
|
|||||||
Map<FeedSubscription, Long> unreadCountBySubscription = new HashMap<>();
|
Map<FeedSubscription, Long> unreadCountBySubscription = new HashMap<>();
|
||||||
|
|
||||||
if (!entries.isEmpty()) {
|
if (!entries.isEmpty()) {
|
||||||
Set<String> lastEntries = cache.getLastEntries(feed);
|
|
||||||
List<String> currentEntries = new ArrayList<>();
|
|
||||||
|
|
||||||
List<FeedSubscription> subscriptions = null;
|
List<FeedSubscription> subscriptions = null;
|
||||||
for (Entry entry : entries) {
|
for (Entry entry : entries) {
|
||||||
String cacheKey = cache.buildUniqueEntryKey(entry);
|
|
||||||
if (!lastEntries.contains(cacheKey)) {
|
|
||||||
log.debug("cache miss for {}", entry.url());
|
|
||||||
if (subscriptions == null) {
|
if (subscriptions == null) {
|
||||||
subscriptions = unitOfWork.call(() -> feedSubscriptionDAO.findByFeed(feed));
|
subscriptions = unitOfWork.call(() -> feedSubscriptionDAO.findByFeed(feed));
|
||||||
}
|
}
|
||||||
@@ -154,24 +139,12 @@ public class FeedRefreshUpdater {
|
|||||||
processed &= addEntryResult.processed;
|
processed &= addEntryResult.processed;
|
||||||
inserted += addEntryResult.inserted ? 1 : 0;
|
inserted += addEntryResult.inserted ? 1 : 0;
|
||||||
addEntryResult.subscriptionsForWhichEntryIsUnread.forEach(sub -> unreadCountBySubscription.merge(sub, 1L, Long::sum));
|
addEntryResult.subscriptionsForWhichEntryIsUnread.forEach(sub -> unreadCountBySubscription.merge(sub, 1L, Long::sum));
|
||||||
|
|
||||||
entryCacheMiss.mark();
|
|
||||||
} else {
|
|
||||||
log.debug("cache hit for {}", entry.url());
|
|
||||||
entryCacheHit.mark();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentEntries.add(cacheKey);
|
if (inserted == 0) {
|
||||||
}
|
|
||||||
cache.setLastEntries(feed, currentEntries);
|
|
||||||
|
|
||||||
if (subscriptions == null) {
|
|
||||||
feed.setMessage("No new entries found");
|
feed.setMessage("No new entries found");
|
||||||
} else if (inserted > 0) {
|
} else if (inserted > 0) {
|
||||||
log.debug("inserted {} entries for feed {}", inserted, feed.getId());
|
feed.setMessage("Found %s new entries".formatted(inserted));
|
||||||
List<User> users = subscriptions.stream().map(FeedSubscription::getUser).toList();
|
|
||||||
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0]));
|
|
||||||
cache.invalidateUserRootCategory(users.toArray(new User[0]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import java.util.List;
|
|||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.commafeed.backend.cache.CacheService;
|
|
||||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||||
import com.commafeed.backend.feed.FeedUtils;
|
import com.commafeed.backend.feed.FeedUtils;
|
||||||
import com.commafeed.backend.model.FeedCategory;
|
import com.commafeed.backend.model.FeedCategory;
|
||||||
@@ -28,7 +27,6 @@ public class OPMLImporter {
|
|||||||
|
|
||||||
private final FeedCategoryDAO feedCategoryDAO;
|
private final FeedCategoryDAO feedCategoryDAO;
|
||||||
private final FeedSubscriptionService feedSubscriptionService;
|
private final FeedSubscriptionService feedSubscriptionService;
|
||||||
private final CacheService cache;
|
|
||||||
|
|
||||||
public void importOpml(User user, String xml) throws IllegalArgumentException, FeedException {
|
public void importOpml(User user, String xml) throws IllegalArgumentException, FeedException {
|
||||||
xml = xml.substring(xml.indexOf('<'));
|
xml = xml.substring(xml.indexOf('<'));
|
||||||
@@ -79,6 +77,5 @@ public class OPMLImporter {
|
|||||||
log.error("error while importing {}: {}", outline.getXmlUrl(), e.getMessage());
|
log.error("error while importing {}: {}", outline.getXmlUrl(), e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import java.time.Instant;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.commafeed.backend.Digests;
|
import com.commafeed.backend.Digests;
|
||||||
import com.commafeed.backend.cache.CacheService;
|
|
||||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||||
@@ -32,7 +31,6 @@ public class FeedEntryService {
|
|||||||
private final FeedEntryStatusDAO feedEntryStatusDAO;
|
private final FeedEntryStatusDAO feedEntryStatusDAO;
|
||||||
private final FeedEntryContentService feedEntryContentService;
|
private final FeedEntryContentService feedEntryContentService;
|
||||||
private final FeedEntryFilteringService feedEntryFilteringService;
|
private final FeedEntryFilteringService feedEntryFilteringService;
|
||||||
private final CacheService cache;
|
|
||||||
|
|
||||||
public FeedEntry find(Feed feed, Entry entry) {
|
public FeedEntry find(Feed feed, Entry entry) {
|
||||||
String guidHash = Digests.sha1Hex(entry.guid());
|
String guidHash = Digests.sha1Hex(entry.guid());
|
||||||
@@ -85,8 +83,6 @@ public class FeedEntryService {
|
|||||||
if (status.isMarkable()) {
|
if (status.isMarkable()) {
|
||||||
status.setRead(read);
|
status.setRead(read);
|
||||||
feedEntryStatusDAO.saveOrUpdate(status);
|
feedEntryStatusDAO.saveOrUpdate(status);
|
||||||
cache.invalidateUnreadCount(sub);
|
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,8 +108,6 @@ public class FeedEntryService {
|
|||||||
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user, subscriptions, true, keywords, null, -1, -1, null,
|
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user, subscriptions, true, keywords, null, -1, -1, null,
|
||||||
false, null, null, null);
|
false, null, null, null);
|
||||||
markList(statuses, olderThan, insertedBefore);
|
markList(statuses, olderThan, insertedBefore);
|
||||||
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0]));
|
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markStarredEntries(User user, Instant olderThan, Instant insertedBefore) {
|
public void markStarredEntries(User user, Instant olderThan, Instant insertedBefore) {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import java.util.stream.Collectors;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.commafeed.CommaFeedConfiguration;
|
import com.commafeed.CommaFeedConfiguration;
|
||||||
import com.commafeed.backend.cache.CacheService;
|
|
||||||
import com.commafeed.backend.dao.FeedDAO;
|
import com.commafeed.backend.dao.FeedDAO;
|
||||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||||
@@ -17,7 +16,6 @@ import com.commafeed.backend.feed.FeedUtils;
|
|||||||
import com.commafeed.backend.model.Feed;
|
import com.commafeed.backend.model.Feed;
|
||||||
import com.commafeed.backend.model.FeedCategory;
|
import com.commafeed.backend.model.FeedCategory;
|
||||||
import com.commafeed.backend.model.FeedSubscription;
|
import com.commafeed.backend.model.FeedSubscription;
|
||||||
import com.commafeed.backend.model.Models;
|
|
||||||
import com.commafeed.backend.model.User;
|
import com.commafeed.backend.model.User;
|
||||||
import com.commafeed.frontend.model.UnreadCount;
|
import com.commafeed.frontend.model.UnreadCount;
|
||||||
|
|
||||||
@@ -33,17 +31,15 @@ public class FeedSubscriptionService {
|
|||||||
private final FeedSubscriptionDAO feedSubscriptionDAO;
|
private final FeedSubscriptionDAO feedSubscriptionDAO;
|
||||||
private final FeedService feedService;
|
private final FeedService feedService;
|
||||||
private final FeedRefreshEngine feedRefreshEngine;
|
private final FeedRefreshEngine feedRefreshEngine;
|
||||||
private final CacheService cache;
|
|
||||||
private final CommaFeedConfiguration config;
|
private final CommaFeedConfiguration config;
|
||||||
|
|
||||||
public FeedSubscriptionService(FeedDAO feedDAO, FeedEntryStatusDAO feedEntryStatusDAO, FeedSubscriptionDAO feedSubscriptionDAO,
|
public FeedSubscriptionService(FeedDAO feedDAO, FeedEntryStatusDAO feedEntryStatusDAO, FeedSubscriptionDAO feedSubscriptionDAO,
|
||||||
FeedService feedService, FeedRefreshEngine feedRefreshEngine, CacheService cache, CommaFeedConfiguration config) {
|
FeedService feedService, FeedRefreshEngine feedRefreshEngine, CommaFeedConfiguration config) {
|
||||||
this.feedDAO = feedDAO;
|
this.feedDAO = feedDAO;
|
||||||
this.feedEntryStatusDAO = feedEntryStatusDAO;
|
this.feedEntryStatusDAO = feedEntryStatusDAO;
|
||||||
this.feedSubscriptionDAO = feedSubscriptionDAO;
|
this.feedSubscriptionDAO = feedSubscriptionDAO;
|
||||||
this.feedService = feedService;
|
this.feedService = feedService;
|
||||||
this.feedRefreshEngine = feedRefreshEngine;
|
this.feedRefreshEngine = feedRefreshEngine;
|
||||||
this.cache = cache;
|
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
||||||
// automatically refresh feeds after they are subscribed to
|
// automatically refresh feeds after they are subscribed to
|
||||||
@@ -95,7 +91,6 @@ public class FeedSubscriptionService {
|
|||||||
sub.setTitle(FeedUtils.truncate(title, 128));
|
sub.setTitle(FeedUtils.truncate(title, 128));
|
||||||
feedSubscriptionDAO.saveOrUpdate(sub);
|
feedSubscriptionDAO.saveOrUpdate(sub);
|
||||||
|
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
return sub.getId();
|
return sub.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +98,6 @@ public class FeedSubscriptionService {
|
|||||||
FeedSubscription sub = feedSubscriptionDAO.findById(user, subId);
|
FeedSubscription sub = feedSubscriptionDAO.findById(user, subId);
|
||||||
if (sub != null) {
|
if (sub != null) {
|
||||||
feedSubscriptionDAO.delete(sub);
|
feedSubscriptionDAO.delete(sub);
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -130,17 +124,9 @@ public class FeedSubscriptionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Map<Long, UnreadCount> getUnreadCount(User user) {
|
public Map<Long, UnreadCount> getUnreadCount(User user) {
|
||||||
return feedSubscriptionDAO.findAll(user).stream().collect(Collectors.toMap(FeedSubscription::getId, this::getUnreadCount));
|
return feedSubscriptionDAO.findAll(user)
|
||||||
}
|
.stream()
|
||||||
|
.collect(Collectors.toMap(FeedSubscription::getId, feedEntryStatusDAO::getUnreadCount));
|
||||||
private UnreadCount getUnreadCount(FeedSubscription sub) {
|
|
||||||
UnreadCount count = cache.getUnreadCount(sub);
|
|
||||||
if (count == null) {
|
|
||||||
log.debug("unread count cache miss for {}", Models.getId(sub));
|
|
||||||
count = feedEntryStatusDAO.getUnreadCount(sub);
|
|
||||||
cache.setUnreadCount(sub, count);
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import org.apache.commons.lang3.ObjectUtils;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.commafeed.CommaFeedConfiguration;
|
import com.commafeed.CommaFeedConfiguration;
|
||||||
import com.commafeed.backend.cache.CacheService;
|
|
||||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||||
@@ -89,7 +88,6 @@ public class CategoryREST {
|
|||||||
private final FeedSubscriptionDAO feedSubscriptionDAO;
|
private final FeedSubscriptionDAO feedSubscriptionDAO;
|
||||||
private final FeedEntryService feedEntryService;
|
private final FeedEntryService feedEntryService;
|
||||||
private final FeedSubscriptionService feedSubscriptionService;
|
private final FeedSubscriptionService feedSubscriptionService;
|
||||||
private final CacheService cache;
|
|
||||||
private final CommaFeedConfiguration config;
|
private final CommaFeedConfiguration config;
|
||||||
|
|
||||||
@Path("/entries")
|
@Path("/entries")
|
||||||
@@ -291,7 +289,6 @@ public class CategoryREST {
|
|||||||
cat.setParent(parent);
|
cat.setParent(parent);
|
||||||
}
|
}
|
||||||
feedCategoryDAO.saveOrUpdate(cat);
|
feedCategoryDAO.saveOrUpdate(cat);
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
return Response.ok(cat.getId()).build();
|
return Response.ok(cat.getId()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +318,6 @@ public class CategoryREST {
|
|||||||
feedCategoryDAO.saveOrUpdate(categories);
|
feedCategoryDAO.saveOrUpdate(categories);
|
||||||
|
|
||||||
feedCategoryDAO.delete(cat);
|
feedCategoryDAO.delete(cat);
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
return Response.ok().build();
|
return Response.ok().build();
|
||||||
} else {
|
} else {
|
||||||
return Response.status(Status.NOT_FOUND).build();
|
return Response.status(Status.NOT_FOUND).build();
|
||||||
@@ -374,7 +370,6 @@ public class CategoryREST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
feedCategoryDAO.saveOrUpdate(category);
|
feedCategoryDAO.saveOrUpdate(category);
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
return Response.ok().build();
|
return Response.ok().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +388,6 @@ public class CategoryREST {
|
|||||||
}
|
}
|
||||||
category.setCollapsed(req.isCollapse());
|
category.setCollapsed(req.isCollapse());
|
||||||
feedCategoryDAO.saveOrUpdate(category);
|
feedCategoryDAO.saveOrUpdate(category);
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
return Response.ok().build();
|
return Response.ok().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,18 +412,14 @@ public class CategoryREST {
|
|||||||
responses = { @ApiResponse(content = @Content(schema = @Schema(implementation = Category.class))) })
|
responses = { @ApiResponse(content = @Content(schema = @Schema(implementation = Category.class))) })
|
||||||
public Response getRootCategory() {
|
public Response getRootCategory() {
|
||||||
User user = authenticationContext.getCurrentUser();
|
User user = authenticationContext.getCurrentUser();
|
||||||
Category root = cache.getUserRootCategory(user);
|
|
||||||
if (root == null) {
|
|
||||||
log.debug("tree cache miss for {}", user.getId());
|
|
||||||
List<FeedCategory> categories = feedCategoryDAO.findAll(user);
|
List<FeedCategory> categories = feedCategoryDAO.findAll(user);
|
||||||
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findAll(user);
|
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findAll(user);
|
||||||
Map<Long, UnreadCount> unreadCount = feedSubscriptionService.getUnreadCount(user);
|
Map<Long, UnreadCount> unreadCount = feedSubscriptionService.getUnreadCount(user);
|
||||||
|
|
||||||
root = buildCategory(null, categories, subscriptions, unreadCount);
|
Category root = buildCategory(null, categories, subscriptions, unreadCount);
|
||||||
root.setId("all");
|
root.setId("all");
|
||||||
root.setName("All");
|
root.setName("All");
|
||||||
cache.setUserRootCategory(user, root);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response.ok(root).build();
|
return Response.ok(root).build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import org.jboss.resteasy.reactive.RestForm;
|
|||||||
|
|
||||||
import com.commafeed.CommaFeedApplication;
|
import com.commafeed.CommaFeedApplication;
|
||||||
import com.commafeed.CommaFeedConfiguration;
|
import com.commafeed.CommaFeedConfiguration;
|
||||||
import com.commafeed.backend.cache.CacheService;
|
|
||||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||||
@@ -114,7 +113,6 @@ public class FeedREST {
|
|||||||
private final FeedRefreshEngine feedRefreshEngine;
|
private final FeedRefreshEngine feedRefreshEngine;
|
||||||
private final OPMLImporter opmlImporter;
|
private final OPMLImporter opmlImporter;
|
||||||
private final OPMLExporter opmlExporter;
|
private final OPMLExporter opmlExporter;
|
||||||
private final CacheService cache;
|
|
||||||
private final CommaFeedConfiguration config;
|
private final CommaFeedConfiguration config;
|
||||||
|
|
||||||
private static FeedEntry initTestEntry() {
|
private static FeedEntry initTestEntry() {
|
||||||
@@ -492,7 +490,6 @@ public class FeedREST {
|
|||||||
} else {
|
} else {
|
||||||
feedSubscriptionDAO.saveOrUpdate(subscription);
|
feedSubscriptionDAO.saveOrUpdate(subscription);
|
||||||
}
|
}
|
||||||
cache.invalidateUserRootCategory(user);
|
|
||||||
return Response.ok().build();
|
return Response.ok().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.apache.commons.io.IOUtils;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import com.commafeed.backend.cache.CacheService;
|
|
||||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||||
import com.commafeed.backend.model.FeedCategory;
|
import com.commafeed.backend.model.FeedCategory;
|
||||||
import com.commafeed.backend.model.User;
|
import com.commafeed.backend.model.User;
|
||||||
@@ -39,12 +38,11 @@ class OPMLImporterTest {
|
|||||||
private void testOpmlVersion(String fileName) throws IOException, IllegalArgumentException, FeedException {
|
private void testOpmlVersion(String fileName) throws IOException, IllegalArgumentException, FeedException {
|
||||||
FeedCategoryDAO feedCategoryDAO = Mockito.mock(FeedCategoryDAO.class);
|
FeedCategoryDAO feedCategoryDAO = Mockito.mock(FeedCategoryDAO.class);
|
||||||
FeedSubscriptionService feedSubscriptionService = Mockito.mock(FeedSubscriptionService.class);
|
FeedSubscriptionService feedSubscriptionService = Mockito.mock(FeedSubscriptionService.class);
|
||||||
CacheService cacheService = Mockito.mock(CacheService.class);
|
|
||||||
User user = Mockito.mock(User.class);
|
User user = Mockito.mock(User.class);
|
||||||
|
|
||||||
String xml = IOUtils.toString(getClass().getResourceAsStream(fileName), StandardCharsets.UTF_8);
|
String xml = IOUtils.toString(getClass().getResourceAsStream(fileName), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
OPMLImporter importer = new OPMLImporter(feedCategoryDAO, feedSubscriptionService, cacheService);
|
OPMLImporter importer = new OPMLImporter(feedCategoryDAO, feedSubscriptionService);
|
||||||
importer.importOpml(user, xml);
|
importer.importOpml(user, xml);
|
||||||
|
|
||||||
Mockito.verify(feedSubscriptionService)
|
Mockito.verify(feedSubscriptionService)
|
||||||
|
|||||||
Reference in New Issue
Block a user