diff --git a/CHANGELOG.md b/CHANGELOG.md
index e7716a4141b69299010bf893bb8fe9544d81a1c7..9bd57e816d8f17f31c92e8bbb37099fb257712d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,11 +10,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+
+### Added
+
+### Changed
+
+### Fixed
+
+### Deprecated
+
+### Removed
+
+## [2.1.4]
+
 ### Added
+ - Students are now provided with a suggestion of their most recent location within a lab when they requeue. [@hpage](https://gitlab.ewi.tudelft.nl/hpage)
 
 ### Changed
+ - Deleting a lab will now show a dialog with a warning. [@hpage](https://gitlab.ewi.tudelft.nl/hpage)
 
 ### Fixed
+ - Requests are now soft-deleted when the corresponding lab is deleted. [@hpage](https://gitlab.ewi.tudelft.nl/hpage)
+ - Students can no longer enqueue twice. [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
 
 ### Deprecated
 
diff --git a/build.gradle.kts b/build.gradle.kts
index ef3e0e3f3f2323a929a46467f6e84cbb51cbac99..2f53b8d1ca9174de7bd7b318547176921f2eb96d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,11 +4,11 @@ import com.diffplug.gradle.spotless.SpotlessExtension
 import org.springframework.boot.gradle.tasks.run.BootRun
 
 group = "nl.tudelft.ewi.queue"
-version = "2.1.3"
+version = "2.1.4"
 
 val javaVersion = JavaVersion.VERSION_17
 
-val libradorVersion = "1.0.3-SNAPSHOT6"
+val libradorVersion = "1.2.2-SNAPSHOT"
 val labradoorVersion = "1.3.5"
 val queryDslVersion = "4.4.0"
 
diff --git a/src/main/java/nl/tudelft/queue/cache/AssignmentCacheManager.java b/src/main/java/nl/tudelft/queue/cache/AssignmentCacheManager.java
index 5dbe488d4320dbdce903517d2acd743b63183c6b..2641417d860c40d0387ad649a275dbcf409fa6de 100644
--- a/src/main/java/nl/tudelft/queue/cache/AssignmentCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/AssignmentCacheManager.java
@@ -21,6 +21,7 @@ import java.util.List;
 
 import nl.tudelft.labracore.api.AssignmentControllerApi;
 import nl.tudelft.labracore.api.dto.AssignmentDetailsDTO;
+import nl.tudelft.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/ClusterCacheManager.java b/src/main/java/nl/tudelft/queue/cache/ClusterCacheManager.java
index 84972774ba151f731a23fb103516782ebaef950c..49b8ac772da73dcd3250530a433ec8bfee0f226a 100644
--- a/src/main/java/nl/tudelft/queue/cache/ClusterCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/ClusterCacheManager.java
@@ -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.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/CohortCacheManager.java b/src/main/java/nl/tudelft/queue/cache/CohortCacheManager.java
index cde48d7f70da921b38b39c42eb3f372892b5a02b..9d01f2470ab9cc74ae6a514fbb9f3f334d6e2240 100644
--- a/src/main/java/nl/tudelft/queue/cache/CohortCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/CohortCacheManager.java
@@ -21,6 +21,7 @@ import java.util.List;
 
 import nl.tudelft.labracore.api.CohortControllerApi;
 import nl.tudelft.labracore.api.dto.CohortDetailsDTO;
+import nl.tudelft.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/CoreCacheManager.java b/src/main/java/nl/tudelft/queue/cache/CoreCacheManager.java
deleted file mode 100644
index e1e309f37f2a340304aabf576948f99c1c606413..0000000000000000000000000000000000000000
--- a/src/main/java/nl/tudelft/queue/cache/CoreCacheManager.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * 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.cache;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-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() {
-	}
-
-	/**
-	 * 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
-	 * cache.
-	 *
-	 * @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) {
-		validateCache();
-
-		List<ID> misses = ids.stream()
-				.filter(id -> !cache.containsKey(id) && !empties.contains(id))
-				.collect(Collectors.toList());
-		if (!misses.isEmpty()) {
-			forEachBatch(misses, batch -> registerImpl(new HashSet<>(batch), fetch(batch)));
-		}
-
-		return ids.stream()
-				.map(cache::get)
-				.collect(Collectors.toList());
-	}
-
-	/**
-	 * Gets a list of DTOs from a stream of IDs. This method is mostly used as a convenience method wrapping
-	 * {@link #get(List)}.
-	 *
-	 * @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) {
-		return get(ids.collect(Collectors.toList()));
-	}
-
-	/**
-	 * Gets a single DTO from its id. If the DTO is already cached, no external lookup is done.
-	 *
-	 * @param  id The id of the DTO to lookup.
-	 * @return    The requested DTO if it exists, otherwise nothing.
-	 */
-	public Optional<DTO> get(ID id) {
-		if (id == null) {
-			return Optional.empty();
-		}
-		return Optional.of(get(List.of(id)))
-				.filter(l -> !l.isEmpty())
-				.map(l -> l.get(0));
-	}
-
-	/**
-	 * Gets the DTO with the given id or throws an exception with status code 404 if none such DTO could be
-	 * found.
-	 *
-	 * @param  id The id of the DTO to find.
-	 * @return    The found DTO with the given id.
-	 */
-	public 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);
-		}
-		return l.get(0);
-	}
-
-	/**
-	 * Registers an entry into the cache after validating the cache first.
-	 *
-	 * @param dto The DTO that is to be registered.
-	 */
-	public void register(DTO dto) {
-		validateCache();
-		registerImpl(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);
-	}
-
-	/**
-	 * Implementation of the register method. Registers a list of entries into the cache and also registers
-	 * the IDs that were requested but not returned as 'empty' IDs.
-	 *
-	 * @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,
-				dtos.stream().map(this::getId).collect(Collectors.toSet())));
-	}
-
-	/**
-	 * Performs additional registers in other caches if necessary.
-	 *
-	 * @param dto The DTO that is being registered.
-	 */
-	protected void registerAdditionally(DTO dto) {
-	}
-
-	/**
-	 * 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.
-	 *
-	 * @param ids The list of ids that are to be batched.
-	 * @param f   The consumer of each batch.
-	 */
-	private void forEachBatch(List<ID> ids, Consumer<List<ID>> f) {
-		List<ID> batch = new ArrayList<>();
-		for (ID id : ids) {
-			batch.add(id);
-
-			if (batch.size() >= batchSize()) {
-				f.accept(batch);
-				batch.clear();
-			}
-		}
-
-		if (!batch.isEmpty()) {
-			f.accept(batch);
-		}
-	}
-
-}
diff --git a/src/main/java/nl/tudelft/queue/cache/CourseCacheManager.java b/src/main/java/nl/tudelft/queue/cache/CourseCacheManager.java
index a841a89a44e23436f095b1e5b1fa29be257a2b5b..b88979fb006d9c0e0afa91f2b7cd2de46ce347ff 100644
--- a/src/main/java/nl/tudelft/queue/cache/CourseCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/CourseCacheManager.java
@@ -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.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/EditionCacheManager.java b/src/main/java/nl/tudelft/queue/cache/EditionCacheManager.java
index b1ef979159d7339a4b8bcfedef4207692bc3fe84..1f086bafa4d5f6af6b7f90fcff4657d786b48122 100644
--- a/src/main/java/nl/tudelft/queue/cache/EditionCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/EditionCacheManager.java
@@ -21,6 +21,7 @@ import java.util.List;
 
 import nl.tudelft.labracore.api.EditionControllerApi;
 import nl.tudelft.labracore.api.dto.EditionDetailsDTO;
+import nl.tudelft.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/EditionCollectionCacheManager.java b/src/main/java/nl/tudelft/queue/cache/EditionCollectionCacheManager.java
index c1c37e0646697e80de01053b3ef7346c89b296a4..b65cb9068baf896900303b5c428170b9713585b1 100644
--- a/src/main/java/nl/tudelft/queue/cache/EditionCollectionCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/EditionCollectionCacheManager.java
@@ -21,6 +21,7 @@ import java.util.List;
 
 import nl.tudelft.labracore.api.EditionCollectionControllerApi;
 import nl.tudelft.labracore.api.dto.EditionCollectionDetailsDTO;
+import nl.tudelft.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/EditionRolesCacheManager.java b/src/main/java/nl/tudelft/queue/cache/EditionRolesCacheManager.java
index ce9ebe998c178569cc96ed7e54e0bbeaf6b3062c..4d040be8ef580611d14e025c7c119ddf55ad8b6f 100644
--- a/src/main/java/nl/tudelft/queue/cache/EditionRolesCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/EditionRolesCacheManager.java
@@ -24,6 +24,7 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import nl.tudelft.labracore.api.EditionControllerApi;
 import nl.tudelft.labracore.api.dto.RolePersonDetailsDTO;
+import nl.tudelft.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/ModuleCacheManager.java b/src/main/java/nl/tudelft/queue/cache/ModuleCacheManager.java
index 3ab0cf2e4fd1ad0b0bd05a719a95396ff6bd7f81..13905ea2c81b482f3bd7a1067f8ea9f77cdbf0c5 100644
--- a/src/main/java/nl/tudelft/queue/cache/ModuleCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/ModuleCacheManager.java
@@ -21,6 +21,7 @@ import java.util.List;
 
 import nl.tudelft.labracore.api.ModuleControllerApi;
 import nl.tudelft.labracore.api.dto.ModuleDetailsDTO;
+import nl.tudelft.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/PersonCacheManager.java b/src/main/java/nl/tudelft/queue/cache/PersonCacheManager.java
index 476877f9846c3403a50f8da52fc2ee42719573a9..85c3587b3e7e41d7180982f805688cfeb1eb1334 100644
--- a/src/main/java/nl/tudelft/queue/cache/PersonCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/PersonCacheManager.java
@@ -21,6 +21,7 @@ import java.util.List;
 
 import nl.tudelft.labracore.api.PersonControllerApi;
 import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
+import nl.tudelft.librador.cache.TimedCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/RoleCacheManager.java b/src/main/java/nl/tudelft/queue/cache/RoleCacheManager.java
index 277de71911c1c2bfc82d674407dd5461fc89d7b1..a6f6a4c7782a55c627db6816c1236517c15ba9d0 100644
--- a/src/main/java/nl/tudelft/queue/cache/RoleCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/RoleCacheManager.java
@@ -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.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/RoomCacheManager.java b/src/main/java/nl/tudelft/queue/cache/RoomCacheManager.java
index 6e4765c41a6808ae748bf910b3e1201225bbff2d..a98e225bb8a02b1cc8f3b39c40ed889b1821d3f5 100644
--- a/src/main/java/nl/tudelft/queue/cache/RoomCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/RoomCacheManager.java
@@ -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.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/SessionCacheManager.java b/src/main/java/nl/tudelft/queue/cache/SessionCacheManager.java
index d6ca827405103f8a97d708f4780afbc512748628..a6654f49ee8c44faf13eb0a7bcbfeb92c9da9d4f 100644
--- a/src/main/java/nl/tudelft/queue/cache/SessionCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/SessionCacheManager.java
@@ -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.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
diff --git a/src/main/java/nl/tudelft/queue/cache/StudentGroupCacheManager.java b/src/main/java/nl/tudelft/queue/cache/StudentGroupCacheManager.java
index 286f15067d80c7ca1a45d5f23d909926c33e5e3a..8badc3273692afa578d4873723b0b13a79f29857 100644
--- a/src/main/java/nl/tudelft/queue/cache/StudentGroupCacheManager.java
+++ b/src/main/java/nl/tudelft/queue/cache/StudentGroupCacheManager.java
@@ -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.librador.cache.CoreCacheManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.lang.Nullable;
diff --git a/src/main/java/nl/tudelft/queue/cache/TimedCacheManager.java b/src/main/java/nl/tudelft/queue/cache/TimedCacheManager.java
deleted file mode 100644
index c4fd1645d4ebafa1878e150ea0422f10527c9801..0000000000000000000000000000000000000000
--- a/src/main/java/nl/tudelft/queue/cache/TimedCacheManager.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.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;
-
-	/**
-	 * 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;
-
-	/**
-	 * 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).
-	 */
-	private long lastValidation = System.currentTimeMillis();
-
-	/**
-	 * Constructs a new timed cache with the timeout set to {@link #DEFAULT_TIMEOUT}.
-	 */
-	public TimedCacheManager() {
-		this.timeout = DEFAULT_TIMEOUT;
-	}
-
-	/**
-	 * Constructs a new timed cache with the timeout set to the given timeout.
-	 *
-	 * @param timeout The timeout to set internally.
-	 */
-	public TimedCacheManager(long timeout) {
-		this.timeout = timeout;
-	}
-
-	@Override
-	protected synchronized void validateCache() {
-		long now = System.currentTimeMillis();
-		if (lastValidation + timeout < now) {
-			cache.clear();
-			empties.clear();
-			lastValidation = now;
-		}
-	}
-}
diff --git a/src/main/java/nl/tudelft/queue/controller/AdminController.java b/src/main/java/nl/tudelft/queue/controller/AdminController.java
index ae26344f00e4d0ad1cc30838894d790963027463..775c54388f7d846a7b15782072ed397114c3bb16 100644
--- a/src/main/java/nl/tudelft/queue/controller/AdminController.java
+++ b/src/main/java/nl/tudelft/queue/controller/AdminController.java
@@ -129,11 +129,11 @@ public class AdminController {
 	@GetMapping("/running")
 	public String getRunningLabs(Model model) {
 		LocalDateTime now = LocalDateTime.now();
-		var runningEditions = eCache.get(Objects.requireNonNull(
+		var runningEditions = eCache.getAndIgnoreMissing(Objects.requireNonNull(
 				eApi.getAllEditionsActiveAtDate(LocalDateTime.now())
 						.map(EditionSummaryDTO::getId).collectList().block()));
 
-		var runningSessions = sCache.get(runningEditions.stream()
+		var runningSessions = sCache.getAndIgnoreMissing(runningEditions.stream()
 				.flatMap(e -> e.getSessions().stream())
 				.filter(s -> s.getEnd().isAfter(now))
 				.map(SessionSummaryDTO::getId));
diff --git a/src/main/java/nl/tudelft/queue/controller/AssignmentController.java b/src/main/java/nl/tudelft/queue/controller/AssignmentController.java
index d66572f5124aaeecd3ce15928a1d84b6452f967a..7f1b31112707b53239ee7d09581cedd59e6b9d12 100644
--- a/src/main/java/nl/tudelft/queue/controller/AssignmentController.java
+++ b/src/main/java/nl/tudelft/queue/controller/AssignmentController.java
@@ -82,6 +82,9 @@ public class AssignmentController {
 	public String createAssignment(@PathVariable Long mId,
 			QueueAssignmentCreateDTO dto,
 			Model model) {
+
+		ModuleDetailsDTO module = mCache.getRequired(mId);
+
 		dto.setModuleId(mId);
 
 		AssignmentCreateDTO create = dto.apply();
@@ -91,7 +94,6 @@ public class AssignmentController {
 
 		aApi.addAssignment(create).block();
 
-		ModuleDetailsDTO module = mCache.getOrThrow(mId);
 		return "redirect:/edition/" + module.getEdition().getId() + "/modules";
 	}
 
@@ -107,13 +109,14 @@ public class AssignmentController {
 	@PreAuthorize("@permissionService.canManageAssignment(#assignmentId)")
 	public String getAssignmentRemovePage(@PathVariable Long assignmentId,
 			Model model) {
-		var assignment = aCache.getOrThrow(assignmentId);
-		var module = mCache.getOrThrow(assignment.getModule().getId());
-		var edition = eCache.getOrThrow(module.getEdition().getId());
+		var assignment = aCache.getRequired(assignmentId);
+		var module = mCache.getRequired(assignment.getModule().getId());
+		var edition = eCache.getRequired(module.getEdition().getId());
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 		model.addAttribute("assignment", assignment);
 
@@ -130,8 +133,8 @@ public class AssignmentController {
 	@PostMapping("/assignment/{assignmentId}/remove")
 	@PreAuthorize("@permissionService.canManageAssignment(#assignmentId)")
 	public String removeAssignment(@PathVariable Long assignmentId) {
-		var assignment = aCache.getOrThrow(assignmentId);
-		var module = mCache.getOrThrow(assignment.getModule().getId());
+		var assignment = aCache.getRequired(assignmentId);
+		var module = mCache.getRequired(assignment.getModule().getId());
 
 		aApi.deleteAssignment(assignmentId).block();
 
@@ -148,12 +151,13 @@ public class AssignmentController {
 	 */
 	private String addCreateAssignmentAttributes(Long moduleId, QueueAssignmentCreateDTO dto,
 			Model model) {
-		ModuleDetailsDTO module = mCache.getOrThrow(moduleId);
-		EditionDetailsDTO edition = eCache.getOrThrow(module.getEdition().getId());
+		ModuleDetailsDTO module = mCache.getRequired(moduleId);
+		EditionDetailsDTO edition = eCache.getRequired(module.getEdition().getId());
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 		model.addAttribute("_module", module);
 
diff --git a/src/main/java/nl/tudelft/queue/controller/EditionController.java b/src/main/java/nl/tudelft/queue/controller/EditionController.java
index 3ffd7223c069abd59ee3e2706c94337fc86690d6..f370d8bbd8d785afc17382fe0d1912a023b35d2b 100644
--- a/src/main/java/nl/tudelft/queue/controller/EditionController.java
+++ b/src/main/java/nl/tudelft/queue/controller/EditionController.java
@@ -48,13 +48,10 @@ import nl.tudelft.queue.model.LabRequest;
 import nl.tudelft.queue.model.enums.QueueSessionType;
 import nl.tudelft.queue.model.labs.Lab;
 import nl.tudelft.queue.repository.QueueSessionRepository;
-import nl.tudelft.queue.service.EditionService;
-import nl.tudelft.queue.service.EditionStatusService;
-import nl.tudelft.queue.service.PermissionService;
-import nl.tudelft.queue.service.QuestionService;
-import nl.tudelft.queue.service.RoleDTOService;
+import nl.tudelft.queue.service.*;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.core.io.Resource;
 import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.Pageable;
@@ -141,6 +138,10 @@ public class EditionController {
 	@Autowired
 	private QuestionService qs;
 
+	@Autowired
+	@Lazy
+	private LabService ls;
+
 	/**
 	 * Gets the page listing all editions. This page incorporates pageables to index many different editions
 	 * and keep the overview small.
@@ -164,7 +165,7 @@ public class EditionController {
 				.block();
 
 		eCache.register(editions.getContent());
-		erCache.get(editions.getContent().stream().map(EditionDetailsDTO::getId));
+		erCache.getAndIgnoreMissing(editions.getContent().stream().map(EditionDetailsDTO::getId));
 
 		model.addAttribute("editions",
 				new PageImpl<>(editions.getContent(), pageable, editions.getTotalElements()));
@@ -224,7 +225,7 @@ public class EditionController {
 		if (!ps.canEnrolForEdition(editionId)) {
 			return "redirect:/editions";
 		}
-		model.addAttribute("edition", eCache.getOrThrow(editionId));
+		model.addAttribute("edition", eCache.getRequired(editionId));
 
 		return "edition/enrol";
 	}
@@ -256,11 +257,12 @@ public class EditionController {
 	 */
 	@GetMapping("/edition/{editionId}")
 	public String getEditionView(@PathVariable Long editionId, Model model) {
-		EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 
 		return "edition/view/info";
@@ -279,7 +281,7 @@ public class EditionController {
 	public String getEditionParticipantsView(@PathVariable Long editionId, Model model,
 			@RequestParam(value = "student-search", required = false) String studentSearch,
 			@PageableDefault(size = 25) Pageable pageable) {
-		var edition = eCache.getOrThrow(editionId);
+		var edition = eCache.getRequired(editionId);
 		var students = roleDTOService.students(edition);
 
 		if (studentSearch != null) {
@@ -288,7 +290,8 @@ public class EditionController {
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 		model.addAttribute("students", toPage(pageable, students));
 
@@ -307,8 +310,8 @@ public class EditionController {
 	@GetMapping("/edition/{editionId}/modules")
 	@PreAuthorize("@permissionService.canViewEdition(#editionId)")
 	public String getEditionModulesView(@PathVariable Long editionId, Model model) {
-		var edition = eCache.getOrThrow(editionId);
-		var modules = mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId));
+		var edition = eCache.getRequired(editionId);
+		var modules = mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId));
 
 		modules.sort(Comparator.comparing(ModuleDetailsDTO::getName));
 		modules.forEach(m -> {
@@ -316,7 +319,7 @@ public class EditionController {
 			m.getAssignments().sort(Comparator.comparing(AssignmentSummaryDTO::getName));
 		});
 
-		sgCache.get(modules.stream()
+		sgCache.getAndIgnoreMissing(modules.stream()
 				.flatMap(m -> m.getGroups().stream().map(StudentGroupSmallSummaryDTO::getId)));
 
 		model.addAttribute("edition", edition);
@@ -325,7 +328,8 @@ public class EditionController {
 				modules.stream().flatMap(m -> m.getAssignments().stream()).toList());
 		model.addAttribute("groups", modules.stream()
 				.collect(Collectors.toMap(ModuleDetailsDTO::getId,
-						m -> sgCache.get(m.getGroups().stream().map(StudentGroupSmallSummaryDTO::getId)))));
+						m -> sgCache.getAndIgnoreMissing(
+								m.getGroups().stream().map(StudentGroupSmallSummaryDTO::getId)))));
 
 		return "edition/view/modules";
 	}
@@ -345,11 +349,11 @@ public class EditionController {
 			@RequestParam(required = false, defaultValue = "") List<QueueSessionType> queueSessionTypes,
 			@RequestParam(required = false, defaultValue = "") List<Long> modules,
 			Model model) {
-		var edition = eCache.getOrThrow(editionId);
+		var edition = eCache.getRequired(editionId);
 
 		// Make sure that session details are cached.
-		var sessions = sCache.get(edition.getSessions().stream()
-				.map(SessionSummaryDTO::getId));
+		var sessions = sCache.getAndHandleAll(edition.getSessions().stream()
+				.map(SessionSummaryDTO::getId), ls.deleteSessionsByIds());
 
 		// Find all labs from the sessions, convert to summaries, and filter.
 		var labs = es.filterLabs(
@@ -366,7 +370,8 @@ public class EditionController {
 		model.addAttribute("allModules", edition.getModules());
 		model.addAttribute("modules", modules);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 
 		return "edition/view/labs";
@@ -382,11 +387,12 @@ public class EditionController {
 	@GetMapping("/edition/{editionId}/questions")
 	@PreAuthorize("@permissionService.canManageQuestions(#editionId)")
 	public String getEditionQuestions(@PathVariable Long editionId, Model model) {
-		EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 		model.addAttribute("questions", qApi.getQuestionsByEdition(editionId).collectList().block());
 
@@ -403,11 +409,12 @@ public class EditionController {
 	@GetMapping("/edition/{editionId}/participants/create")
 	@PreAuthorize("@permissionService.canManageParticipants(#editionId)")
 	public String getAddParticipantPage(@PathVariable Long editionId, Model model) {
-		EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 		model.addAttribute("role", new QueueRoleCreateDTO(editionId));
 
@@ -430,11 +437,12 @@ public class EditionController {
 
 		RoleCreateDTO create = dto.apply();
 		if (dto.hasErrors()) {
-			EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+			EditionDetailsDTO edition = eCache.getRequired(editionId);
 
 			model.addAttribute("edition", edition);
 			model.addAttribute("assignments",
-					mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+					mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+							.stream()
 							.flatMap(m -> m.getAssignments().stream()).toList());
 			model.addAttribute("role", dto);
 
@@ -470,7 +478,7 @@ public class EditionController {
 	public String importParticipants(@PathVariable Long editionId,
 			@RequestParam("file") MultipartFile csv,
 			RedirectAttributes attributes) {
-		EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
 		try {
 			es.addCourseParticipants(csv, edition);
@@ -495,13 +503,14 @@ public class EditionController {
 	@PreAuthorize("@permissionService.canManageParticipants(#editionId)")
 	public String getRemoveParticipantPage(@PathVariable Long editionId, @PathVariable Long personId,
 			Model model) {
-		EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
-		model.addAttribute("role", rCache.getOrThrow(new Id()
+		model.addAttribute("role", rCache.getRequired(new Id()
 				.personId(personId).editionId(editionId)));
 
 		return "edition/remove/participant";
@@ -548,10 +557,11 @@ public class EditionController {
 	@PreAuthorize("@permissionService.canLeaveEdition(#editionId)")
 	public String getParticipantLeavePage(@PathVariable Long editionId,
 			Model model) {
-		EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+		EditionDetailsDTO edition = eCache.getRequired(editionId);
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 
 		return "edition/view/leave";
@@ -586,12 +596,14 @@ public class EditionController {
 	@PreAuthorize("@permissionService.canViewEditionStatus(#editionId)")
 	public String status(@PathVariable Long editionId,
 			Model model) {
-		var edition = eCache.getOrThrow(editionId);
-		var sessions = sCache.get(edition.getSessions().stream().map(SessionSummaryDTO::getId));
+		var edition = eCache.getRequired(editionId);
+		var sessions = sCache.getAndHandleAll(edition.getSessions().stream().map(SessionSummaryDTO::getId),
+				ls.deleteSessionsByIds());
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 
 		model.addAttribute("activeLabs", getLabSummariesFromSessions(
@@ -599,9 +611,9 @@ public class EditionController {
 		model.addAttribute("inactiveLabs", getLabSummariesFromSessions(
 				sessions, s -> !(s.getStart().isBefore(now()) && now().isBefore(s.getEnd()))));
 
-		model.addAttribute("assignments", aCache.get(sessions.stream()
+		model.addAttribute("assignments", aCache.getAndIgnoreMissing(sessions.stream()
 				.flatMap(s -> s.getAssignments().stream().map(AssignmentSummaryDTO::getId)).distinct()));
-		model.addAttribute("rooms", rmCache.get(sessions.stream()
+		model.addAttribute("rooms", rmCache.getAndIgnoreMissing(sessions.stream()
 				.flatMap(s -> s.getRooms().stream().map(RoomDetailsDTO::getId)).distinct()));
 
 		return "edition/view/status";
@@ -704,11 +716,12 @@ public class EditionController {
 	@PreAuthorize("@permissionService.canManageQuestions(#editionId)")
 	public String getEditQuestionPage(@PathVariable Long editionId, @PathVariable Long questionId,
 			Model model) {
-		EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 		model.addAttribute("question", qApi.getQuestionById(questionId).block());
 		model.addAttribute("assignments",
@@ -749,11 +762,12 @@ public class EditionController {
 	@PreAuthorize("@permissionService.canManageQuestions(#editionId)")
 	public String getDeleteQuestionPage(@PathVariable Long editionId, @PathVariable Long questionId,
 			Model model) {
-		EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("assignments",
-				mCache.get(edition.getModules().stream().map(ModuleSummaryDTO::getId)).stream()
+				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
+						.stream()
 						.flatMap(m -> m.getAssignments().stream()).toList());
 		model.addAttribute("question", qApi.getQuestionById(questionId).block());
 
diff --git a/src/main/java/nl/tudelft/queue/controller/HistoryController.java b/src/main/java/nl/tudelft/queue/controller/HistoryController.java
index 2dbfaf9b23370602199206c63d3bfe9a8db09481..49c985066eaa72a7d01cef2fff8a24e367da7f20 100644
--- a/src/main/java/nl/tudelft/queue/controller/HistoryController.java
+++ b/src/main/java/nl/tudelft/queue/controller/HistoryController.java
@@ -20,6 +20,8 @@ package nl.tudelft.queue.controller;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import javax.transaction.Transactional;
+
 import nl.tudelft.labracore.api.PersonControllerApi;
 import nl.tudelft.labracore.api.StudentGroupControllerApi;
 import nl.tudelft.labracore.api.dto.EditionDetailsDTO;
@@ -34,6 +36,7 @@ import nl.tudelft.queue.cache.PersonCacheManager;
 import nl.tudelft.queue.cache.SessionCacheManager;
 import nl.tudelft.queue.repository.LabRequestRepository;
 import nl.tudelft.queue.repository.QueueSessionRepository;
+import nl.tudelft.queue.service.LabService;
 import nl.tudelft.queue.service.RequestTableService;
 
 import org.springframework.beans.factory.annotation.Autowired;
@@ -76,6 +79,9 @@ public class HistoryController {
 	@Autowired
 	private RequestTableService rts;
 
+	@Autowired
+	private LabService ls;
+
 	/**
 	 * Gets the view representing the request history of the currently authenticated student.
 	 *
@@ -111,14 +117,14 @@ public class HistoryController {
 			@PathVariable Long editionId,
 			@PathVariable Long studentId,
 			@PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC, size = 25) Pageable pageable) {
-		var edition = eCache.getOrThrow(editionId);
+		var edition = eCache.getRequired(editionId);
 
 		List<Long> groups = sgApi.getGroupsForPersonAndCourse(studentId, edition.getCourse().getId())
 				.map(StudentGroupSummaryDTO::getId)
 				.collectList().block();
 
 		model.addAttribute("edition", edition);
-		model.addAttribute("student", pCache.getOrThrow(studentId));
+		model.addAttribute("student", pCache.getRequired(studentId));
 
 		setHistoryModel("/history/edition/" + editionId + "/student/" + studentId,
 				model, studentId, groups, pageable);
@@ -136,18 +142,21 @@ public class HistoryController {
 	 * @param groupIds The ids of the groups to find requests for.
 	 * @param pageable The pageable containing the information on the page to show.
 	 */
-	private void setHistoryModel(String key, Model model, Long studentId, List<Long> groupIds,
+	@Transactional
+	public void setHistoryModel(String key, Model model, Long studentId, List<Long> groupIds,
 			Pageable pageable) {
-		var editions = eCache.get(pApi.getRolesForPerson(studentId)
+		var editions = eCache.getAndIgnoreMissing(pApi.getRolesForPerson(studentId)
 				.filter(r -> r.getType() == RoleEditionDetailsDTO.TypeEnum.STUDENT)
 				.map(r -> r.getEdition().getId())
 				.toStream());
-		erCache.get(editions.stream().map(EditionDetailsDTO::getId));
+
+		erCache.getAndIgnoreMissing(editions.stream().map(EditionDetailsDTO::getId));
 
 		var sessionIds = editions.stream()
 				.flatMap(e -> e.getSessions().stream().map(SessionSummaryDTO::getId))
 				.collect(Collectors.toList());
-		sCache.get(sessionIds);
+
+		sCache.getAndHandleAll(sessionIds, ls.deleteSessionsByIds());
 
 		var labs = qsr.findAllBySessions(sessionIds);
 
diff --git a/src/main/java/nl/tudelft/queue/controller/HomeController.java b/src/main/java/nl/tudelft/queue/controller/HomeController.java
index 16cdd161ff245ba5faeb478e9f3dd7c5ffbed088..20766ace181932bc2e824bee04a908a51916fc96 100644
--- a/src/main/java/nl/tudelft/queue/controller/HomeController.java
+++ b/src/main/java/nl/tudelft/queue/controller/HomeController.java
@@ -171,17 +171,17 @@ public class HomeController {
 							Set.of(TEACHER_RO).contains(role.getType()))
 					.collect(Collectors.toList());
 
-			var editions = eCache.get(roles.stream().map(r -> r.getEdition().getId()))
+			var editions = eCache.getAndIgnoreMissing(roles.stream().map(r -> r.getEdition().getId()))
 					.stream()
 					.collect(Collectors.toMap(EditionDetailsDTO::getId, Function.identity()));
 
-			Set<SessionDetailsDTO> sessions = new HashSet<>(sCache.get(editions.values().stream()
+			Set<SessionDetailsDTO> sessions = new HashSet<>(sCache.getAndHandleAll(editions.values().stream()
 					.flatMap(e -> e.getSessions().stream())
 					.filter(s -> s.getEnd().isAfter(now.minusMonths(1)))
-					.map(SessionSummaryDTO::getId)));
+					.map(SessionSummaryDTO::getId), missingIds -> ls.deleteSessionsByIds()));
 
 			var sharedEditions = editions.values().stream().map(EditionDetailsDTO::getEditionCollections)
-					.flatMap(List::stream).map(e -> ecCache.getOrThrow(e.getId()))
+					.flatMap(List::stream).map(e -> ecCache.getRequired(e.getId()))
 					.collect(Collectors.toSet());
 
 			var calendarEntries = ls.convertToCalendarEntries(
@@ -198,7 +198,8 @@ public class HomeController {
 			var sharedLabs = sharedEditions.stream().collect(Collectors.toMap(
 					EditionCollectionDetailsDTO::getId,
 					s -> es.sortLabs(View.convert(new ArrayList<>(lr.findAllBySessions(
-							eCache.get(s.getEditions().stream().map(EditionSummaryDTO::getId).toList())
+							eCache.getAndIgnoreMissing(
+									s.getEditions().stream().map(EditionSummaryDTO::getId).toList())
 									.stream().map(EditionDetailsDTO::getSessions).flatMap(List::stream)
 									.map(SessionSummaryDTO::getId).collect(Collectors.toList()))),
 							QueueSessionSummaryDTO.class)).stream()
@@ -207,7 +208,7 @@ public class HomeController {
 							.collect(Collectors.toList())));
 
 			if (pd.getDefaultRole() == ADMIN || pd.getDefaultRole() == TEACHER) {
-				model.addAttribute("allEditions", eCache.get(
+				model.addAttribute("allEditions", eCache.getAndIgnoreMissing(
 						Objects.requireNonNullElse(
 								eApi.getAllEditionsActiveDuringPeriod(
 										new Period().start(LocalDateTime.now())
@@ -280,7 +281,7 @@ public class HomeController {
 	 * @param pageable    The pageable containing information on how much feedback needs to be shown.
 	 */
 	private void fillInFeedbackModel(Long assistantId, Person person, Model model, Pageable pageable) {
-		var assistant = pCache.getOrThrow(assistantId);
+		var assistant = pCache.getRequired(assistantId);
 
 		Page<Feedback> feedback = assistantId.equals(person.getId())
 				? fr.findByAssistantAnonymised(assistantId, pageable)
diff --git a/src/main/java/nl/tudelft/queue/controller/LabController.java b/src/main/java/nl/tudelft/queue/controller/LabController.java
index 259078aa0400b063860fbce1380d299ead2b5b2d..773c15859e3dacef9e774fd926798cc6f54b6895 100644
--- a/src/main/java/nl/tudelft/queue/controller/LabController.java
+++ b/src/main/java/nl/tudelft/queue/controller/LabController.java
@@ -31,6 +31,7 @@ import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
 import javax.transaction.Transactional;
 
 import nl.tudelft.labracore.api.dto.*;
@@ -119,6 +120,9 @@ public class LabController {
 	@Autowired
 	private RoleDTOService roleService;
 
+	@Autowired
+	private HttpSession session;
+
 	/**
 	 * Gets the page with information on the lab with the given id. This page is different for TAs than for
 	 * students. For students, this page shows their history of requests and their current requests. For TAs,
@@ -165,7 +169,7 @@ public class LabController {
 					.ifPresent(r -> model.addAttribute("selectionResult", r.toViewDTO()));
 		}
 
-		model.addAttribute("modules", mCache.get(qSession.getModules().stream()));
+		model.addAttribute("modules", mCache.getAndIgnoreMissing(qSession.getModules().stream()));
 
 		return "lab/view/" + qSession.getType().name().toLowerCase();
 	}
@@ -209,6 +213,10 @@ public class LabController {
 			model.addAttribute("request", new LabRequestCreateDTO());
 			model.addAttribute("rType", "lab");
 
+			if (session.getAttribute("lastKnownLocation") != null) {
+				model.addAttribute("lastKnownLocation", session.getAttribute("lastKnownLocation"));
+			}
+
 			return "lab/enqueue/lab";
 		} else {
 			model.addAttribute("request", new SelectionRequestCreateDTO());
@@ -233,7 +241,7 @@ public class LabController {
 			Model model) {
 		setEnqueuePageAttributes(lab, model, person);
 
-		model.addAttribute("assignment", aCache.getOrThrow(assignment));
+		model.addAttribute("assignment", aCache.getRequired(assignment));
 
 		return "lab/question";
 	}
@@ -254,7 +262,7 @@ public class LabController {
 			Model model) {
 		setEnqueuePageAttributes(lab, model, person);
 
-		model.addAttribute("assignment", aCache.getOrThrow(assignment));
+		model.addAttribute("assignment", aCache.getRequired(assignment));
 		model.addAttribute("question", question);
 		model.addAttribute("request", new LabRequestCreateDTO());
 		model.addAttribute("rType", "lab");
@@ -284,6 +292,11 @@ public class LabController {
 
 		dto.setSession(lab);
 		rs.createRequest(dto, student.getId(), true);
+
+		if (dto.getComment() != null) {
+			session.setAttribute("lastKnownLocation", dto.getComment());
+		}
+
 		return "redirect:/lab/" + lab.getId();
 	}
 
@@ -357,7 +370,7 @@ public class LabController {
 	public String getLabCreateView(@PathVariable Long editionId,
 			@RequestParam QueueSessionType type, Model model) {
 		model.addAttribute("dto", type.newCreateDto());
-		model.addAttribute("edition", eCache.getOrThrow(editionId));
+		model.addAttribute("edition", eCache.getRequired(editionId));
 		model.addAttribute("lType", type);
 
 		setSessionEditingPageAttributes(List.of(editionId), model);
@@ -377,7 +390,7 @@ public class LabController {
 	@PreAuthorize("@permissionService.canManageSharedSessions(#editionCollectionId)")
 	public String getSharedLabCreateView(@PathVariable Long editionCollectionId,
 			@RequestParam QueueSessionType type, Model model) {
-		var editionCollection = ecCache.getOrThrow(editionCollectionId);
+		var editionCollection = ecCache.getRequired(editionCollectionId);
 
 		model.addAttribute("dto", type.newCreateDto());
 		model.addAttribute("ec", editionCollection);
@@ -502,25 +515,7 @@ public class LabController {
 	}
 
 	/**
-	 * Gets the confirmation view for deleting a single lab.
-	 *
-	 * @param  qSession The lab to delete.
-	 * @param  model    The model to fill out for Thymeleaf template resolution.
-	 * @return          The Thymeleaf template to resolve.
-	 */
-	@GetMapping("/lab/{qSession}/remove")
-	@PreAuthorize("@permissionService.canManageSession(#qSession)")
-	public String getSessionDeleteView(@PathEntity QueueSession<?> qSession,
-			Model model) {
-		model.addAttribute("qSession", qSession);
-		ls.setOrganizationInModel(qSession, model);
-
-		return "lab/remove";
-	}
-
-	/**
-	 * Handles a POST request to the session delete endpoint by setting the deleted at date for the given
-	 * session.
+	 * Handles a GET request to the session delete endpoint by soft-deleting the session.
 	 *
 	 * @param  qSession The session to delete.
 	 * @return          A redirect back to the edition lab overview.
@@ -528,8 +523,8 @@ public class LabController {
 	@PostMapping("/lab/{qSession}/remove")
 	@PreAuthorize("@permissionService.canManageSession(#qSession)")
 	public String deleteSession(@PathEntity QueueSession<?> qSession) {
+		var session = sCache.getRequired(qSession.getSession(), id -> ls.deleteSession(qSession));
 		ls.deleteSession(qSession);
-		var session = sCache.getOrThrow(qSession.getSession());
 
 		if (session.getEditionCollection() != null) {
 			return "redirect:/shared-edition/" + session.getEditionCollection().getId();
@@ -550,7 +545,7 @@ public class LabController {
 	@PreAuthorize("@permissionService.canManageSession(#qSession)")
 	public String getLabCopyView(@PathEntity QueueSession<?> qSession,
 			Model model) {
-		var session = sCache.getOrThrow(qSession.getSession());
+		var session = sCache.getRequired(qSession.getSession(), id -> ls.deleteSession(qSession));
 
 		model.addAttribute("dto", qSession.copyLabCreateDTO(session));
 		model.addAttribute("lType", qSession.getType());
@@ -688,8 +683,9 @@ public class LabController {
 	 * @param model    The model to fill using edition and lab information.
 	 * @param person   The person used to fill in the information.
 	 */
-	private void setEnqueuePageAttributes(QueueSession<?> qSession, Model model, Person person) {
-		var session = sCache.getOrThrow(qSession.getSession());
+	@Transactional
+	public void setEnqueuePageAttributes(QueueSession<?> qSession, Model model, Person person) {
+		var session = sCache.getRequired(qSession.getSession(), id -> ls.deleteSession(qSession));
 		session.getRooms().sort(Comparator.comparing(r -> r.getBuilding().getName() + r.getName()));
 
 		model.addAttribute("qSession", qSession.toViewDTO());
@@ -701,26 +697,29 @@ public class LabController {
 			var lab = (Lab) qSession;
 			var isStaffInAll = session.getEditions().stream().allMatch(e -> roleService
 					.rolesForPersonInEdition(e, person).stream().allMatch(roleService::isStaff));
-			List<AssignmentDetailsDTO> allowedAssignments = aCache.get(lab.getAllowedRequests().stream()
-					.map(AllowedRequest::getAssignment).distinct().collect(Collectors.toList()))
+			List<AssignmentDetailsDTO> allowedAssignments = aCache
+					.getAndHandleAll(lab.getAllowedRequests().stream()
+							.map(AllowedRequest::getAssignment).distinct().collect(Collectors.toList()),
+							assignmentIds -> lab.getAllowedRequests()
+									.removeIf(a -> assignmentIds.contains(a.getAssignment())))
 					.stream().filter(a -> {
-						var edition = mCache.getOrThrow(a.getModule().getId()).getEdition();
+						var edition = mCache.getRequired(a.getModule().getId()).getEdition();
 						return isStaffInAll || roleService.rolesForPersonInEdition(edition, person).stream()
 								.noneMatch(roleService::isStaff);
 					}).toList();
 			Map<Long, String> assignments = allowedAssignments.stream()
 					.collect(Collectors.toMap(AssignmentDetailsDTO::getId, a -> {
-						var edition = mCache.getOrThrow(a.getModule().getId()).getEdition();
+						var edition = mCache.getRequired(a.getModule().getId()).getEdition();
 						return session.getEditions().size() == 1
 								? a.getName()
-								: eCache.getOrThrow(edition.getId()).getCourse().getName() + " - "
+								: eCache.getRequired(edition.getId()).getCourse().getName() + " - "
 										+ a.getName();
 					}));
 			Map<Long, Long> notEnqueueAble = allowedAssignments.stream().filter(a -> {
-				var edition = mCache.getOrThrow(a.getModule().getId()).getEdition();
+				var edition = mCache.getRequired(a.getModule().getId()).getEdition();
 				return roleService.rolesForPersonInEdition(edition, person).isEmpty();
 			}).collect(Collectors.toMap(AssignmentDetailsDTO::getId, a -> {
-				var edition = mCache.getOrThrow(a.getModule().getId()).getEdition();
+				var edition = mCache.getRequired(a.getModule().getId()).getEdition();
 				return edition.getId();
 			}));
 
@@ -730,9 +729,11 @@ public class LabController {
 					.collect(Collectors.groupingBy(AllowedRequest::getAssignment,
 							Collectors.mapping(ar -> ar.getType().displayName(), Collectors.toSet()))));
 		} else {
-			model.addAttribute("modules", mCache.get(qSession.getModules().stream())
-					.stream().sorted(Comparator.comparing(ModuleDetailsDTO::getName))
-					.collect(Collectors.toList()));
+			model.addAttribute("modules",
+					mCache.getAndHandle(qSession.getModules().stream(),
+							moduleID -> qSession.getModules().remove(moduleID))
+							.stream().sorted(Comparator.comparing(ModuleDetailsDTO::getName))
+							.collect(Collectors.toList()));
 		}
 	}
 
@@ -743,14 +744,14 @@ public class LabController {
 	 * @param model      The model to fill using edition information.
 	 */
 	private void setSessionEditingPageAttributes(List<Long> editionIds, Model model) {
-		var editions = eCache.get(editionIds);
+		var editions = eCache.getAndIgnoreMissing(editionIds);
 
-		var modules = mCache.get(editions.stream()
+		var modules = mCache.getAndIgnoreMissing(editions.stream()
 				.flatMap(e -> e.getModules().stream().map(ModuleSummaryDTO::getId).distinct()));
-		var assignments = aCache.get(modules.stream()
+		var assignments = aCache.getAndIgnoreMissing(modules.stream()
 				.flatMap(m -> m.getAssignments().stream().map(AssignmentSummaryDTO::getId)).distinct());
 		var clusters = cCache
-				.get(editions.stream().map(e -> e.getCohort().getId()).distinct())
+				.getAndIgnoreMissing(editions.stream().map(e -> e.getCohort().getId()).distinct())
 				.stream().flatMap(cohort -> cohort.getClusters().stream()).distinct()
 				.sorted(Comparator.comparing(ClusterSummaryDTO::getName)).collect(Collectors.toList());
 
@@ -787,13 +788,13 @@ public class LabController {
 	 * @param model    The model to fill using lab information.
 	 */
 	private void setSessionEditingPageAttributes(QueueSession<?> qSession, Model model) {
-		var session = sCache.getOrThrow(qSession.getSession());
+		var session = sCache.getRequired(qSession.getSession(), id -> ls.deleteSession(qSession));
 		model.addAttribute("lSession", session);
 
 		if (session.getEdition() != null) {
-			model.addAttribute("edition", eCache.getOrThrow(session.getEdition().getId()));
+			model.addAttribute("edition", eCache.getRequired(session.getEdition().getId()));
 		} else {
-			model.addAttribute("ec", ecCache.getOrThrow(session.getEditionCollection().getId()));
+			model.addAttribute("ec", ecCache.getRequired(session.getEditionCollection().getId()));
 		}
 
 		setSessionEditingPageAttributes(session.getEditions().stream()
@@ -813,7 +814,8 @@ public class LabController {
 		Matcher matcher = testPattern.matcher(request.getRequestURI());
 		if (matcher.matches() && matcher.groupCount() == 1) {
 			Optional<QueueSession<?>> lab = qsRepository.findById(Long.parseLong(matcher.group(1)));
-			var session = sCache.getOrThrow(lab.orElseThrow().getSession());
+			var session = sCache.getRequired(lab.orElseThrow().getSession(),
+					id -> ls.deleteSession(lab.get()));
 			var edition = session.getEdition().getId();
 			return "redirect:/edition/" + edition + "/enrol";
 		}
diff --git a/src/main/java/nl/tudelft/queue/controller/ModuleController.java b/src/main/java/nl/tudelft/queue/controller/ModuleController.java
index a86c0aed9e402b5c200de3660f1bbcaf1c33cf5c..2429b42fd28f54bdf50995af7970760a7ba4ccdd 100644
--- a/src/main/java/nl/tudelft/queue/controller/ModuleController.java
+++ b/src/main/java/nl/tudelft/queue/controller/ModuleController.java
@@ -95,8 +95,8 @@ public class ModuleController {
 	@PreAuthorize("@permissionService.canManageModule(#moduleId)")
 	public String getModuleRemovePage(@PathVariable Long moduleId,
 			Model model) {
-		var module = mCache.getOrThrow(moduleId);
-		var edition = eCache.getOrThrow(module.getEdition().getId());
+		var module = mCache.getRequired(moduleId);
+		var edition = eCache.getRequired(module.getEdition().getId());
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("_module", module);
@@ -113,8 +113,8 @@ public class ModuleController {
 	@PostMapping("/module/{moduleId}/remove")
 	@PreAuthorize("@permissionService.canManageModule(#moduleId)")
 	public String removeModule(@PathVariable Long moduleId) {
-		var module = mCache.getOrThrow(moduleId);
-		var edition = eCache.getOrThrow(module.getEdition().getId());
+		var module = mCache.getRequired(moduleId);
+		var edition = eCache.getRequired(module.getEdition().getId());
 
 		mApi.deleteModule(moduleId).block();
 
@@ -131,7 +131,7 @@ public class ModuleController {
 	 * @return           The Thymeleaf template to resolve.
 	 */
 	private String addCreateModuleAttributes(Long editionId, QueueModuleCreateDTO dto, Model model) {
-		EditionDetailsDTO edition = eCache.getOrThrow(editionId);
+		EditionDetailsDTO edition = eCache.getRequired(editionId);
 
 		model.addAttribute("edition", edition);
 		model.addAttribute("dto", dto);
diff --git a/src/main/java/nl/tudelft/queue/controller/RequestController.java b/src/main/java/nl/tudelft/queue/controller/RequestController.java
index 5e640964ea03443b68c3e7981ca6e1e42508414f..3d427167a590ce89ef0f733a24399fe2e8e89e2e 100644
--- a/src/main/java/nl/tudelft/queue/controller/RequestController.java
+++ b/src/main/java/nl/tudelft/queue/controller/RequestController.java
@@ -27,6 +27,7 @@ import java.util.stream.Collectors;
 
 import javax.transaction.Transactional;
 
+import nl.tudelft.labracore.api.AssignmentControllerApi;
 import nl.tudelft.labracore.api.PersonControllerApi;
 import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
 import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
@@ -74,6 +75,9 @@ public class RequestController {
 	@Autowired
 	private PersonControllerApi pApi;
 
+	@Autowired
+	private AssignmentControllerApi aApi;
+
 	@Autowired
 	private RoleDTOService rds;
 
@@ -359,9 +363,10 @@ public class RequestController {
 	@PreAuthorize("@permissionService.canFinishRequest(#request.id)")
 	public String getRequestForwardView(@AuthenticatedPerson Person user,
 			@PathEntity LabRequest request, Model model) {
+
 		List<PersonSummaryDTO> assistants = rds
-				.roles(eCache.getOrThrow(
-						mCache.getOrThrow(aCache.getOrThrow(request.getAssignment()).getModule().getId())
+				.roles(eCache.getRequired(
+						mCache.getRequired(aCache.getRequired(request.getAssignment()).getModule().getId())
 								.getEdition().getId()),
 						Set.of(TA, HEAD_TA, TEACHER))
 				.stream().filter(p -> !p.getId().equals(user.getId())).toList();
@@ -395,7 +400,8 @@ public class RequestController {
 		if (forwardedTo == -1L) {
 			rs.forwardRequestToAnyone(request, assistant, reasonForAssistant);
 		} else {
-			rs.forwardRequestToPerson(request, assistant, pCache.getOrThrow(forwardedTo), reasonForAssistant);
+			rs.forwardRequestToPerson(request, assistant, pCache.getRequired(forwardedTo),
+					reasonForAssistant);
 		}
 
 		redirectAttributes.addFlashAttribute("message", "Request #" + request.getId() + " forwarded");
diff --git a/src/main/java/nl/tudelft/queue/controller/SharedEditionController.java b/src/main/java/nl/tudelft/queue/controller/SharedEditionController.java
index b1fa235f51bff95ac358d54dfe1848cdc3dd7eb4..b2e47e3d8dc15fb88de1a1b54cf7485575bc2032 100644
--- a/src/main/java/nl/tudelft/queue/controller/SharedEditionController.java
+++ b/src/main/java/nl/tudelft/queue/controller/SharedEditionController.java
@@ -72,8 +72,9 @@ public class SharedEditionController {
 	@GetMapping("/{id}")
 	@PreAuthorize("@permissionService.isAuthenticated()")
 	public String getSharedEdition(@PathVariable Long id, Model model) {
-		var collection = editionCollectionCacheManager.getOrThrow(id);
-		var editions = editionCacheManager.get(
+		var collection = editionCollectionCacheManager.getRequired(id);
+
+		var editions = editionCacheManager.getAndIgnoreMissing(
 				collection.getEditions().stream().map(EditionSummaryDTO::getId).toList());
 
 		var teachers = editions.stream().map(edition -> roleDTOService.teachers(edition))
diff --git a/src/main/java/nl/tudelft/queue/dto/create/QueueSessionCreateDTO.java b/src/main/java/nl/tudelft/queue/dto/create/QueueSessionCreateDTO.java
index 480c00ca34b92c714e550127577d66deb9bad5b2..ad84489636fd6a386f27ee11f65699518996cd94 100644
--- a/src/main/java/nl/tudelft/queue/dto/create/QueueSessionCreateDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/create/QueueSessionCreateDTO.java
@@ -108,7 +108,7 @@ public abstract class QueueSessionCreateDTO<D extends QueueSession<?>> extends C
 	 * @return Room details objects representing the currently selected rooms.
 	 */
 	public List<RoomDetailsDTO> roomDetails() {
-		return getBean(RoomCacheManager.class).get(new ArrayList<>(rooms));
+		return getBean(RoomCacheManager.class).getAndIgnoreMissing(new ArrayList<>(rooms));
 	}
 
 	protected void nonNull(String field, Object o) {
diff --git a/src/main/java/nl/tudelft/queue/dto/create/requests/LabRequestCreateDTO.java b/src/main/java/nl/tudelft/queue/dto/create/requests/LabRequestCreateDTO.java
index 8b535e49adc7ccb51a75fe5bbc6194b54752fb59..e176a56f2bee60fdac0ffaa8661d44cf32ec2d49 100644
--- a/src/main/java/nl/tudelft/queue/dto/create/requests/LabRequestCreateDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/create/requests/LabRequestCreateDTO.java
@@ -80,7 +80,7 @@ public class LabRequestCreateDTO extends RequestCreateDTO<LabRequest, Lab> {
 			errors.rejectValue("onlineMode", "Room or online mode not selected");
 		}
 
-		var session = getBean(SessionCacheManager.class).getOrThrow(this.getSession().getSession());
+		var session = getBean(SessionCacheManager.class).getRequired(this.getSession().getSession());
 		if (room != null && session.getRooms().stream().noneMatch(r -> Objects.equals(r.getId(), room))) {
 			errors.rejectValue("room", "A room with id " + room + " is not available in the lab.");
 		}
@@ -99,7 +99,7 @@ public class LabRequestCreateDTO extends RequestCreateDTO<LabRequest, Lab> {
 
 	@Override
 	public Long getModule() {
-		return getBean(AssignmentCacheManager.class).getOrThrow(assignment)
+		return getBean(AssignmentCacheManager.class).getRequired(assignment)
 				.getModule().getId();
 	}
 
diff --git a/src/main/java/nl/tudelft/queue/dto/patch/RequestPatchDTO.java b/src/main/java/nl/tudelft/queue/dto/patch/RequestPatchDTO.java
index c6f18b3bf902a343f78c1dbfc1c4c47534cc2d54..8e5271d2b7da892ec2f9946b105661f0f2b78ab6 100644
--- a/src/main/java/nl/tudelft/queue/dto/patch/RequestPatchDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/patch/RequestPatchDTO.java
@@ -17,10 +17,11 @@
  */
 package nl.tudelft.queue.dto.patch;
 
+import static nl.tudelft.librador.SpringContext.getBean;
+
 import java.util.Objects;
 
 import lombok.*;
-import nl.tudelft.librador.SpringContext;
 import nl.tudelft.librador.dto.patch.Patch;
 import nl.tudelft.queue.cache.SessionCacheManager;
 import nl.tudelft.queue.model.LabRequest;
@@ -46,8 +47,8 @@ public class RequestPatchDTO extends Patch<LabRequest> {
 
 	@Override
 	protected void validate() {
-		var session = SpringContext.getBean(SessionCacheManager.class)
-				.getOrThrow(data.getSession().getSession());
+		var session = getBean(SessionCacheManager.class)
+				.getRequired(data.getSession().getSession());
 		if (room != null && session.getRooms().stream().noneMatch(r -> Objects.equals(r.getId(), room))) {
 			errors.rejectValue("room", "Room is not found in the surrounding lab");
 		}
diff --git a/src/main/java/nl/tudelft/queue/dto/view/CalendarEntryViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/CalendarEntryViewDTO.java
index 5fa5277ca295ae5bad77bd4ec078e2d0b43c343a..65120971b2ddb314f2cc0579e620a706ba8b2ca7 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/CalendarEntryViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/CalendarEntryViewDTO.java
@@ -48,7 +48,7 @@ public class CalendarEntryViewDTO extends View<Lab> {
 	public void postApply() {
 		super.postApply();
 
-		session = SpringContext.getBean(SessionCacheManager.class).getOrThrow(data.getSession());
+		session = SpringContext.getBean(SessionCacheManager.class).getRequired(data.getSession());
 
 		if (data instanceof AbstractSlottedLab) {
 			AbstractSlottedLab lab = (AbstractSlottedLab) data;
diff --git a/src/main/java/nl/tudelft/queue/dto/view/ExamRequestViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/ExamRequestViewDTO.java
index 519bf9242b1325bbc981465c8965be749eb2e2e5..11e78a080aeff2ba2ea0e4a096afa684b7ae1f9d 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/ExamRequestViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/ExamRequestViewDTO.java
@@ -43,6 +43,6 @@ public class ExamRequestViewDTO extends View<LabRequest> {
 	public void postApply() {
 		super.postApply();
 
-		requesterName = getBean(PersonCacheManager.class).getOrThrow(data.getRequester()).getDisplayName();
+		requesterName = getBean(PersonCacheManager.class).getRequired(data.getRequester()).getDisplayName();
 	}
 }
diff --git a/src/main/java/nl/tudelft/queue/dto/view/ExamTimeSlotViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/ExamTimeSlotViewDTO.java
index 0c265b3d65b4d1cc5fa9da0525f7dd204e523720..60c7b414eecead943d5e56d882308ce5d7a86a34 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/ExamTimeSlotViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/ExamTimeSlotViewDTO.java
@@ -58,7 +58,8 @@ public class ExamTimeSlotViewDTO extends View<ClosableTimeSlot> {
 		handled = data.countHandledRequests();
 
 		// Ensure caching of exam request people and then get exam request views
-		getBean(PersonCacheManager.class).get(data.getRequests().stream().map(LabRequest::getRequester));
+		getBean(PersonCacheManager.class)
+				.getAndIgnoreMissing(data.getRequests().stream().map(LabRequest::getRequester));
 		examRequests = View.convert(new ArrayList<>(data.getRequests()), ExamRequestViewDTO.class);
 	}
 
diff --git a/src/main/java/nl/tudelft/queue/dto/view/FeedbackViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/FeedbackViewDTO.java
index 820b8cf1f57f3d61a6f3661d5525ab7e5be89a6f..9967547da457d10885c5a04b23731bf4b19b9d2d 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/FeedbackViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/FeedbackViewDTO.java
@@ -65,7 +65,7 @@ public class FeedbackViewDTO extends View<Feedback> {
 	}
 
 	private String editionName(Long sessionId) {
-		var session = getBean(SessionCacheManager.class).getOrThrow(sessionId);
+		var session = getBean(SessionCacheManager.class).getRequired(sessionId);
 
 		return (session.getEdition() == null) ? session.getEditionCollection().getName()
 				: session.getEdition().getName();
@@ -91,7 +91,7 @@ public class FeedbackViewDTO extends View<Feedback> {
 	 * @return The name of the requesting entity (either the group or the student).
 	 */
 	private String requesterEntityName(Long studentGroupId) {
-		var studentGroup = getBean(StudentGroupCacheManager.class).getOrThrow(studentGroupId);
+		var studentGroup = getBean(StudentGroupCacheManager.class).getRequired(studentGroupId);
 		if (studentGroup.getMemberUsernames().size() == 1) {
 			return studentGroup.getMembers().get(0).getPerson().getDisplayName();
 		} else {
diff --git a/src/main/java/nl/tudelft/queue/dto/view/QueueSessionSummaryDTO.java b/src/main/java/nl/tudelft/queue/dto/view/QueueSessionSummaryDTO.java
index fa7fa48c3cf78d5cec835f680a4f55aa1602a19b..f7e1c4036c824bb4dd8a8e38b9fd810a4b6ec9ef 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/QueueSessionSummaryDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/QueueSessionSummaryDTO.java
@@ -68,7 +68,7 @@ public class QueueSessionSummaryDTO extends View<QueueSession<?>> {
 
 	@Override
 	public void postApply() {
-		var session = getBean(SessionCacheManager.class).getOrThrow(data.getSession());
+		var session = getBean(SessionCacheManager.class).getRequired(data.getSession());
 
 		this.name = session.getName();
 		this.readableName = session.getName() + " : " + DateTimeFormatter.ofPattern("HH:mm - dd " +
@@ -92,7 +92,7 @@ public class QueueSessionSummaryDTO extends View<QueueSession<?>> {
 		isShared = session.getEditionCollection() != null;
 		if (isShared) {
 			var editionCollection = getBean(EditionCollectionCacheManager.class)
-					.getOrThrow(session.getEditionCollection().getId());
+					.getRequired(session.getEditionCollection().getId());
 			associatedEditions = editionCollection.getEditions();
 		} else {
 			associatedEditions = List.of(Objects.requireNonNull(session.getEdition()));
diff --git a/src/main/java/nl/tudelft/queue/dto/view/QueueSessionViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/QueueSessionViewDTO.java
index efef4fad47d4c9e6928635b36b6a3d33a5749367..a5eb722faa32353a4066d41de35538b473698aa8 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/QueueSessionViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/QueueSessionViewDTO.java
@@ -71,9 +71,9 @@ public abstract class QueueSessionViewDTO<D extends QueueSession<?>> extends Vie
 
 	@Override
 	public void postApply() {
-		session = getBean(SessionCacheManager.class).getOrThrow(data.getSession());
+		session = getBean(SessionCacheManager.class).getRequired(data.getSession());
 		modules = new HashSet<>(
-				getBean(ModuleCacheManager.class).get(data.getModules().stream()));
+				getBean(ModuleCacheManager.class).getAndIgnoreMissing(data.getModules().stream()));
 		status = getBean(LabService.class).getLabStatus(data);
 
 		Parser parser = Parser.builder()
diff --git a/src/main/java/nl/tudelft/queue/dto/view/RequestEventInfoViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/RequestEventInfoViewDTO.java
index 26bf22ef2cf184adfe8fd058d671aff50d644984..1ef788c0598961c9252fef2a450d13ff72ff72f4 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/RequestEventInfoViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/RequestEventInfoViewDTO.java
@@ -54,7 +54,7 @@ public class RequestEventInfoViewDTO extends View<RequestEventInfo> {
 	@Override
 	public void postApply() {
 		if (data.getAssignedTo() != null) {
-			assignedTo = SpringContext.getBean(PersonCacheManager.class).getOrThrow(data.getAssignedTo());
+			assignedTo = SpringContext.getBean(PersonCacheManager.class).getRequired(data.getAssignedTo());
 		}
 	}
 }
diff --git a/src/main/java/nl/tudelft/queue/dto/view/RequestViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/RequestViewDTO.java
index 7a9cfa5e03592f35bc49db0c9a4df96ae432d22b..5ee5d87cb09dd514549e125c6593d3c109367c39 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/RequestViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/RequestViewDTO.java
@@ -76,16 +76,16 @@ public abstract class RequestViewDTO<R extends Request<?>> extends View<R>
 				.orElse(new RoomDetailsDTO().id(-1L).abbreviation("")
 						.name("To be determined")
 						.building(new BuildingSummaryDTO().name("TBD")));
-		requester = getBean(PersonCacheManager.class).getOrThrow(data.getRequester());
+		requester = getBean(PersonCacheManager.class).getRequired(data.getRequester());
 
-		session = getBean(SessionCacheManager.class).getOrThrow(qSession.getSession());
-		studentGroup = getBean(StudentGroupCacheManager.class).getOrThrow(data.getStudentGroup());
+		session = getBean(SessionCacheManager.class).getRequired(qSession.getSession());
+		studentGroup = getBean(StudentGroupCacheManager.class).getRequired(data.getStudentGroup());
 
 		if (session.getEdition() != null) {
-			edition = getBean(EditionCacheManager.class).getOrThrow(session.getEdition().getId());
+			edition = getBean(EditionCacheManager.class).getRequired(session.getEdition().getId());
 		} else if (!session.getEditions().isEmpty()) {
 			editionCollection = getBean(EditionCollectionCacheManager.class)
-					.getOrThrow(session.getEditionCollection().getId());
+					.getRequired(session.getEditionCollection().getId());
 		}
 
 		eventInfo.postApply();
diff --git a/src/main/java/nl/tudelft/queue/dto/view/events/RequestForwardedToPersonEventViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/events/RequestForwardedToPersonEventViewDTO.java
index 09201cbd923ba079af05d997c7ff624e153d306f..04b50ba7b0b8af9482586772684379abe50810f1 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/events/RequestForwardedToPersonEventViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/events/RequestForwardedToPersonEventViewDTO.java
@@ -38,7 +38,7 @@ public class RequestForwardedToPersonEventViewDTO extends RequestEventViewDTO<Re
 
 	@Override
 	public void postApply() {
-		forwardedTo = getBean(PersonCacheManager.class).getOrThrow(data.getForwardedTo());
+		forwardedTo = getBean(PersonCacheManager.class).getRequired(data.getForwardedTo());
 	}
 
 	@Override
diff --git a/src/main/java/nl/tudelft/queue/dto/view/events/RequestHandledEventViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/events/RequestHandledEventViewDTO.java
index 22c6aa8200526e83b6136cccf42c663d3d4bd1c0..197ee4cbb73a4fdcb2ce4f675e5c732c9474e501 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/events/RequestHandledEventViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/events/RequestHandledEventViewDTO.java
@@ -40,6 +40,6 @@ public abstract class RequestHandledEventViewDTO<T extends RequestHandledEvent>
 
 	@Override
 	public void postApply() {
-		assistant = getBean(PersonCacheManager.class).getOrThrow(data.getAssistant());
+		assistant = getBean(PersonCacheManager.class).getRequired(data.getAssistant());
 	}
 }
diff --git a/src/main/java/nl/tudelft/queue/dto/view/events/RequestPickedEventViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/events/RequestPickedEventViewDTO.java
index 67db9917b086ef851a923104ef34e7f4ec14c241..fbaf0411e86f5ab0eef647ebb2ecc09d54ea6a99 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/events/RequestPickedEventViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/events/RequestPickedEventViewDTO.java
@@ -37,7 +37,7 @@ public class RequestPickedEventViewDTO extends RequestEventViewDTO<RequestPicked
 
 	@Override
 	public void postApply() {
-		takenBy = getBean(PersonCacheManager.class).getOrThrow(data.getAssistant());
+		takenBy = getBean(PersonCacheManager.class).getRequired(data.getAssistant());
 	}
 
 	@Override
diff --git a/src/main/java/nl/tudelft/queue/dto/view/events/RequestTakenEventViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/events/RequestTakenEventViewDTO.java
index 2054839fef59804d8cdd412fc1eaeb027b356279..26dbaf96286c39470b213da1b6f50a45ff42381a 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/events/RequestTakenEventViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/events/RequestTakenEventViewDTO.java
@@ -37,7 +37,7 @@ public class RequestTakenEventViewDTO extends RequestEventViewDTO<RequestTakenEv
 
 	@Override
 	public void postApply() {
-		takenBy = getBean(PersonCacheManager.class).getOrThrow(data.getAssistant());
+		takenBy = getBean(PersonCacheManager.class).getRequired(data.getAssistant());
 	}
 
 	@Override
diff --git a/src/main/java/nl/tudelft/queue/dto/view/requests/LabRequestViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/requests/LabRequestViewDTO.java
index a861a969294985f9d9277ad73eba64ab17916740..9a4efc940b2194e338022daef0840004252e0324 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/requests/LabRequestViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/requests/LabRequestViewDTO.java
@@ -72,13 +72,13 @@ public class LabRequestViewDTO extends RequestViewDTO<LabRequest> {
 			setRoom(null);
 		}
 
-		assignment = getBean(AssignmentCacheManager.class).getOrThrow(data.getAssignment());
+		assignment = getBean(AssignmentCacheManager.class).getRequired(data.getAssignment());
 		timeSlot = data.getTimeSlot();
 
 		if (super.getEdition() == null) {
-			var editionId = getBean(ModuleCacheManager.class).getOrThrow(assignment.getModule().getId())
+			var editionId = getBean(ModuleCacheManager.class).getRequired(assignment.getModule().getId())
 					.getEdition().getId();
-			super.setEdition(getBean(EditionCacheManager.class).getOrThrow(editionId));
+			super.setEdition(getBean(EditionCacheManager.class).getRequired(editionId));
 		}
 
 		var handledEvent = getEvents().stream()
diff --git a/src/main/java/nl/tudelft/queue/model/QueueSession.java b/src/main/java/nl/tudelft/queue/model/QueueSession.java
index 104b479e2dff390aba2784baa5ac4b6abb5c887b..16502a08391f1ef6a0b326a19281c39fbed392d0 100644
--- a/src/main/java/nl/tudelft/queue/model/QueueSession.java
+++ b/src/main/java/nl/tudelft/queue/model/QueueSession.java
@@ -49,6 +49,7 @@ import org.hibernate.validator.constraints.UniqueElements;
 @Inheritance(strategy = InheritanceType.JOINED)
 @SQLDelete(sql = "UPDATE queue_session SET deleted_at = NOW() WHERE id = ?")
 public abstract class QueueSession<R extends Request<?>> {
+
 	@Id
 	@GeneratedValue(strategy = GenerationType.IDENTITY)
 	private Long id;
diff --git a/src/main/java/nl/tudelft/queue/model/constraints/ClusterConstraint.java b/src/main/java/nl/tudelft/queue/model/constraints/ClusterConstraint.java
index 66e9b63acddec2c5d230aa9747bd14be4b6664f0..b1666dc37e663dae03c72385c2c214e08627fc70 100644
--- a/src/main/java/nl/tudelft/queue/model/constraints/ClusterConstraint.java
+++ b/src/main/java/nl/tudelft/queue/model/constraints/ClusterConstraint.java
@@ -17,6 +17,8 @@
  */
 package nl.tudelft.queue.model.constraints;
 
+import static nl.tudelft.librador.SpringContext.getBean;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Objects;
@@ -26,6 +28,7 @@ import java.util.stream.Collectors;
 import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
 import javax.persistence.OneToOne;
+import javax.transaction.Transactional;
 import javax.validation.constraints.NotNull;
 
 import lombok.AllArgsConstructor;
@@ -34,7 +37,6 @@ import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
 import lombok.experimental.SuperBuilder;
 import nl.tudelft.labracore.api.dto.ClusterDetailsDTO;
-import nl.tudelft.librador.SpringContext;
 import nl.tudelft.queue.cache.ClusterCacheManager;
 import nl.tudelft.queue.dto.create.constraints.ClusterConstraintCreateDTO;
 import nl.tudelft.queue.model.LabRequestConstraint;
@@ -62,11 +64,12 @@ public class ClusterConstraint extends LabRequestConstraint {
 	private QueueSession<?> session;
 
 	@Override
+	@Transactional
 	public boolean canCreateRequest(Long personId) {
 		// Get the clusters in the list of allowed clusters and check whether the person is in one of these.
 		// TODO: Optimize this by creating a method for getting cluster ids per person.
-		return SpringContext.getBean(ClusterCacheManager.class)
-				.get(clusters.stream()).stream()
+		return getBean(ClusterCacheManager.class)
+				.getAndHandle(clusters.stream(), clusterID -> this.clusters.remove(clusterID)).stream()
 				.anyMatch(cluster -> cluster.getPeople().stream()
 						.anyMatch(person -> Objects.equals(person.getId(), personId)));
 	}
@@ -78,8 +81,9 @@ public class ClusterConstraint extends LabRequestConstraint {
 
 	@Override
 	public String constraintDescription() {
-		return "in cluster " + SpringContext.getBean(ClusterCacheManager.class).get(new ArrayList<>(clusters))
-				.stream().map(ClusterDetailsDTO::getName).collect(Collectors.joining(", "));
+		return "in cluster "
+				+ getBean(ClusterCacheManager.class).getAndIgnoreMissing(new ArrayList<>(clusters))
+						.stream().map(ClusterDetailsDTO::getName).collect(Collectors.joining(", "));
 	}
 
 	@Override
diff --git a/src/main/java/nl/tudelft/queue/model/labs/CapacitySession.java b/src/main/java/nl/tudelft/queue/model/labs/CapacitySession.java
index a419aeaaa21cc2d4436aae8dd32f418fdd2d1f4e..8821fadec0ddbdad0a08075b93b4beaffee04116 100644
--- a/src/main/java/nl/tudelft/queue/model/labs/CapacitySession.java
+++ b/src/main/java/nl/tudelft/queue/model/labs/CapacitySession.java
@@ -45,6 +45,7 @@ import nl.tudelft.queue.model.Request;
 import nl.tudelft.queue.model.SelectionRequest;
 import nl.tudelft.queue.model.embeddables.CapacitySessionConfig;
 import nl.tudelft.queue.model.enums.QueueSessionType;
+import nl.tudelft.queue.service.LabService;
 
 @Data
 @Entity
@@ -65,7 +66,8 @@ public class CapacitySession extends QueueSession<SelectionRequest> {
 
 	@Override
 	public boolean allowsRequest(Request<?> request) {
-		var session = getBean(SessionCacheManager.class).getOrThrow(getSession());
+		var session = getBean(SessionCacheManager.class).getRequired(getSession(),
+				id -> getBean(LabService.class).deleteSession(this));
 
 		boolean allowed = request instanceof SelectionRequest && super.allowsRequest(request);
 		if (capacitySessionConfig.getProcedure().isFcfs()) {
diff --git a/src/main/java/nl/tudelft/queue/model/labs/Lab.java b/src/main/java/nl/tudelft/queue/model/labs/Lab.java
index 0ced510627ef11c7247a541816426bf0c1df6f48..da8157a3dfd6519fddadd1c2624cb93a41d74c2f 100644
--- a/src/main/java/nl/tudelft/queue/model/labs/Lab.java
+++ b/src/main/java/nl/tudelft/queue/model/labs/Lab.java
@@ -43,6 +43,7 @@ import nl.tudelft.queue.model.embeddables.AllowedRequest;
 import nl.tudelft.queue.model.enums.CommunicationMethod;
 import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
+import nl.tudelft.queue.service.LabService;
 
 import org.hibernate.validator.constraints.UniqueElements;
 
@@ -134,7 +135,9 @@ public abstract class Lab extends QueueSession<LabRequest> {
 
 	@Override
 	public boolean allowsRequest(Request<?> request) {
-		var session = getBean(SessionCacheManager.class).getOrThrow(getSession());
+
+		var session = getBean(SessionCacheManager.class).getRequired(getSession(),
+				id -> getBean(LabService.class).deleteSession(this));
 
 		return request instanceof LabRequest && super.allowsRequest(request)
 				&& allowsRequest((LabRequest) request)
diff --git a/src/main/java/nl/tudelft/queue/realtime/messages/RequestCreatedMessage.java b/src/main/java/nl/tudelft/queue/realtime/messages/RequestCreatedMessage.java
index 3ef20d8449cb8ccddd0fe2b5fa8bb20b3e829d8e..db7b74d5a5d3ec86aab1a31e623d6b4d7d60404e 100644
--- a/src/main/java/nl/tudelft/queue/realtime/messages/RequestCreatedMessage.java
+++ b/src/main/java/nl/tudelft/queue/realtime/messages/RequestCreatedMessage.java
@@ -82,7 +82,8 @@ public class RequestCreatedMessage extends View<LabRequest> implements Message {
 			courseId = view.getEdition().getCourse().getId();
 		} else if (view.getEditionCollection() != null) {
 			ecId = view.getEditionCollection().getId();
-			editionId = getBean(ModuleCacheManager.class).getOrThrow(view.getAssignment().getModule().getId())
+			editionId = getBean(ModuleCacheManager.class)
+					.getRequired(view.getAssignment().getModule().getId())
 					.getEdition().getId();
 		}
 		organizationName = view.organizationName();
diff --git a/src/main/java/nl/tudelft/queue/realtime/messages/RequestTakenMessage.java b/src/main/java/nl/tudelft/queue/realtime/messages/RequestTakenMessage.java
index b269bb099835c8b16b12e04fd9b137f1048b1b94..887ce717a8eea03d08021ff8a29a66fb0f8a53d0 100644
--- a/src/main/java/nl/tudelft/queue/realtime/messages/RequestTakenMessage.java
+++ b/src/main/java/nl/tudelft/queue/realtime/messages/RequestTakenMessage.java
@@ -38,7 +38,7 @@ public class RequestTakenMessage extends RequestStatusUpdateMessage<RequestTaken
 	public void postApply() {
 		super.postApply();
 
-		takenBy = getBean(PersonCacheManager.class).getOrThrow(data.getAssistant()).getDisplayName();
+		takenBy = getBean(PersonCacheManager.class).getRequired(data.getAssistant()).getDisplayName();
 	}
 
 	@Override
diff --git a/src/main/java/nl/tudelft/queue/service/CapacitySessionService.java b/src/main/java/nl/tudelft/queue/service/CapacitySessionService.java
index b7817b290e1e5f5d05ff36d8c93bcb3561138b0c..bfd9b51974731a2ff8d813bad042cd701bf25990 100644
--- a/src/main/java/nl/tudelft/queue/service/CapacitySessionService.java
+++ b/src/main/java/nl/tudelft/queue/service/CapacitySessionService.java
@@ -41,6 +41,7 @@ import nl.tudelft.queue.repository.CapacitySessionRepository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
@@ -63,6 +64,10 @@ public class CapacitySessionService {
 	@Autowired
 	private PersonCacheManager pCache;
 
+	@Autowired
+	@Lazy
+	private LabService ls;
+
 	private SessionCacheManager sCache;
 
 	private StudentGroupCacheManager sgCache;
@@ -74,7 +79,7 @@ public class CapacitySessionService {
 	 * @return      The number of students in the group.
 	 */
 	private int countStudentMembers(Long sgId) {
-		return (int) sgCache.getOrThrow(sgId).getMembers().stream()
+		return (int) sgCache.getRequired(sgId).getMembers().stream()
 				.filter(role -> role.getType() == RolePersonLayer1DTO.TypeEnum.STUDENT)
 				.count();
 	}
@@ -245,7 +250,7 @@ public class CapacitySessionService {
 	 * @return                         The number of people that should be selected.
 	 */
 	private int getAmountToSelect(CapacitySession lab, List<SelectionRequest> alreadySelectedRequests) {
-		var session = sCache.getOrThrow(lab.getSession());
+		var session = sCache.getRequired(lab.getSession(), id -> ls.deleteSession(lab));
 
 		// Count the number of requests that have already been selected in case someone gets manually selected.
 		int countAlreadySelected = (int) alreadySelectedRequests.stream()
@@ -263,21 +268,6 @@ public class CapacitySessionService {
 		return amountToSelect;
 	}
 
-	/**
-	 * Initializes all caches that are used when running a selection. Pre-fetches all data required for a lab
-	 * so that no more fetched need to be done during the algorithm.
-	 *
-	 * @param labs The labs that selections will be run over.
-	 */
-	private void initializeCaches(List<CapacitySession> labs) {
-		sCache = new SessionCacheManager(sApi);
-		sgCache = new StudentGroupCacheManager(sgApi, pCache);
-
-		sCache.get(labs.stream().map(CapacitySession::getSession));
-		sgCache.get(labs.stream().flatMap(lab -> lab.getRequests().stream())
-				.map(SelectionRequest::getStudentGroup));
-	}
-
 	/**
 	 * Divides the given list of requests that were selected to come over the rooms that are available for the
 	 * lab automatically. Student groups are assigned to a room in the order in which they were selected until
@@ -288,7 +278,7 @@ public class CapacitySessionService {
 	 */
 	@Transactional
 	public void divideSelectedRequests(CapacitySession qSession, List<SelectionRequest> selectedRequests) {
-		var session = sCache.getOrThrow(qSession.getSession());
+		var session = sCache.getRequired(qSession.getSession(), id -> ls.deleteSession(qSession));
 
 		// For every room in the session, initialize a count
 		Map<RoomDetailsDTO, Integer> occupancy = new HashMap<>();
@@ -329,6 +319,22 @@ public class CapacitySessionService {
 		}
 	}
 
+	/**
+	 * Initializes all caches that are used when running a selection. Pre-fetches all data required for a lab
+	 * so that no more fetched need to be done during the algorithm.
+	 *
+	 * @param labs The labs that selections will be run over.
+	 */
+	@Transactional
+	public void initializeCaches(List<CapacitySession> labs) {
+		sCache = new SessionCacheManager(sApi);
+		sgCache = new StudentGroupCacheManager(sgApi, pCache);
+
+		sCache.getAndHandleAll(labs.stream().map(CapacitySession::getSession), ls.deleteSessionsByIds());
+		sgCache.getAndIgnoreMissing(labs.stream().flatMap(lab -> lab.getRequests().stream())
+				.map(SelectionRequest::getStudentGroup));
+	}
+
 	/**
 	 * Runs every minute on the 3rd second of the minute. This method finds all capacity-labs that have to
 	 * have their requests picked. For each of these labs, it selects students up to the capacity of the rooms
@@ -380,7 +386,8 @@ public class CapacitySessionService {
 		}
 		// If we have a fcfs lab we should select new students immediatly.
 		if (session.getCapacitySessionConfig().getProcedure().isFcfs()) {
-			for (RoomDetailsDTO room : sCache.getOrThrow(session.getSession()).getRooms()) {
+			for (RoomDetailsDTO room : sCache
+					.getRequired(session.getSession(), id -> ls.deleteSession(session)).getRooms()) {
 				session.getRequests().stream()
 						.filter(selectionRequest -> !selectionRequest.getEventInfo().getStatus().isSelected())
 						.peek((r) -> r.setRoom(room.getId()))
diff --git a/src/main/java/nl/tudelft/queue/service/EditionStatusService.java b/src/main/java/nl/tudelft/queue/service/EditionStatusService.java
index b2163bb56226d3903ea748a0ea772b49d82b88e6..896d4c6cd084f528483b26d7188b64a2be10f4f1 100644
--- a/src/main/java/nl/tudelft/queue/service/EditionStatusService.java
+++ b/src/main/java/nl/tudelft/queue/service/EditionStatusService.java
@@ -26,6 +26,8 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
+import javax.transaction.Transactional;
+
 import nl.tudelft.labracore.api.dto.AssignmentDetailsDTO;
 import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
 import nl.tudelft.labracore.api.dto.RoomDetailsDTO;
@@ -40,6 +42,7 @@ import nl.tudelft.queue.model.labs.Lab;
 import nl.tudelft.queue.repository.LabRequestRepository;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -59,6 +62,10 @@ public class EditionStatusService {
 	@Autowired
 	private PersonCacheManager pCache;
 
+	@Autowired
+	@Lazy
+	private LabService ls;
+
 	/**
 	 * Creates the buckets used for counting frequencies of events occurring. Buckets are based on the minimum
 	 * time of day a lab in the given list of labs starts and the maximum time of day such a lab ends. The
@@ -74,9 +81,10 @@ public class EditionStatusService {
 	 * @param  nBuckets The number of buckets to create.
 	 * @return          A treeset of interval start times.
 	 */
+	@Transactional
 	public TreeSet<Long> createBucketsOverCourse(List<Lab> labs,
 			int nBuckets) {
-		var sessions = sCache.get(labs.stream().map(Lab::getSession));
+		var sessions = sCache.getAndHandleAll(labs.stream().map(Lab::getSession), ls.deleteSessionsByIds());
 
 		long min = sessions.stream()
 				.map(l -> l.getStart().getLong(SECOND_OF_DAY))
@@ -214,7 +222,7 @@ public class EditionStatusService {
 	 */
 	public Map<String, Long> countRequestsPerAssignment(List<Long> assignments,
 			List<LabRequest> requests) {
-		return aCache.get(assignments).stream()
+		return aCache.getAndIgnoreMissing(assignments).stream()
 				.collect(Collectors.toMap(
 						AssignmentDetailsDTO::getName,
 						a -> countWhere(requests, r -> r.getAssignment().equals(a.getId()))));
@@ -230,7 +238,7 @@ public class EditionStatusService {
 	 *                  occurs in the list of requests.
 	 */
 	public Map<String, Long> countRequestsPerRoom(List<Long> rooms, List<LabRequest> requests) {
-		return rCache.get(rooms).stream()
+		return rCache.getAndIgnoreMissing(rooms).stream()
 				.distinct()
 				.collect(Collectors.toMap(
 						RoomDetailsDTO::getName,
@@ -289,7 +297,7 @@ public class EditionStatusService {
 				.distinct()
 				.filter(Objects::nonNull)
 				.collect(Collectors.toList());
-		Map<Long, String> names = pCache.get(ids)
+		Map<Long, String> names = pCache.getAndIgnoreMissing(ids)
 				.stream()
 				.collect(Collectors.toMap(
 						PersonSummaryDTO::getId,
diff --git a/src/main/java/nl/tudelft/queue/service/FeedbackService.java b/src/main/java/nl/tudelft/queue/service/FeedbackService.java
index dab0b51811f6e6f36c2b9f3f1d0555acd5dbd581..8dc69ea92dac183abdd159866fd29b64e447c7dd 100644
--- a/src/main/java/nl/tudelft/queue/service/FeedbackService.java
+++ b/src/main/java/nl/tudelft/queue/service/FeedbackService.java
@@ -58,7 +58,7 @@ public class FeedbackService {
 	 *                   given request.
 	 */
 	public List<PersonSummaryDTO> assistantsInvolvedInRequest(Long requestId) {
-		return pCache.get(rr.findById(requestId).stream()
+		return pCache.getAndIgnoreMissing(rr.findById(requestId).stream()
 				.flatMap(request -> request.getEventInfo().involvedAssistants().stream()));
 	}
 
diff --git a/src/main/java/nl/tudelft/queue/service/JitsiService.java b/src/main/java/nl/tudelft/queue/service/JitsiService.java
index 9bd2d5990803272eb10f718561d8ec97c369a6b4..204c3576dbc44c0bfb68e7bc8796c1766a30ffc9 100644
--- a/src/main/java/nl/tudelft/queue/service/JitsiService.java
+++ b/src/main/java/nl/tudelft/queue/service/JitsiService.java
@@ -49,7 +49,7 @@ public class JitsiService {
 		UUID uuid = UUID.randomUUID();
 
 		// Lookup the username of the requester and add it to the UUID to make a room name
-		return pCache.getOrThrow(request.getRequester()).getUsername() + "-" +
+		return pCache.getRequired(request.getRequester()).getUsername() + "-" +
 				uuid.toString().substring(0, 8);
 	}
 
diff --git a/src/main/java/nl/tudelft/queue/service/LabService.java b/src/main/java/nl/tudelft/queue/service/LabService.java
index b10bfd080e7bb3ad7019bd6cbde2893309d2a856..79eea8c1d5f22d102d726949917ec18d5c81f22f 100644
--- a/src/main/java/nl/tudelft/queue/service/LabService.java
+++ b/src/main/java/nl/tudelft/queue/service/LabService.java
@@ -23,6 +23,7 @@ import static nl.tudelft.queue.misc.QueueSessionStatus.*;
 import java.io.IOException;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 import javax.transaction.Transactional;
@@ -132,7 +133,7 @@ public class LabService {
 	 * @param model The model to set for Thymeleaf resolution.
 	 */
 	public void setOrganizationInModel(QueueSession<?> lab, Model model) {
-		var session = sCache.getOrThrow(lab.getSession());
+		var session = sCache.getRequired(lab.getSession(), id -> deleteSession(lab));
 		setOrganizationInModel(session, model);
 	}
 
@@ -145,10 +146,10 @@ public class LabService {
 	 */
 	public void setOrganizationInModel(SessionDetailsDTO session, Model model) {
 		model.addAttribute("edition",
-				(session.getEdition() != null) ? eCache.getOrThrow(session.getEdition().getId()) : null);
+				(session.getEdition() != null) ? eCache.getRequired(session.getEdition().getId()) : null);
 		model.addAttribute("ec",
 				(session.getEditionCollection() != null)
-						? ecCache.getOrThrow(session.getEditionCollection().getId())
+						? ecCache.getRequired(session.getEditionCollection().getId())
 						: null);
 	}
 
@@ -167,7 +168,7 @@ public class LabService {
 			header = LabRequestViewDTO.csvHeader();
 		}
 		String[] finalHeader = header;
-		String name = sCache.getOrThrow(qSession.getId()).getName();
+		String name = sCache.getRequired(qSession.getId(), id -> deleteSession(qSession)).getName();
 		var requests = rts.convertRequestsToView(qSession.getRequests());
 		return fs.writeTempResource("session-" + name + "-" + requests.hashCode() + ".csv",
 				() -> CsvHelper.writeCsvValues(finalHeader, requests, ','));
@@ -231,7 +232,7 @@ public class LabService {
 			return GENERIC_CLOSED;
 		}
 
-		var session = sCache.getOrThrow(qSession.getSession());
+		var session = sCache.getRequired(qSession.getSession(), id -> deleteSession(qSession));
 
 		if (session.getEnd().isBefore(now())) {
 			return FINISHED_SESSION;
@@ -360,7 +361,20 @@ public class LabService {
 	 */
 	@Transactional
 	public void deleteSession(QueueSession<?> session) {
-		session.setDeletedAt(now());
+		qsr.delete(session);
+	}
+
+	/**
+	 * Callback for deleting a list of labs/sessions by setting the deletedAt to the current date.
+	 * <p>
+	 * <b>NOTE:</b> This method is not transactional, as it merely returns a lambda, so it should be used in a
+	 * transactional context.
+	 * </p>
+	 *
+	 * @return A consumer which deletes sessions based on the list of ids provided
+	 */
+	public Consumer<List<Long>> deleteSessionsByIds() {
+		return ids -> qsr.deleteAllById(ids);
 	}
 
 	/**
@@ -429,10 +443,11 @@ public class LabService {
 	 * @return        A list of labs which are all occurring within the specified period.
 	 */
 	public List<Lab> getAllLabsWithinPeriod(Period period) {
-		var editions = eCache.get(Objects.requireNonNull(eApi.getAllEditionsActiveDuringPeriod(period)
-				.map(EditionSummaryDTO::getId).collectList().block()));
+		var editions = eCache
+				.getAndIgnoreMissing(Objects.requireNonNull(eApi.getAllEditionsActiveDuringPeriod(period)
+						.map(EditionSummaryDTO::getId).collectList().block()));
 		var sessions = editions.stream().map(EditionDetailsDTO::getSessions).flatMap(List::stream)
-				.collect(Collectors.toList());
+				.toList();
 
 		return lr.findAllBySessions(
 				sessions.stream().map(SessionSummaryDTO::getId).collect(Collectors.toList()));
@@ -444,9 +459,11 @@ public class LabService {
 	 * @param  labs The labs to count for.
 	 * @return      The number of labs which are currenlty open.
 	 */
+	@Transactional
 	public long countOngoingLabs(List<Lab> labs) {
 		LocalDateTime now = LocalDateTime.now();
-		List<SessionDetailsDTO> sessions = sCache.get(labs.stream().map(QueueSession::getSession));
+		List<SessionDetailsDTO> sessions = sCache.getAndHandleAll(labs.stream().map(QueueSession::getSession),
+				deleteSessionsByIds());
 		return sessions.stream().filter(s -> s.getStart().isBefore(now) &&
 				s.getEnd().isAfter(now)).count();
 	}
@@ -465,7 +482,8 @@ public class LabService {
 				case EXAM:
 					AbstractSlottedLab<TimeSlot> lab = (AbstractSlottedLab<TimeSlot>) l;
 					return lab.getSlottedLabConfig().getSelectionOpensAt().isBefore(now)
-							&& sCache.getOrThrow(lab.getSession()).getEnd().isAfter(now);
+							&& sCache.getRequired(lab.getSession(), id -> deleteSession(lab)).getEnd()
+									.isAfter(now);
 				default:
 					return false;
 			}
@@ -481,10 +499,10 @@ public class LabService {
 	 * @return           A list of assignments a TA is allowed to get.
 	 */
 	public List<AssignmentSummaryDTO> getAllowedAssignmentsInLab(Lab lab, Person assistant) {
-		var sessionDetails = sCache.getOrThrow(lab.getSession());
+		var sessionDetails = sCache.getRequired(lab.getSession(), id -> deleteSession(lab));
 		var allowedAssignments = sessionDetails.getAssignments();
 		if (sessionDetails.getEditionCollection() != null) {
-			var collection = ecCache.getOrThrow(sessionDetails.getEditionCollection().getId());
+			var collection = ecCache.getRequired(sessionDetails.getEditionCollection().getId());
 			var roles = collection.getEditions().stream().map(e -> rService.rolesForPersonInEdition(e,
 					assistant)).flatMap(List::stream).filter(rService::isStaff).toList();
 			allowedAssignments = roles.stream()
diff --git a/src/main/java/nl/tudelft/queue/service/PermissionService.java b/src/main/java/nl/tudelft/queue/service/PermissionService.java
index 85c2a68a6f1b6537c165a7b959a1481814a0c32e..809842b40fb0853050eda4ca013173adae2db71b 100644
--- a/src/main/java/nl/tudelft/queue/service/PermissionService.java
+++ b/src/main/java/nl/tudelft/queue/service/PermissionService.java
@@ -281,7 +281,8 @@ public class PermissionService {
 			Function2<Person, RoleDetailsDTO.TypeEnum, Boolean> f) {
 		return withAuthenticatedUser(
 				person -> rCache
-						.get(editionIds.stream().map(e -> new Id().editionId(e).personId(person.getId())))
+						.getAndIgnoreMissing(
+								editionIds.stream().map(e -> new Id().editionId(e).personId(person.getId())))
 						.stream().filter(Objects::nonNull)
 						.anyMatch(role -> f.apply(person, role.getType())));
 	}
diff --git a/src/main/java/nl/tudelft/queue/service/RequestService.java b/src/main/java/nl/tudelft/queue/service/RequestService.java
index f6c047af11a8f187ddbeef997b5a7cd9daff8bdb..c0f89203132a54de64a2fd032b2fd08a8483b0e6 100644
--- a/src/main/java/nl/tudelft/queue/service/RequestService.java
+++ b/src/main/java/nl/tudelft/queue/service/RequestService.java
@@ -19,6 +19,7 @@ package nl.tudelft.queue.service;
 
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Collectors;
 
@@ -57,6 +58,7 @@ import reactor.core.publisher.Mono;
 
 @Service
 public class RequestService {
+
 	@Autowired
 	private RequestRepository rr;
 
@@ -109,7 +111,8 @@ public class RequestService {
 	@Autowired
 	private PermissionService permissionService;
 
-	private static final ReentrantLock lock = new ReentrantLock();
+	private static final ReentrantLock requestTakingLock = new ReentrantLock();
+	private static final Set<Long> currentlyEnqueuing = ConcurrentHashMap.newKeySet();
 
 	/**
 	 * Creates a request in the request database based on the person that posted the request and the
@@ -132,39 +135,44 @@ public class RequestService {
 			throw new AccessDeniedException("Request not allowed");
 		}
 
-		if (request instanceof LabRequest) {
-			var labRequest = (LabRequest) request;
-			/*
-			 * TODO in the future, delegate to another service layer component,
-			 *  and let it generate a link for the respective online mode.
-			 * This is for the far future, when more online modes need to be supported.
-			 */
-
-			if (labRequest.getTimeSlot() != null && !labRequest.getTimeSlot().canTakeSlot()) {
-				throw new AccessDeniedException("Time slot is not available");
-			}
-
-			if (((LabRequest) request).getOnlineMode() == OnlineMode.JITSI) {
-				labRequest.setJitsiRoom(js.createJitsiRoomName(labRequest));
-			}
-			if (((LabRequest) request).getSession().getEnableExperimental()) {
-				((LabRequest) request).setQuestionId(qApi.addQuestion(new QuestionCreateDTO()
-						.edition(new EditionIdDTO()
-								.id(mCache.getOrThrow(dto.getModule()).getEdition().getId()))
-						.question(((LabRequest) request).getQuestion())).block());
+		try {
+			if (!currentlyEnqueuing.add(personId))
+				return;
+			if (request instanceof LabRequest labRequest) {
+				/*
+				 * TODO in the future, delegate to another service layer component,
+				 *  and let it generate a link for the respective online mode.
+				 * This is for the far future, when more online modes need to be supported.
+				 */
+
+				if (labRequest.getTimeSlot() != null && !labRequest.getTimeSlot().canTakeSlot()) {
+					throw new AccessDeniedException("Time slot is not available");
+				}
+
+				if (((LabRequest) request).getOnlineMode() == OnlineMode.JITSI) {
+					labRequest.setJitsiRoom(js.createJitsiRoomName(labRequest));
+				}
+				if (((LabRequest) request).getSession().getEnableExperimental()) {
+					((LabRequest) request).setQuestionId(qApi.addQuestion(new QuestionCreateDTO()
+							.edition(new EditionIdDTO()
+									.id(mCache.getRequired(dto.getModule()).getEdition().getId()))
+							.question(((LabRequest) request).getQuestion())).block());
+				}
 			}
-		}
 
-		request = rr.save(request);
-		var event = rer.applyAndSave(new RequestCreatedEvent(request));
+			request = rr.save(request);
+			var event = rer.applyAndSave(new RequestCreatedEvent(request));
 
-		if (request instanceof SelectionRequest) {
-			((SelectionRequest) request).getSession().getRequests().add((SelectionRequest) request);
-			checkFcfsSelection((SelectionRequest) request);
-		}
+			if (request instanceof SelectionRequest) {
+				((SelectionRequest) request).getSession().getRequests().add((SelectionRequest) request);
+				checkFcfsSelection((SelectionRequest) request);
+			}
 
-		if (sendEvent) {
-			wss.sendRequestCreated(event);
+			if (sendEvent) {
+				wss.sendRequestCreated(event);
+			}
+		} finally {
+			currentlyEnqueuing.remove(personId);
 		}
 	}
 
@@ -343,7 +351,7 @@ public class RequestService {
 	@Transactional(Transactional.TxType.REQUIRES_NEW)
 	public Optional<LabRequest> takeNextRequestFromTimeSlot(Person assistant,
 			ClosableTimeSlot timeSlot) {
-		lock.lock();
+		requestTakingLock.lock();
 		try {
 
 			// If the person is already working on a request, no new event should be created.
@@ -362,7 +370,7 @@ public class RequestService {
 
 			return request;
 		} finally {
-			lock.unlock();
+			requestTakingLock.unlock();
 		}
 	}
 
@@ -379,11 +387,11 @@ public class RequestService {
 	 */
 	public Optional<LabRequest> takeNextRequest(Person assistant, Lab lab,
 			RequestTableFilterDTO filter) {
-		lock.lock();
+		requestTakingLock.lock();
 		try {
 			return getNextRequest(assistant, lab, filter);
 		} finally {
-			lock.unlock();
+			requestTakingLock.unlock();
 		}
 	}
 
@@ -415,7 +423,7 @@ public class RequestService {
 	 */
 	@Transactional(Transactional.TxType.REQUIRES_NEW)
 	public LabRequest pickRequest(Person assistant, LabRequest request) {
-		lock.lock();
+		requestTakingLock.lock();
 		try {
 			Optional<LabRequest> oldRequests = lrr.findCurrentlyProcessingRequest(assistant,
 					request.getSession());
@@ -427,7 +435,7 @@ public class RequestService {
 
 			return request;
 		} finally {
-			lock.unlock();
+			requestTakingLock.unlock();
 		}
 	}
 
@@ -491,7 +499,7 @@ public class RequestService {
 			return;
 		}
 
-		var room = rCache.getOrThrow(request.getRoom());
+		var room = rCache.getRequired(request.getRoom());
 		int openSpaces = request.getSession().openSpacesInRoom(room);
 
 		request.getSession().getPendingRequestsInRoom(room.getId()).stream()
@@ -562,7 +570,7 @@ public class RequestService {
 	 */
 	private StudentGroupDetailsDTO createIndividualStudentGroup(Long sessionId, Long personId,
 			Long moduleId) {
-		var person = pCache.getOrThrow(personId);
+		var person = pCache.getRequired(personId);
 		var module = mApi.getModuleById(moduleId).block();
 		var edition = module.getEdition();
 
diff --git a/src/main/java/nl/tudelft/queue/service/RequestTableService.java b/src/main/java/nl/tudelft/queue/service/RequestTableService.java
index 6e311d9f3f4d8398c772729015e6cb2e5d2601ee..393a309a5d04a21537725ace68e8b2ae1b7a0f3b 100644
--- a/src/main/java/nl/tudelft/queue/service/RequestTableService.java
+++ b/src/main/java/nl/tudelft/queue/service/RequestTableService.java
@@ -26,6 +26,7 @@ import java.util.stream.IntStream;
 import java.util.stream.Stream;
 
 import javax.servlet.http.HttpSession;
+import javax.transaction.Transactional;
 
 import nl.tudelft.labracore.api.AssignmentControllerApi;
 import nl.tudelft.labracore.api.EditionControllerApi;
@@ -186,7 +187,7 @@ public class RequestTableService {
 	public List<QueueSession<?>> addFilterAttributes(Model model,
 			List<QueueSession<?>> labs) {
 		// Fetch the editions currently active and assisted by the user.
-		List<EditionDetailsDTO> editions = eCache.get(eApi.getAllEditionsCurrentlyAssistedBy()
+		List<EditionDetailsDTO> editions = eCache.getAndIgnoreMissing(eApi.getAllEditionsCurrentlyAssistedBy()
 				.map(EditionSummaryDTO::getId).collectList().blockOptional().orElse(List.of()));
 
 		// Get all information necessary to display filters.
@@ -219,10 +220,12 @@ public class RequestTableService {
 	 */
 	public List<RequestViewDTO<?>> convertRequestsToView(List<? extends Request<?>> requests) {
 		// Pre-load caches with people, sessions, student groups and rooms
-		pCache.get(requests.stream().map(Request::getRequester).distinct());
-		sCache.get(requests.stream().map(r -> r.getSession().getSession()).distinct());
-		sgCache.get(requests.stream().map(Request::getStudentGroup).distinct());
-		rCache.get(requests.stream().map(Request::getRoom).filter(Objects::nonNull).distinct());
+		pCache.getAndIgnoreMissing(requests.stream().map(Request::getRequester).distinct());
+		sCache.getAndHandleAll(requests.stream().map(r -> r.getSession().getSession()).distinct(),
+				lService.deleteSessionsByIds());
+		sgCache.getAndIgnoreMissing(requests.stream().map(Request::getStudentGroup).distinct());
+		rCache.getAndIgnoreMissing(
+				requests.stream().map(Request::getRoom).filter(Objects::nonNull).distinct());
 
 		// Map each of the request to view DTOs that individually do cache requests
 		return requests.stream().map(Request::toViewDTO).collect(Collectors.toList());
@@ -280,7 +283,7 @@ public class RequestTableService {
 	private List<PersonSummaryDTO> assistants(List<SessionDetailsDTO> sessions) {
 		return sessions.stream()
 				.flatMap(s -> s.getEditions().stream()).distinct()
-				.flatMap(e -> erCache.getOrThrow(e.getId()).getRoles().stream())
+				.flatMap(e -> erCache.getRequired(e.getId()).getRoles().stream())
 				.filter(role -> Set.of(TA, HEAD_TA, TEACHER, TEACHER_RO).contains(role.getType()))
 				.map(RolePersonDetailsDTO::getPerson)
 				.distinct()
@@ -295,7 +298,7 @@ public class RequestTableService {
 	 * @return          The list of all rooms used within the given sessions.
 	 */
 	private List<RoomDetailsDTO> rooms(List<SessionDetailsDTO> sessions) {
-		return rCache.get(
+		return rCache.getAndIgnoreMissing(
 				sessions.stream().flatMap(l -> l.getRooms().stream().map(RoomDetailsDTO::getId)).distinct());
 	}
 
@@ -334,9 +337,11 @@ public class RequestTableService {
 	 * @param  labs The labs of which the sessions need to be looked up.
 	 * @return      The list of all session details gotten from the list of labs.
 	 */
+	@Transactional
 	private List<SessionDetailsDTO> sessions(List<? extends QueueSession<?>> labs) {
-		return sCache
-				.get(labs.stream().map(QueueSession::getSession).distinct().collect(Collectors.toList()));
+		return sCache.getAndHandleAll(
+				labs.stream().map(QueueSession::getSession).distinct().collect(Collectors.toList()),
+				missedIds -> lService.deleteSessionsByIds());
 	}
 
 	/**
@@ -347,7 +352,7 @@ public class RequestTableService {
 	 * @return          The list of all assignments within the given editions.
 	 */
 	private List<AssignmentDetailsDTO> assignments(List<SessionDetailsDTO> sessions) {
-		return aCache.get(sessions.stream()
+		return aCache.getAndIgnoreMissing(sessions.stream()
 				.flatMap(s -> s.getAssignments().stream())
 				.map(AssignmentSummaryDTO::getId).distinct());
 	}
@@ -369,7 +374,7 @@ public class RequestTableService {
 				.requireNonNull(aApi.getAssignmentsWithModules(assignmentIds).collectList().block());
 
 		List<String> courseCodes = eCache
-				.get(assignmentModules.stream()
+				.getAndIgnoreMissing(assignmentModules.stream()
 						.map(assignment -> assignment.getModule().getEdition().getId()))
 				.stream().map(edition -> edition.getCourse().getCode()).toList();
 
diff --git a/src/main/java/nl/tudelft/queue/service/RoleDTOService.java b/src/main/java/nl/tudelft/queue/service/RoleDTOService.java
index 0fb97e106c57afefa27548b9a928b0c5b7de0d48..440df091fab771c0fb58ee997fa3e869466b4381 100644
--- a/src/main/java/nl/tudelft/queue/service/RoleDTOService.java
+++ b/src/main/java/nl/tudelft/queue/service/RoleDTOService.java
@@ -53,7 +53,7 @@ public class RoleDTOService {
 	}
 
 	public List<String> roleNames(EditionDetailsDTO eDto, Set<RolePersonDetailsDTO.TypeEnum> types) {
-		return roleNames(erCache.getOrThrow(eDto.getId()).getRoles(), types);
+		return roleNames(erCache.getRequired(eDto.getId()).getRoles(), types);
 	}
 
 	public List<PersonSummaryDTO> roles(List<RolePersonDetailsDTO> roles,
@@ -74,7 +74,7 @@ public class RoleDTOService {
 
 	public List<PersonSummaryDTO> roles(EditionDetailsDTO eDto,
 			Set<RolePersonDetailsDTO.TypeEnum> types) {
-		return roles(erCache.getOrThrow(eDto.getId()).getRoles(), types);
+		return roles(erCache.getRequired(eDto.getId()).getRoles(), types);
 	}
 
 	/**
diff --git a/src/main/resources/templates/edition/view/labs.html b/src/main/resources/templates/edition/view/labs.html
index 251c224a9f2f36f570d38a06e19fa64bd60e0f2a..c21a3aa0299d788e4b93affb7dd53f47f9e270b8 100644
--- a/src/main/resources/templates/edition/view/labs.html
+++ b/src/main/resources/templates/edition/view/labs.html
@@ -48,7 +48,7 @@
     </div>
 
     <div class="page-sub-header">
-        <button type="button" class="btn btn-primary float-right" data-toggle="modal" data-target="#create-modal" th:if="${@permissionService.canManageSessions(edition.id)}">
+        <button type="button" class="btn btn-primary float-right" data-toggle="modal" data-target="#create-session-modal" th:if="${@permissionService.canManageSessions(edition.id)}">
             <span>Create session</span>
         </button>
         <h3>Labs</h3>
@@ -131,10 +131,32 @@
                             </a>
                         </th:block>
                         <th:block th:if="${@permissionService.canManageSessions(edition.id)}">
-                            <a th:href="@{/lab/{id}/remove(id=${lab.id})}"
-                               class="btn btn-sm btn-danger">
+                            <div
+                               class="btn btn-sm btn-danger" data-toggle="modal" th:data-target="'#delete-session-modal-' + ${lab.id}">
                                 <i class="fa fa-trash-o" aria-hidden="true"></i>
-                            </a>
+                            </div>
+                            <div class="modal" th:id="'delete-session-modal-' + ${lab.id}" tabindex="-1" role="dialog" aria-hidden="true" th:if="${@permissionService.canManageSessions(edition.id)}">
+                                <div class="modal-dialog modal-dialog-centered">
+                                    <div class="modal-content" >
+                                        <div class="modal-header">
+                                            <h3 class="modal-title" th:text="'Delete Session #' + ${lab.id}"></h3>
+                                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                                                <span aria-hidden="true">&times;</span>
+                                            </button>
+                                        </div>
+                                        <div class="modal-body">
+                                            <div class="alert alert-danger" th:text="'You are about to delete session #' + ${lab.id} + '!'">
+                                            </div>
+                                            <p><strong>WARNING: </strong>Deleting labs that already have existing requests is generally not recommended, as it can cause adverse effects. Therefore, please avoid deleting such labs whenever possible.</p>
+                                        </div>
+                                        <div class="modal-footer">
+                                            <form th:action="@{/lab/{id}/remove(id=${lab.id})}" method="post">
+                                                <button type="submit" class="btn btn-danger" >Delete Session</button>
+                                            </form>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
                         </th:block>
                         <th:block th:if="${@permissionService.canManageSessions(edition.id)}">
                             <a href="#"
@@ -149,7 +171,7 @@
         </ul>
     </th:block>
 
-    <div class="modal" id="create-modal" tabindex="-1" role="dialog" aria-hidden="true" th:if="${@permissionService.canManageSessions(edition.id)}">
+    <div class="modal" id="create-session-modal" tabindex="-1" role="dialog" aria-hidden="true" th:if="${@permissionService.canManageSessions(edition.id)}">
         <div class="modal-dialog modal-dialog-centered">
             <form class="modal-content" method="get" th:action="@{/edition/{id}/lab/create(id=${edition.id})}">
                 <div class="modal-header">
diff --git a/src/main/resources/templates/lab/enqueue.html b/src/main/resources/templates/lab/enqueue.html
index e60b8c5cd0d95d46652c3e8e84cb075133d66452..47f5575c3339da9f02e5af149abcccd81e8c5f45 100644
--- a/src/main/resources/templates/lab/enqueue.html
+++ b/src/main/resources/templates/lab/enqueue.html
@@ -48,7 +48,7 @@
     </div>
 
     <form th:action="@{/lab/{id}/enqueue/{rType}(id=${qSession.id}, rType=${rType})}" th:object="${request}" method="post"
-          class="form-horizontal">
+          class="form-horizontal" id="enqueue-form">
         <th:block layout:fragment="enqueue-body">
         </th:block>
 
@@ -61,6 +61,13 @@
             </div>
         </div>
     </form>
+    <script>
+        document.addEventListener("DOMContentLoaded", function () {
+            document.getElementById("enqueue-form").addEventListener("submit", function () {
+                document.getElementById("enqueue").setAttribute("disabled", "");
+            });
+        });
+    </script>
 </section>
 </body>
 </html>
diff --git a/src/main/resources/templates/lab/enqueue/lab.html b/src/main/resources/templates/lab/enqueue/lab.html
index 5a6ba4a4fa378fcfc836206ca1e8a07b3410d277..89538030a9682dd18e000ff973704e44a66744e6 100644
--- a/src/main/resources/templates/lab/enqueue/lab.html
+++ b/src/main/resources/templates/lab/enqueue/lab.html
@@ -168,8 +168,10 @@
 
             <div class="col-sm-8">
                 <textarea maxlength="250" th:classappend="${#fields.hasErrors('comment')} ? 'is-invalid'"
-                          class="form-control" id="input-comment" th:field="*{comment}"
-                          placeholder="You can use this field to specify a location within the room, e.g. your cubicle number."></textarea>
+                class="form-control" id="input-comment" th:field="*{comment}"
+                th:placeholder="'You can use this field to specify a location within the room, e.g. your cubicle number.'"></textarea>
+
+                <input type="hidden" id="lastKnownLocation" th:value="${lastKnownLocation}" />
 
                 <div class="invalid-feedback" th:if="${#fields.hasErrors('comment')}" th:errors="*{comment}">
                     Comment error
@@ -181,6 +183,7 @@
         <script src="/js/map_loader.js"></script>
 
         <script type="text/javascript" th:inline="javascript">
+
             function checkEnrolled(val) {
                 $(".enrollment-status").css('display', 'none')
                 $("#enqueue").prop('disabled', false)
@@ -191,6 +194,14 @@
                 }
             }
 
+            $(document).ready(() => {
+                const lastKnownLocation = $("#lastKnownLocation").val();
+                const inputCommentField = $("#input-comment");
+                if (lastKnownLocation) {
+                    inputCommentField.val(lastKnownLocation);
+                }
+            });
+
             //<![CDATA[
             const spDefaults = $.fn.selectpicker.Constructor.DEFAULTS;
             spDefaults.mobile = true;
diff --git a/src/main/resources/templates/lab/remove.html b/src/main/resources/templates/lab/remove.html
deleted file mode 100644
index 7223294af27cf7865ec7da8130505883a9310824..0000000000000000000000000000000000000000
--- a/src/main/resources/templates/lab/remove.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!--
-
-    Queue - A Queueing system that can be used to handle labs in higher education
-    Copyright (C) 2016-2020  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/>.
-
--->
-<!DOCTYPE html>
-<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
-      layout:decorate="~{edition/view}">
-
-<!--@thymesVar id="ec" type="nl.tudelft.labracore.api.dto.EditionCollectionDetailsDTO"-->
-<!--@thymesVar id="edition" type="nl.tudelft.labracore.api.dto.EditionDetailsDTO"-->
-
-<!--@thymesVar id="qSession" type="nl.tudelft.queue.model.QueueSession"-->
-
-<body>
-<section layout:fragment="subcontent">
-    <div class="page-sub-header">
-        <h3>Remove lab</h3>
-    </div>
-
-    <form action="#" th:action="@{/lab/{id}/remove(id=${qSession.id})}" th:object="${qSession}"
-          class="form-horizontal"
-          method="post">
-        <input type="hidden" th:field="*{id}"/>
-
-        Are you sure you want to remove <strong th:text="${'session #' + qSession.id}"></strong>?
-
-        <div class="text-center">
-            <button class="btn btn-danger">Delete this session</button>
-            <small>or <a
-                    th:href="@{/{ou}/{id}/labs(id=${ec == null ? edition.id : ec.id}, ou=${ec == null ? 'edition' : 'shared-edition'})}">go
-                back</a></small>
-        </div>
-    </form>
-</section>
-</body>
-</html>
diff --git a/src/main/resources/templates/shared-edition/view.html b/src/main/resources/templates/shared-edition/view.html
index 6ba75a80df706ad97aaff8a645d3d82eb100ae55..55250ae704f87972ec168f457ee36f9835145be0 100644
--- a/src/main/resources/templates/shared-edition/view.html
+++ b/src/main/resources/templates/shared-edition/view.html
@@ -68,7 +68,7 @@
                     <td>
                         <ul>
                             <li th:each="r : ${instance.getValue()}"
-                                th:text="|${r.getType().getValue()} (${@editionCacheManager.getOrThrow(r.id.editionId).course.name})|">
+                                th:text="|${r.getType().getValue()} (${@editionCacheManager.getRequired(r.id.editionId).course.name})|">
                             </li>
                         </ul>
                     </td>
diff --git a/src/main/resources/templates/shared-edition/view/session-list.html b/src/main/resources/templates/shared-edition/view/session-list.html
index ec18c69fcb07d58a6e293e993236877963895f36..e3d139f186313db6a1e941a7d52b19bc9d00822e 100644
--- a/src/main/resources/templates/shared-edition/view/session-list.html
+++ b/src/main/resources/templates/shared-edition/view/session-list.html
@@ -59,17 +59,38 @@
         <td>
           <span th:text="${#temporals.format(lab.slot.closesAt, 'dd MMMM yyyy HH:mm')}"></span>
           <div class="btn-group float-right" th:if="${@permissionService.canManageInAnyEdition(lab.associatedEditions)}">
-
             <th:block>
               <a th:href="@{/lab/{id}/edit(id=${lab.id})}" class="btn btn-sm btn-secondary">
                 <i class="fa fa-pencil" aria-hidden="true"></i>
               </a>
             </th:block>
             <th:block>
-              <a th:href="@{/lab/{id}/remove(id=${lab.id})}"
-                 class="btn btn-sm btn-danger">
+              <div
+                      class="btn btn-sm btn-danger" data-toggle="modal" th:data-target="'#delete-session-modal-' + ${lab.id}">
                 <i class="fa fa-trash-o" aria-hidden="true"></i>
-              </a>
+              </div>
+              <div class="modal" th:id="'delete-session-modal-' + ${lab.id}" tabindex="-1" role="dialog" aria-hidden="true">
+                <div class="modal-dialog modal-dialog-centered">
+                  <div class="modal-content" >
+                    <div class="modal-header">
+                      <h3 class="modal-title" th:text="'Delete Session #' + ${lab.id}"></h3>
+                      <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                        <span aria-hidden="true">&times;</span>
+                      </button>
+                    </div>
+                    <div class="modal-body">
+                      <div class="alert alert-danger" th:text="'You are about to delete session #' + ${lab.id} + '!'">
+                      </div>
+                      <p><strong>WARNING: </strong>Deleting labs that already have existing requests is generally not recommended, as it can cause adverse effects. Therefore, please avoid deleting such labs whenever possible.</p>
+                    </div>
+                    <div class="modal-footer">
+                      <form th:action="@{/lab/{id}/remove(id=${lab.id})}" method="post">
+                        <button type="submit" class="btn btn-danger" >Delete Session</button>
+                      </form>
+                    </div>
+                  </div>
+                </div>
+              </div>
             </th:block>
             <th:block>
               <a href="#"
diff --git a/src/test/java/nl/tudelft/queue/controller/LabControllerTest.java b/src/test/java/nl/tudelft/queue/controller/LabControllerTest.java
index 0bf830f5bbed3c3a9ef921b6abaa445eb0fe6c2f..ed7227545d245348d943675416dd3eebcc7c146b 100644
--- a/src/test/java/nl/tudelft/queue/controller/LabControllerTest.java
+++ b/src/test/java/nl/tudelft/queue/controller/LabControllerTest.java
@@ -377,6 +377,7 @@ class LabControllerTest {
 	@Test
 	@WithUserDetails("student5")
 	void processingRequestForLabForwardsToRequests() throws Exception {
+
 		LabRequest request = rr.save(LabRequest.builder()
 				.requestType(RequestType.QUESTION)
 				.comment("I am a comment")
@@ -385,7 +386,7 @@ class LabControllerTest {
 				.room(room1.getId())
 				.session(regLab1)
 				.requester(student5.getId())
-				.studentGroup(1L)
+				.studentGroup(sg1.getId())
 				.build());
 		rer.applyAndSave(new RequestTakenEvent(request, 0L));
 
@@ -569,14 +570,6 @@ class LabControllerTest {
 		verify(ls).createSessions(isA(ExamLabCreateDTO.class), any(), eq(LabService.SessionType.SHARED));
 	}
 
-	@Test
-	@WithUserDetails("admin")
-	void getLabDeleteViewWorks() throws Exception {
-		mvc.perform(get("/lab/" + regLab1.getId() + "/remove"))
-				.andExpect(status().isOk())
-				.andExpect(view().name("lab/remove"));
-	}
-
 	@Test
 	@WithUserDetails("admin")
 	void deleteLabWorks() throws Exception {
diff --git a/src/test/java/nl/tudelft/queue/service/CapacitySessionServiceTest.java b/src/test/java/nl/tudelft/queue/service/CapacitySessionServiceTest.java
index 9d31e6434eb89c198bd8d21395525e9bacb11af6..e49cd8cdedd30a76a670113666b7a00c44188760 100644
--- a/src/test/java/nl/tudelft/queue/service/CapacitySessionServiceTest.java
+++ b/src/test/java/nl/tudelft/queue/service/CapacitySessionServiceTest.java
@@ -140,7 +140,6 @@ public class CapacitySessionServiceTest {
 		sApiMocker.mock();
 		sgApiMocker.mock();
 
-		//		when(rCache.getOrThrow(8932L)).thenReturn(new RoomDetailsDTO().capacity(5));
 	}
 
 	private void mockIndividualStudentGroup(Long moduleId, List<Long> requesterIds, Long studentGroupId) {
diff --git a/src/test/java/nl/tudelft/queue/service/JitsiServiceTest.java b/src/test/java/nl/tudelft/queue/service/JitsiServiceTest.java
index 0a60552b34cd518d3c03c4b26ba23482b94096d8..905bf17cebbeeb6b5f2ea43db56c7828743c6c5e 100644
--- a/src/test/java/nl/tudelft/queue/service/JitsiServiceTest.java
+++ b/src/test/java/nl/tudelft/queue/service/JitsiServiceTest.java
@@ -56,7 +56,7 @@ public class JitsiServiceTest {
 
 	@Test
 	void createdJitsiRoomNameShouldStartWithUsername() {
-		when(pCache.getOrThrow(request.getRequester()))
+		when(pCache.getRequired(request.getRequester()))
 				.thenReturn(new PersonSummaryDTO().username("jvdcbaskjhbdsaj"));
 
 		assertThat(js.createJitsiRoomName(request))
diff --git a/src/test/java/nl/tudelft/queue/service/LabServiceTest.java b/src/test/java/nl/tudelft/queue/service/LabServiceTest.java
index cace86da5ba351fa30c85f58253113483f9eafc5..5935702e5ea97ce52b2ea4d31b2395240a355088 100644
--- a/src/test/java/nl/tudelft/queue/service/LabServiceTest.java
+++ b/src/test/java/nl/tudelft/queue/service/LabServiceTest.java
@@ -120,6 +120,9 @@ class LabServiceTest {
 	@Autowired
 	private PersonApiMocker personApiMocker;
 
+	@SpyBean
+	private QueueSessionRepository qsr;
+
 	private SessionDetailsDTO session1;
 	private SessionDetailsDTO session2;
 	private SessionDetailsDTO session3;
@@ -487,7 +490,7 @@ class LabServiceTest {
 	@Test
 	void deleteLabWorks() {
 		ls.deleteSession(lab1);
-		assertThat(lab1.getDeletedAt()).isNotNull();
+		verify(qsr, times(1)).delete(any());
 	}
 
 	@Test