Merge branch 'RED-9573-bp' into 'release/4.73.x'

RED-9517: Use redis for chaching report templates

See merge request redactmanager/redaction-report-service!83
This commit is contained in:
Ali Oezyetimoglu 2024-07-11 09:16:51 +02:00
commit f8af6cbc79
12 changed files with 92 additions and 58 deletions

View File

@ -14,11 +14,7 @@ public interface ReportTemplateResource {
String REPORT_TEMPLATE_PATH = "/report-templates";
String REPORT_TEMPLATE_UPLOAD_PATH = "/upload-template";
String TEMPLATE_ID = "templateId";
String TEMPLATE_ID_PATH_VARIABLE = "/template-id/{" + TEMPLATE_ID + "}";
String EVICT_REPORT_TEMPLATE_CACHE_PATH = "/evict-report-template-cache";
String DOSSIER_TEMPLATE_ID = "dossierTemplateId";
@ -29,7 +25,7 @@ public interface ReportTemplateResource {
List<ReportTemplate> getReportTemplatesByPlaceholder(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody JSONPrimitive<String> placeholder);
@PostMapping(value = REPORT_TEMPLATE_UPLOAD_PATH + TEMPLATE_ID_PATH_VARIABLE)
void uploadTemplate(@PathVariable(TEMPLATE_ID) String templateId);
@PostMapping(value = EVICT_REPORT_TEMPLATE_CACHE_PATH)
void evictReportTemplateCache();
}

View File

@ -50,6 +50,9 @@ dependencies {
implementation("org.springframework.cloud:spring-cloud-starter-openfeign:4.1.1")
implementation("org.apache.commons:commons-lang3:3.12.0")
implementation("org.springframework.boot:spring-boot-starter-cache:${springBootStarterVersion}")
implementation("org.springframework.boot:spring-boot-starter-data-redis:${springBootStarterVersion}")
implementation("com.github.ben-manes.caffeine:caffeine:3.1.8")
implementation("net.logstash.logback:logstash-logback-encoder:7.4")

View File

@ -0,0 +1,31 @@
package com.iqser.red.service.redaction.report.v1.server.cache;
import java.time.Duration;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class RedisCachingConfiguration {
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return (builder) -> builder.withCacheConfiguration("reportTemplateCache",
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)).disableCachingNullValues());
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
}

View File

@ -0,0 +1,32 @@
package com.iqser.red.service.redaction.report.v1.server.cache;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.iqser.red.service.redaction.report.v1.server.storage.ReportStorageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class ReportTemplateCache {
private final ReportStorageService reportStorageService;
@Cacheable(value = "reportTemplateCache")
public byte[] getTemplate(String storageId) {
log.info("Putting report template with storageId {} into cache", storageId);
return reportStorageService.getReportTemplate(storageId);
}
@CacheEvict(value = "reportTemplateCache", allEntries = true)
public void evictReportTemplateCache() {
log.info("Evicted cache for report templates");
}
}

View File

@ -10,7 +10,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSON
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplate;
import com.iqser.red.service.redaction.report.v1.api.resource.ReportTemplateResource;
import com.iqser.red.service.redaction.report.v1.server.service.PlaceholderService;
import com.iqser.red.service.redaction.report.v1.server.utils.TemplateCache;
import com.iqser.red.service.redaction.report.v1.server.cache.ReportTemplateCache;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
public class ReportTemplateController implements ReportTemplateResource {
private final PlaceholderService placeholderService;
private final ReportTemplateCache reportTemplateCache;
@Override
@ -30,15 +31,10 @@ public class ReportTemplateController implements ReportTemplateResource {
}
/**
* If a template with the same id is uploaded we evict it from the cache.
*
* @param templateId The id for the uploaded template
*/
@Override
public void uploadTemplate(String templateId) {
public void evictReportTemplateCache(){
TemplateCache.evictCache(templateId);
reportTemplateCache.evictReportTemplateCache();
}
}

View File

@ -21,7 +21,7 @@ import com.iqser.red.service.redaction.report.v1.server.model.ReportRedactionEnt
import com.iqser.red.service.redaction.report.v1.server.model.ReportTemplatesModel;
import com.iqser.red.service.redaction.report.v1.server.storage.ReportStorageService;
import com.iqser.red.service.redaction.report.v1.server.storage.ReportStorageServiceAsyncWrapper;
import com.iqser.red.service.redaction.report.v1.server.utils.TemplateCache;
import com.iqser.red.service.redaction.report.v1.server.cache.ReportTemplateCache;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@ -37,6 +37,7 @@ public class ExcelReportTemplateService {
ExcelReportGenerationService excelTemplateReportGenerationService;
GeneratePlaceholderService generatePlaceholderService;
ReportStorageServiceAsyncWrapper reportStorageServiceAsyncWrapper;
ReportTemplateCache reportTemplateCache;
public CompletableFuture<StoredFileInformation> createExcelReportFromTemplateAsync(Dossier dossier,
@ -47,7 +48,7 @@ public class ExcelReportTemplateService {
List<ReportRedactionEntry> reportEntries,
ReportTemplate reportTemplate) {
byte[] excelTemplate = TemplateCache.getTemplate(reportTemplate.getStorageId(), reportStorageService);
byte[] excelTemplate = reportTemplateCache.getTemplate(reportTemplate.getStorageId());
try (ByteArrayInputStream is = new ByteArrayInputStream(excelTemplate)) {
XSSFWorkbook readWorkbook = new XSSFWorkbook(is);
SXSSFWorkbook writeWorkbook = new SXSSFWorkbook();
@ -80,7 +81,7 @@ public class ExcelReportTemplateService {
public void prepareExcelReportTemplates(String dossierTemplateId, String templateId, ReportTemplate reportTemplate, ReportTemplatesModel reportTemplatesModel) {
byte[] excelTemplate = TemplateCache.getTemplate(reportTemplate.getStorageId(), reportStorageService);
byte[] excelTemplate = reportTemplateCache.getTemplate(reportTemplate.getStorageId());
try (ByteArrayInputStream is = new ByteArrayInputStream(excelTemplate)) {
XSSFWorkbook readWorkbook = new XSSFWorkbook(is);
SXSSFWorkbook writeWorkbook = new SXSSFWorkbook();

View File

@ -7,9 +7,6 @@ import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.stereotype.Service;
@ -29,7 +26,6 @@ import com.iqser.red.service.redaction.report.v1.server.model.ReportTemplatesMod
import com.iqser.red.service.redaction.report.v1.server.settings.ReportTemplateSettings;
import com.iqser.red.service.redaction.report.v1.server.storage.ReportStorageService;
import com.iqser.red.service.redaction.report.v1.server.storage.ReportStorageServiceAsyncWrapper;
import com.iqser.red.service.redaction.report.v1.server.utils.TemplateCache;
import io.micrometer.core.annotation.Timed;
import lombok.AccessLevel;

View File

@ -19,7 +19,7 @@ import com.iqser.red.service.redaction.report.v1.server.model.ReportRedactionEnt
import com.iqser.red.service.redaction.report.v1.server.model.ReportTemplatesModel;
import com.iqser.red.service.redaction.report.v1.server.storage.ReportStorageService;
import com.iqser.red.service.redaction.report.v1.server.storage.ReportStorageServiceAsyncWrapper;
import com.iqser.red.service.redaction.report.v1.server.utils.TemplateCache;
import com.iqser.red.service.redaction.report.v1.server.cache.ReportTemplateCache;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@ -34,6 +34,7 @@ public class WordReportTemplateService {
ReportStorageService reportStorageService;
ReportStorageServiceAsyncWrapper reportStorageServiceAsyncWrapper;
WordReportGenerationService wordReportGenerationService;
ReportTemplateCache reportTemplateCache;
public CompletableFuture<StoredFileInformation> createWordReportFromTemplateAsync(Dossier dossier,
FileModel fileStatus,
@ -43,7 +44,7 @@ public class WordReportTemplateService {
List<ReportRedactionEntry> reportEntries,
ReportTemplate reportTemplate) {
byte[] wordTemplate = TemplateCache.getTemplate(reportTemplate.getStorageId(), reportStorageService);
byte[] wordTemplate = reportTemplateCache.getTemplate(reportTemplate.getStorageId());
try (ByteArrayInputStream is = new ByteArrayInputStream(wordTemplate)) {
XWPFDocument doc = new XWPFDocument(is);
wordReportGenerationService.generateWordReport(reportEntries, placeholderModel, templateName, doc, fileStatus, dossier, true);
@ -60,7 +61,7 @@ public class WordReportTemplateService {
public void prepareWordReportTemplates(String templateId, ReportTemplate reportTemplate, ReportTemplatesModel reportTemplatesModel) {
byte[] wordTemplate = TemplateCache.getTemplate(reportTemplate.getStorageId(), reportStorageService);
byte[] wordTemplate = reportTemplateCache.getTemplate(reportTemplate.getStorageId());
try (ByteArrayInputStream is = new ByteArrayInputStream(wordTemplate)) {
XWPFDocument doc = new XWPFDocument(is);
MultiFileDocument multiFileDocument = new MultiFileDocument(wordTemplate, doc, templateId, reportTemplate.getFileName(), 0, 0);

View File

@ -1,31 +0,0 @@
package com.iqser.red.service.redaction.report.v1.server.utils;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.iqser.red.service.redaction.report.v1.server.storage.ReportStorageService;
import lombok.extern.slf4j.Slf4j;
/**
* This is a caching mechanism for templates to avoid getting a template from storage for each file when we request a download,
* since the templates are provided at startup.
*/
@Slf4j
public class TemplateCache {
private static final Cache<String, byte[]> cache = Caffeine.newBuilder().maximumSize(500).build();
public static byte[] getTemplate(String templateId, ReportStorageService reportStorageService) {
return cache.get(templateId, reportStorageService::getReportTemplate);
}
public static void evictCache(String templateId) {
log.info("Evicting cache for template {}", templateId);
cache.invalidate(templateId);
}
}

View File

@ -38,6 +38,8 @@ spring:
max-attempts: 3
max-interval: 15000
prefetch: 1
cache:
type: redis
data:
mongodb:
auto-index-creation: true
@ -47,7 +49,12 @@ spring:
port: ${MONGODB_PORT:27017}
username: ${MONGODB_USER}
password: ${MONGODB_PASSWORD}
redis:
database: 0
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD}
timeout: 60000
management:
endpoint:

View File

@ -19,7 +19,7 @@ public final class OsUtils {
if (isWindows() && StringUtils.isNotBlank(tmpdir)) {
return tmpdir;
}
return "/tmp";
return "/tmp/";
}
}

View File

@ -9,6 +9,8 @@ management:
spring:
main:
allow-circular-references: true
cache:
type: SIMPLE
data:
mongodb:
auto-index-creation: true