added posts on feed and posts on map

This commit is contained in:
SlimusMinus
2026-09-29 01:03:54 +03:00
parent a44ca00222
commit 870739f45f
22 changed files with 639 additions and 44 deletions

View File

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