added posts on feed and posts on map
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package com.krylov.refound.service.redis;
|
||||
|
||||
import com.krylov.refound.dto.CachedPostPage;
|
||||
import com.krylov.refound.dto.MapMarkerDto;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class PostCacheService {
|
||||
|
||||
// Общая версия ВСЕХ данных posts. От неё зависят и лента, и карта —
|
||||
// любое изменение поста должно инвалидировать оба представления разом.
|
||||
// Ключ без TTL. Redis должен работать с maxmemory-policy volatile-lru,
|
||||
// чтобы этот ключ не вытеснялся
|
||||
private static final String VERSION_KEY = "posts:version";
|
||||
|
||||
private final RedisTemplate<String, CachedPostPage> feedRedisTemplate;
|
||||
private final RedisTemplate<String, List<MapMarkerDto>> mapRedisTemplate;
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
public CachedPostPage getFeed(String key) {
|
||||
try {
|
||||
return feedRedisTemplate.opsForValue().get(key);
|
||||
} catch (Exception e) {
|
||||
// Кэш best-effort: любая проблема (Redis недоступен, битый JSON) = miss
|
||||
log.warn("Не удалось прочитать feed из Redis, key={}: {}", key, e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void saveFeed(String key, CachedPostPage value, Duration ttl) {
|
||||
try {
|
||||
feedRedisTemplate.opsForValue().set(key, value, ttl);
|
||||
} catch (Exception e) {
|
||||
log.warn("Не удалось сохранить feed в Redis, key={}: {}", key, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public List<MapMarkerDto> getMap(String key) {
|
||||
try {
|
||||
return mapRedisTemplate.opsForValue().get(key);
|
||||
} catch (Exception e) {
|
||||
log.warn("Не удалось прочитать map из Redis, key={}: {}", key, e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void saveMap(String key, List<MapMarkerDto> value, Duration ttl) {
|
||||
try {
|
||||
mapRedisTemplate.opsForValue().set(key, value, ttl);
|
||||
} catch (Exception e) {
|
||||
log.warn("Не удалось сохранить map в Redis, key={}: {}", key, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/** Текущая версия данных posts. Должна читаться ДО запроса в БД, в обоих сервисах. */
|
||||
public String currentVersion() {
|
||||
try {
|
||||
String version = stringRedisTemplate.opsForValue().get(VERSION_KEY);
|
||||
return version == null ? "0" : version;
|
||||
} catch (Exception e) {
|
||||
log.warn("Не удалось прочитать версию posts cache: {}", e.toString());
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Инвалидирует ВСЕ производные кэши (ленту и карту) после коммита текущей транзакции
|
||||
* (или сразу, если транзакции нет).
|
||||
*/
|
||||
public void invalidatePosts() {
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
bumpVersion();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
bumpVersion();
|
||||
}
|
||||
}
|
||||
|
||||
private void bumpVersion() {
|
||||
try {
|
||||
Long version = stringRedisTemplate.opsForValue().increment(VERSION_KEY);
|
||||
log.info("Posts cache инвалидирован (лента + карта), версия={}", version);
|
||||
} catch (Exception e) {
|
||||
// Устаревшие записи доживут до TTL
|
||||
log.warn("Не удалось инвалидировать posts cache: {}", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user