Skip to content
Snippets Groups Projects
Commit 13ff5317 authored by Chris Lemaire's avatar Chris Lemaire
Browse files

Add cache configuration options

parent 48f3bdc0
1 merge request!527Draft: Resolve "[Queue-2.0] Incredible amount of requests to roles endpoint"
Showing
with 335 additions and 169 deletions
......@@ -21,6 +21,7 @@ import java.util.List;
import nl.tudelft.labracore.api.AssignmentControllerApi;
import nl.tudelft.labracore.api.dto.AssignmentDetailsDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -28,17 +29,17 @@ import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class AssignmentCacheManager extends CoreCacheManager<Long, AssignmentDetailsDTO> {
public class AssignmentCacheManager extends BaseCoreCacheManager<Long, AssignmentDetailsDTO> {
@Autowired
private AssignmentControllerApi api;
@Override
protected List<AssignmentDetailsDTO> fetch(List<Long> ids) {
public List<AssignmentDetailsDTO> fetch(List<Long> ids) {
return api.getAllAssignmentsById(ids).collectList().block();
}
@Override
protected Long getId(AssignmentDetailsDTO dto) {
public Long getId(AssignmentDetailsDTO dto) {
return dto.getId();
}
}
......@@ -22,6 +22,7 @@ import java.util.stream.Collectors;
import nl.tudelft.labracore.api.ClusterControllerApi;
import nl.tudelft.labracore.api.dto.ClusterDetailsDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -29,19 +30,19 @@ import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class ClusterCacheManager extends CoreCacheManager<Long, ClusterDetailsDTO> {
public class ClusterCacheManager extends BaseCoreCacheManager<Long, ClusterDetailsDTO> {
@Autowired
private ClusterControllerApi cApi;
@Override
protected List<ClusterDetailsDTO> fetch(List<Long> ids) {
public List<ClusterDetailsDTO> fetch(List<Long> ids) {
return ids.stream()
.map(id -> cApi.getClusterById(id).block())
.collect(Collectors.toList());
}
@Override
protected Long getId(ClusterDetailsDTO dto) {
public Long getId(ClusterDetailsDTO dto) {
return dto.getId();
}
}
......@@ -21,6 +21,7 @@ import java.util.List;
import nl.tudelft.labracore.api.CohortControllerApi;
import nl.tudelft.labracore.api.dto.CohortDetailsDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -28,17 +29,17 @@ import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class CohortCacheManager extends CoreCacheManager<Long, CohortDetailsDTO> {
public class CohortCacheManager extends BaseCoreCacheManager<Long, CohortDetailsDTO> {
@Autowired
private CohortControllerApi cApi;
@Override
protected List<CohortDetailsDTO> fetch(List<Long> ids) {
public List<CohortDetailsDTO> fetch(List<Long> ids) {
return cApi.getAllCohortsById(ids).collectList().block();
}
@Override
protected Long getId(CohortDetailsDTO dto) {
public Long getId(CohortDetailsDTO dto) {
return dto.getId();
}
}
......@@ -20,48 +20,19 @@ package nl.tudelft.queue.cache;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.Getter;
import nl.tudelft.queue.properties.CacheProperties;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
import com.google.common.collect.Sets;
public abstract class CoreCacheManager<ID, DTO> {
protected final Map<ID, DTO> cache = new ConcurrentHashMap<>();
protected final Set<ID> empties = new HashSet<>();
/**
* Fetches the objects currently missing from the cache. These objects are fetched through a list of their
* IDs.
*
* @param ids The list of IDs of objects to fetch.
* @return The list of fetched objects.
*/
protected abstract List<DTO> fetch(List<ID> ids);
/**
* Gets the ID of a particular instance of the data object.
*
* @param dto The DTO data object that will be cached.
* @return The ID of the DTO object.
*/
protected abstract ID getId(DTO dto);
/**
* @return The maximum number of items to be fetched from Labracore in one go.
*/
protected int batchSize() {
return 128;
}
/**
* Used to manually check the status of the cache and invalidate the cache if necessary.
*/
protected synchronized void validateCache() {
}
public interface CoreCacheManager<ID, DTO> {
/**
* Gets a list of DTOs from a list of ids. The list of ids gets broken up into already cached items and to
* be fetched items. The to be fetched are then fetched and the already cached are just fetched from
......@@ -70,18 +41,19 @@ public abstract class CoreCacheManager<ID, DTO> {
* @param ids The ids to find matching DTOs for.
* @return The list of DTOs found to be matching.
*/
public List<DTO> get(List<ID> ids) {
default List<DTO> get(List<ID> ids) {
validateCache();
List<ID> misses = ids.stream()
.filter(id -> !cache.containsKey(id) && !empties.contains(id))
.filter(id -> !getCache().containsKey(id) && !getEmpties().contains(id))
.collect(Collectors.toList());
if (!misses.isEmpty()) {
forEachBatch(misses, batch -> registerImpl(new HashSet<>(batch), fetch(batch)));
forEachBatch(misses, batch -> register(new HashSet<>(batch), fetch(batch)));
}
return ids.stream()
.map(cache::get)
.filter(Predicate.not(getEmpties()::contains))
.map(getCache()::get)
.collect(Collectors.toList());
}
......@@ -92,7 +64,7 @@ public abstract class CoreCacheManager<ID, DTO> {
* @param ids The stream of ids to be collected and requested from the cache.
* @return The list of DTOs with the given IDs.
*/
public List<DTO> get(Stream<ID> ids) {
default List<DTO> get(Stream<ID> ids) {
return get(ids.collect(Collectors.toList()));
}
......@@ -102,7 +74,7 @@ public abstract class CoreCacheManager<ID, DTO> {
* @param id The id of the DTO to lookup.
* @return The requested DTO if it exists, otherwise nothing.
*/
public Optional<DTO> get(ID id) {
default Optional<DTO> get(ID id) {
if (id == null) {
return Optional.empty();
}
......@@ -111,6 +83,21 @@ public abstract class CoreCacheManager<ID, DTO> {
.map(l -> l.get(0));
}
/**
* Fetches the objects currently missing from the cache. These objects are fetched through a list of their
* IDs.
*
* @param ids The list of IDs of objects to fetch.
* @return The list of fetched objects.
*/
List<DTO> fetch(List<ID> ids);
/**
* Used to manually check the status of the cache and invalidate the cache if necessary.
*/
default void validateCache() {
}
/**
* Gets the DTO with the given id or throws an exception with status code 404 if none such DTO could be
* found.
......@@ -118,7 +105,7 @@ public abstract class CoreCacheManager<ID, DTO> {
* @param id The id of the DTO to find.
* @return The found DTO with the given id.
*/
public DTO getOrThrow(ID id) {
default DTO getOrThrow(ID id) {
var l = get(List.of(id));
if (l == null || l.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find DTO by id " + id);
......@@ -131,39 +118,14 @@ public abstract class CoreCacheManager<ID, DTO> {
*
* @param dto The DTO that is to be registered.
*/
public void register(DTO dto) {
validateCache();
registerImpl(dto);
}
void register(DTO dto);
/**
* Registers a list of entries into the cache after validating the cache first.
*
* @param dtos The DTOs that are to be registered.
*/
public void register(List<DTO> dtos) {
validateCache();
registerImpl(dtos);
}
/**
* Implementation of the register method. Registers an entry into the cache.
*
* @param dto The DTO that is to be registered.
*/
private void registerImpl(DTO dto) {
cache.put(getId(dto), dto);
registerAdditionally(dto);
}
/**
* Implementation of the register method. Registers a list of entries into the cache.
*
* @param dtos The DTOs that are to be registered.
*/
private void registerImpl(List<DTO> dtos) {
dtos.forEach(this::registerImpl);
}
void register(List<DTO> dtos);
/**
* Implementation of the register method. Registers a list of entries into the cache and also registers
......@@ -172,24 +134,23 @@ public abstract class CoreCacheManager<ID, DTO> {
* @param requests The set of requested IDs.
* @param dtos The list of DTOs returned from the request.
*/
private void registerImpl(Set<ID> requests, List<DTO> dtos) {
dtos.forEach(this::registerImpl);
empties.addAll(Sets.difference(requests,
private void register(Set<ID> requests, List<DTO> dtos) {
register(dtos);
getEmpties().addAll(Sets.difference(requests,
dtos.stream().map(this::getId).collect(Collectors.toSet())));
}
/**
* Performs additional registers in other caches if necessary.
*
* @param dto The DTO that is being registered.
* @return The maximum number of items to be fetched from Labracore in one go.
*/
protected void registerAdditionally(DTO dto) {
default int getBatchSize() {
return getSettings().getBatchSize();
}
/**
* Helper function for iterating over batches of items. The batch size is determined through the
* {@link #batchSize()} method in implementations of this class. Each created batch will be passed on to
* the consumer function f.
* {@link #getBatchSize()} method in implementations of this class. Each created batch will be passed on
* to the consumer function f.
*
* @param ids The list of ids that are to be batched.
* @param f The consumer of each batch.
......@@ -199,7 +160,7 @@ public abstract class CoreCacheManager<ID, DTO> {
for (ID id : ids) {
batch.add(id);
if (batch.size() >= batchSize()) {
if (batch.size() >= getBatchSize()) {
f.accept(batch);
batch.clear();
}
......@@ -210,4 +171,78 @@ public abstract class CoreCacheManager<ID, DTO> {
}
}
/**
* Gets the ID of a particular instance of the data object.
*
* @param dto The DTO data object that will be cached.
* @return The ID of the DTO object.
*/
ID getId(DTO dto);
/**
* @return The cache containing stored objects mapped by their IDs.
*/
Map<ID, DTO> getCache();
/**
* @return The set of all IDs that are confirmed to not have a mirroring entry in Labracore.
*/
Set<ID> getEmpties();
/**
* @return The settings for this cache.
*/
CacheProperties.CacheSettings getSettings();
abstract class BaseCoreCacheManager<ID, DTO> implements CoreCacheManager<ID, DTO> {
@Getter
protected final Map<ID, DTO> cache = new ConcurrentHashMap<>();
@Getter
protected final Set<ID> empties = new HashSet<>();
@Override
public void register(DTO dto) {
validateCache();
registerImpl(dto);
}
@Override
public void register(List<DTO> dtos) {
validateCache();
registerImpl(dtos);
}
/**
* Implementation of the register method. Registers an entry into the cache.
*
* @param dto The DTO that is to be registered.
*/
private void registerImpl(DTO dto) {
cache.put(getId(dto), dto);
registerAdditionally(dto);
}
/**
* Implementation of the register method. Registers a list of entries into the cache.
*
* @param dtos The DTOs that are to be registered.
*/
private void registerImpl(List<DTO> dtos) {
dtos.forEach(this::registerImpl);
}
/**
* Performs additional registers in other caches if necessary.
*
* @param dto The DTO that is being registered.
*/
protected void registerAdditionally(DTO dto) {
}
@Override
public CacheProperties.CacheSettings getSettings() {
return new CacheProperties.CacheSettings();
}
}
}
......@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import nl.tudelft.labracore.api.CourseControllerApi;
import nl.tudelft.labracore.api.dto.CourseDetailsDTO;
import nl.tudelft.labracore.api.dto.CourseSummaryDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -30,7 +31,7 @@ import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class CourseCacheManager extends CoreCacheManager<Long, CourseDetailsDTO> {
public class CourseCacheManager extends BaseCoreCacheManager<Long, CourseDetailsDTO> {
@Autowired
private CourseControllerApi api;
......@@ -43,12 +44,12 @@ public class CourseCacheManager extends CoreCacheManager<Long, CourseDetailsDTO>
}
@Override
protected List<CourseDetailsDTO> fetch(List<Long> ids) {
public List<CourseDetailsDTO> fetch(List<Long> ids) {
return api.getAllCoursesById(ids).collectList().block();
}
@Override
protected Long getId(CourseDetailsDTO dto) {
public Long getId(CourseDetailsDTO dto) {
return dto.getId();
}
}
......@@ -21,6 +21,7 @@ import java.util.List;
import nl.tudelft.labracore.api.EditionControllerApi;
import nl.tudelft.labracore.api.dto.EditionDetailsDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -28,17 +29,17 @@ import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class EditionCacheManager extends CoreCacheManager<Long, EditionDetailsDTO> {
public class EditionCacheManager extends BaseCoreCacheManager<Long, EditionDetailsDTO> {
@Autowired
private EditionControllerApi api;
@Override
protected List<EditionDetailsDTO> fetch(List<Long> ids) {
public List<EditionDetailsDTO> fetch(List<Long> ids) {
return api.getEditionsById(ids).collectList().block();
}
@Override
protected Long getId(EditionDetailsDTO dto) {
public Long getId(EditionDetailsDTO dto) {
return dto.getId();
}
}
......@@ -21,6 +21,7 @@ import java.util.List;
import nl.tudelft.labracore.api.EditionCollectionControllerApi;
import nl.tudelft.labracore.api.dto.EditionCollectionDetailsDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -28,17 +29,17 @@ import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class EditionCollectionCacheManager extends CoreCacheManager<Long, EditionCollectionDetailsDTO> {
public class EditionCollectionCacheManager extends BaseCoreCacheManager<Long, EditionCollectionDetailsDTO> {
@Autowired
private EditionCollectionControllerApi api;
@Override
protected List<EditionCollectionDetailsDTO> fetch(List<Long> ids) {
public List<EditionCollectionDetailsDTO> fetch(List<Long> ids) {
return api.getEditionCollectionsById(ids).collectList().block();
}
@Override
protected Long getId(EditionCollectionDetailsDTO dto) {
public Long getId(EditionCollectionDetailsDTO dto) {
return dto.getId();
}
}
......@@ -17,52 +17,84 @@
*/
package nl.tudelft.queue.cache;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Data;
import nl.tudelft.labracore.api.EditionControllerApi;
import lombok.Getter;
import nl.tudelft.labracore.api.RoleControllerApi;
import nl.tudelft.labracore.api.dto.RolePersonDetailsDTO;
import nl.tudelft.queue.properties.CacheProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class EditionRolesCacheManager extends CoreCacheManager<Long, EditionRolesCacheManager.RoleHolder> {
public interface EditionRolesCacheManager
extends CoreCacheManager<Long, EditionRolesCacheManager.RoleHolder> {
/**
* Holds a list of roles cached by the ID of the edition that was to be looked up.
*/
@Data
@AllArgsConstructor
public static class RoleHolder {
class RoleHolder {
private Long id;
private List<RolePersonDetailsDTO> roles;
private final List<RolePersonDetailsDTO> roles = new ArrayList<>();
}
private final EditionControllerApi eApi;
@Override
default List<RoleHolder> fetch(List<Long> ids) {
var holders = ids.stream()
.collect(Collectors.toMap(Function.identity(), RoleHolder::new));
public EditionRolesCacheManager(@Autowired EditionControllerApi eApi) {
this.eApi = eApi;
}
getRoleApi().getRolesByEditions(ids).toStream()
.forEach(role -> holders.get(role.getId().getEditionId()).roles.add(role));
@Override
protected List<RoleHolder> fetch(List<Long> ids) {
return ids.stream()
.map(id -> new RoleHolder(id, eApi.getEditionParticipants(id).collectList().block()))
.collect(Collectors.toList());
return new ArrayList<>(holders.values());
}
@Override
protected Long getId(RoleHolder roleHolder) {
default Long getId(RoleHolder roleHolder) {
return roleHolder.id;
}
@Override
protected int batchSize() {
// Set batch size to MAX because we already do batches of n=1 in fetch.
return Integer.MAX_VALUE;
default CacheProperties.CacheSettings getSettings() {
return getCacheProperties().getPerson();
}
RoleControllerApi getRoleApi();
CacheProperties getCacheProperties();
@Getter
@Service
@ConditionalOnProperty(name = "queue.cache.edition-roles.type", havingValue = "timed")
class Timed extends BaseCoreCacheManager<Long, RoleHolder>
implements EditionRolesCacheManager {
@Autowired
private RoleControllerApi roleApi;
@Autowired
private CacheProperties cacheProperties;
}
@Getter
@Component
@RequestScope
@ConditionalOnMissingBean(value = EditionRolesCacheManager.class)
class RequestScoped extends BaseCoreCacheManager<Long, RoleHolder>
implements EditionRolesCacheManager {
@Autowired
private RoleControllerApi roleApi;
@Autowired
private CacheProperties cacheProperties;
}
}
......@@ -21,6 +21,7 @@ import java.util.List;
import nl.tudelft.labracore.api.ModuleControllerApi;
import nl.tudelft.labracore.api.dto.ModuleDetailsDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -28,22 +29,22 @@ import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class ModuleCacheManager extends CoreCacheManager<Long, ModuleDetailsDTO> {
public class ModuleCacheManager extends BaseCoreCacheManager<Long, ModuleDetailsDTO> {
@Autowired
private ModuleControllerApi api;
@Override
protected List<ModuleDetailsDTO> fetch(List<Long> ids) {
public List<ModuleDetailsDTO> fetch(List<Long> ids) {
return api.getModulesById(ids).collectList().block();
}
@Override
protected Long getId(ModuleDetailsDTO dto) {
public Long getId(ModuleDetailsDTO dto) {
return dto.getId();
}
@Override
protected int batchSize() {
public int getBatchSize() {
return 1;
}
}
......@@ -21,6 +21,8 @@ import java.util.List;
import nl.tudelft.labracore.api.PersonControllerApi;
import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
import nl.tudelft.queue.cache.TimedCacheManager.BaseTimedCacheManager;
import nl.tudelft.queue.properties.CacheProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -28,24 +30,25 @@ import org.springframework.web.context.annotation.ApplicationScope;
@Component
@ApplicationScope
public class PersonCacheManager extends TimedCacheManager<Long, PersonSummaryDTO> {
public class PersonCacheManager extends BaseTimedCacheManager<Long, PersonSummaryDTO> {
@Autowired
private PersonControllerApi api;
/**
* Creates a new person cache by setting the timeout on its timed cache implementation to 5 minutes.
*/
public PersonCacheManager() {
super(5 * 60 * 1000L);
}
@Autowired
private CacheProperties cp;
@Override
protected List<PersonSummaryDTO> fetch(List<Long> ids) {
public List<PersonSummaryDTO> fetch(List<Long> ids) {
return api.getPeopleById(ids).collectList().block();
}
@Override
protected Long getId(PersonSummaryDTO dto) {
public Long getId(PersonSummaryDTO dto) {
return dto.getId();
}
@Override
public CacheProperties.CacheSettings getSettings() {
return cp.getPerson();
}
}
......@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import nl.tudelft.labracore.api.RoleControllerApi;
import nl.tudelft.labracore.api.dto.Id;
import nl.tudelft.labracore.api.dto.RoleDetailsDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -30,7 +31,7 @@ import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class RoleCacheManager extends CoreCacheManager<Id, RoleDetailsDTO> {
public class RoleCacheManager extends BaseCoreCacheManager<Id, RoleDetailsDTO> {
@Autowired
private RoleControllerApi api;
......@@ -38,7 +39,7 @@ public class RoleCacheManager extends CoreCacheManager<Id, RoleDetailsDTO> {
private PersonCacheManager pCache;
@Override
protected List<RoleDetailsDTO> fetch(List<Id> ids) {
public List<RoleDetailsDTO> fetch(List<Id> ids) {
return api.getRolesById(
ids.stream().map(Id::getEditionId).collect(Collectors.toList()),
ids.stream().map(Id::getPersonId).collect(Collectors.toList()))
......@@ -46,7 +47,7 @@ public class RoleCacheManager extends CoreCacheManager<Id, RoleDetailsDTO> {
}
@Override
protected Id getId(RoleDetailsDTO dto) {
public Id getId(RoleDetailsDTO dto) {
return dto.getId();
}
......
......@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import nl.tudelft.labracore.api.RoomControllerApi;
import nl.tudelft.labracore.api.dto.RoomDetailsDTO;
import nl.tudelft.labracore.api.dto.RoomSummaryDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -30,7 +31,7 @@ import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class RoomCacheManager extends CoreCacheManager<Long, RoomDetailsDTO> {
public class RoomCacheManager extends BaseCoreCacheManager<Long, RoomDetailsDTO> {
@Autowired
private RoomControllerApi api;
......@@ -43,12 +44,12 @@ public class RoomCacheManager extends CoreCacheManager<Long, RoomDetailsDTO> {
}
@Override
protected List<RoomDetailsDTO> fetch(List<Long> ids) {
public List<RoomDetailsDTO> fetch(List<Long> ids) {
return api.getAllRoomsById(ids).collectList().block();
}
@Override
protected Long getId(RoomDetailsDTO dto) {
public Long getId(RoomDetailsDTO dto) {
return dto.getId();
}
}
......@@ -23,6 +23,7 @@ import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import nl.tudelft.labracore.api.SessionControllerApi;
import nl.tudelft.labracore.api.dto.SessionDetailsDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -32,17 +33,17 @@ import org.springframework.web.context.annotation.RequestScope;
@RequestScope
@NoArgsConstructor
@AllArgsConstructor
public class SessionCacheManager extends CoreCacheManager<Long, SessionDetailsDTO> {
public class SessionCacheManager extends BaseCoreCacheManager<Long, SessionDetailsDTO> {
@Autowired
private SessionControllerApi api;
@Override
protected List<SessionDetailsDTO> fetch(List<Long> ids) {
public List<SessionDetailsDTO> fetch(List<Long> ids) {
return api.getSessionsById(ids).collectList().block();
}
@Override
protected Long getId(SessionDetailsDTO dto) {
public Long getId(SessionDetailsDTO dto) {
return dto.getId();
}
}
......@@ -29,6 +29,7 @@ import nl.tudelft.labracore.api.StudentGroupControllerApi;
import nl.tudelft.labracore.api.dto.RolePersonLayer1DTO;
import nl.tudelft.labracore.api.dto.StudentGroupDetailsDTO;
import nl.tudelft.labracore.api.dto.StudentGroupSummaryDTO;
import nl.tudelft.queue.cache.CoreCacheManager.BaseCoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
......@@ -39,7 +40,7 @@ import org.springframework.web.context.annotation.RequestScope;
@RequestScope
@NoArgsConstructor
@AllArgsConstructor
public class StudentGroupCacheManager extends CoreCacheManager<Long, StudentGroupDetailsDTO> {
public class StudentGroupCacheManager extends BaseCoreCacheManager<Long, StudentGroupDetailsDTO> {
private final Map<Long, List<StudentGroupDetailsDTO>> attemptedPersons = new HashMap<>();
@Autowired
......@@ -68,7 +69,7 @@ public class StudentGroupCacheManager extends CoreCacheManager<Long, StudentGrou
}
@Override
protected List<StudentGroupDetailsDTO> fetch(@Nullable List<Long> ids) {
public List<StudentGroupDetailsDTO> fetch(@Nullable List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return List.of();
}
......@@ -76,7 +77,7 @@ public class StudentGroupCacheManager extends CoreCacheManager<Long, StudentGrou
}
@Override
protected Long getId(StudentGroupDetailsDTO dto) {
public Long getId(StudentGroupDetailsDTO dto) {
return dto.getId();
}
......
......@@ -17,47 +17,54 @@
*/
package nl.tudelft.queue.cache;
public abstract class TimedCacheManager<ID, DTO> extends CoreCacheManager<ID, DTO> {
/**
* The default timeout of the timed cache.
*/
public static final long DEFAULT_TIMEOUT = 60000L;
import lombok.Getter;
import lombok.Setter;
public interface TimedCacheManager<ID, DTO> extends CoreCacheManager<ID, DTO> {
/**
* The set timeout of the timed cache in milliseconds. This many milliseconds after the last cache
* @return The set timeout of the timed cache in milliseconds. This many milliseconds after the last cache
* invalidation, this cache is invalidated again.
*/
private final long timeout;
default long getTimeout() {
return getSettings().getTimeout().toMillis();
}
/**
* The last time in milliseconds since epoch (system-time) that this cache was (in)validated.
* {@link #timeout} milliseconds after this timestamp, the cache is invalidated (again).
* @return The last time in milliseconds since epoch (system-time) that this cache was (in)validated.
* {@link #getTimeout()} milliseconds after this timestamp, the cache is invalidated (again).
*/
private long lastValidation = System.currentTimeMillis();
long getLastValidation();
/**
* Constructs a new timed cache with the timeout set to {@link #DEFAULT_TIMEOUT}.
* Sets the last validation time on this cache manager.
*
* @param lastValidation The time in millis that the cache was last validated.
*/
public TimedCacheManager() {
this.timeout = DEFAULT_TIMEOUT;
void setLastValidation(long lastValidation);
@Override
default void validateCache() {
long now = System.currentTimeMillis();
if (getLastValidation() + getTimeout() < now) {
getCache().clear();
getEmpties().clear();
setLastValidation(now);
}
}
abstract class BaseTimedCacheManager<ID, DTO> extends BaseCoreCacheManager<ID, DTO>
implements TimedCacheManager<ID, DTO> {
/**
* Constructs a new timed cache with the timeout set to the given timeout.
*
* @param timeout The timeout to set internally.
* The last time in milliseconds since epoch (system-time) that this cache was (in)validated.
* {@link #getTimeout()} milliseconds after this timestamp, the cache is invalidated (again).
*/
public TimedCacheManager(long timeout) {
this.timeout = timeout;
}
@Getter
@Setter
private long lastValidation = System.currentTimeMillis();
@Override
protected synchronized void validateCache() {
long now = System.currentTimeMillis();
if (lastValidation + timeout < now) {
cache.clear();
empties.clear();
lastValidation = now;
public synchronized void validateCache() {
TimedCacheManager.super.validateCache();
}
}
}
......@@ -28,7 +28,8 @@ import org.springframework.context.annotation.Configuration;
JitsiProperties.class,
PushProperties.class,
InstituteProperties.class,
StartupProperties.class
StartupProperties.class,
CacheProperties.class
})
public class PropertiesConfig {
}
/*
* Queue - A Queueing system that can be used to handle labs in higher education
* Copyright (C) 2016-2021 Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.queue.properties;
import static nl.tudelft.queue.properties.CacheProperties.CacheType.PER_REQUEST;
import static nl.tudelft.queue.properties.CacheProperties.CacheType.TIMED;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
@Data
@ConfigurationProperties(prefix = "queue.cache")
public class CacheProperties {
/**
* Cache settings for the person cache bean.
*/
private CacheSettings person = new CacheSettings(TIMED);
/**
* Cache settings for the edition roles cache bean.
*/
private CacheSettings editionRoles;
@Data
@NoArgsConstructor
public static class CacheSettings {
/**
* The number of ids that get fetched by the cache in a single call to Labracore.
*/
private int batchSize = 256;
/**
* The type of cache that is used for this specific cache type.
*/
private CacheType type = PER_REQUEST;
/**
* The time after which the timed cache is cleared. Does nothing when the cache type is per-request.
* (By default this duration is in milliseconds)
*/
@DurationUnit(ChronoUnit.MILLIS)
private Duration timeout = Duration.ofMillis(5 * 60 * 1000);
public CacheSettings(CacheType type) {
this.type = type;
}
}
/**
* The types of cache that could be used.
*/
public enum CacheType {
PER_REQUEST,
TIMED
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment