Files
nahodka-back/src/main/java/com/krylov/refound/service/redis/PostCacheService.java
2026-09-29 01:03:54 +03:00

102 lines
4.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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());
}
}
}