WIP add a decorator to limit cache size
This commit is contained in:
parent
563d6fe27a
commit
4fa581d242
|
@ -0,0 +1,83 @@
|
|||
package org.hl7.fhir.validation.cli.services;
|
||||
|
||||
import org.hl7.fhir.validation.ValidationEngine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class MaxSizeSessionCacheDecorator implements SessionCache {
|
||||
|
||||
public final int maxSize;
|
||||
public final SessionCache sessionCache;
|
||||
|
||||
private final List<String> sessionIds;
|
||||
|
||||
public MaxSizeSessionCacheDecorator(SessionCache sessionCache, int maxSize) {
|
||||
this.sessionCache = sessionCache;
|
||||
this.maxSize = maxSize;
|
||||
this.sessionIds = new ArrayList<>(sessionCache.getSessionIds());
|
||||
if (this.sessionIds.size() > maxSize) {
|
||||
throw new IllegalArgumentException("Session cache size exceeds the maximum size");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cacheSession(ValidationEngine validationEngine) {
|
||||
checkSizeAndMaintainMax(null);
|
||||
String key = sessionCache.cacheSession(validationEngine);
|
||||
sessionIds.add(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cacheSession(Supplier<ValidationEngine> validationEngineSupplier) {
|
||||
checkSizeAndMaintainMax(null);
|
||||
ValidationEngine validationEngine = validationEngineSupplier.get();
|
||||
return sessionCache.cacheSession(validationEngine);
|
||||
}
|
||||
|
||||
private void checkSizeAndMaintainMax(String keyToAdd) {
|
||||
if (keyToAdd != null || sessionCache.sessionExists(keyToAdd)) {
|
||||
return;
|
||||
}
|
||||
Set<String> sessionIds = sessionCache.getSessionIds();
|
||||
//Sync our tracked keys, in case the underlying cache has changed
|
||||
this.sessionIds.removeIf(key -> !sessionIds.contains(key));
|
||||
|
||||
if (this.sessionIds.size() >= maxSize) {
|
||||
final String key = this.sessionIds.remove(0);
|
||||
sessionCache.removeSession(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cacheSession(String sessionId, ValidationEngine validationEngine) {
|
||||
checkSizeAndMaintainMax(sessionId);
|
||||
cacheSession(sessionId, validationEngine);
|
||||
return sessionCache.cacheSession(
|
||||
sessionId, validationEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sessionExists(String sessionId) {
|
||||
return sessionCache.sessionExists(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationEngine removeSession(String sessionId) {
|
||||
sessionIds.remove(sessionId);
|
||||
return sessionCache.removeSession(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationEngine fetchSessionValidatorEngine(String sessionId) {
|
||||
return sessionCache.fetchSessionValidatorEngine(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSessionIds() {
|
||||
return sessionCache.getSessionIds();
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package org.hl7.fhir.validation.cli.services;
|
|||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.collections4.map.PassiveExpiringMap;
|
||||
import org.hl7.fhir.validation.ValidationEngine;
|
||||
|
@ -44,6 +45,12 @@ public class PassiveExpiringSessionCache implements SessionCache {
|
|||
return generatedId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cacheSession(Supplier<ValidationEngine> validationEngineSupplier) {
|
||||
ValidationEngine engine = validationEngineSupplier.get();
|
||||
return this.cacheSession(engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the initialized {@link ValidationEngine} in the cache with the passed in id as the key. If a null key is
|
||||
* passed in, a new key is generated and returned.
|
||||
|
@ -96,6 +103,11 @@ public class PassiveExpiringSessionCache implements SessionCache {
|
|||
return cachedSessions.containsKey(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationEngine removeSession(String sessionId) {
|
||||
return cachedSessions.remove(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stored {@link ValidationEngine} associated with the passed in session id, if one such instance exists.
|
||||
* @param sessionId The {@link String} session id.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.validation.cli.services;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hl7.fhir.validation.ValidationEngine;
|
||||
|
||||
|
@ -14,6 +15,14 @@ public interface SessionCache {
|
|||
*/
|
||||
String cacheSession(ValidationEngine validationEngine);
|
||||
|
||||
/**
|
||||
* Uses the passed {@link Supplier} to generate a {@link ValidationEngine} and add it to the cache. Returns the
|
||||
* session id that will be associated with the generated instance.
|
||||
* @param validationEngineSupplier {@link Supplier} of {@link ValidationEngine}
|
||||
* @return The {@link String} id associated with the stored instance.
|
||||
*/
|
||||
String cacheSession(Supplier<ValidationEngine> validationEngineSupplier);
|
||||
|
||||
/**
|
||||
* Stores the initialized {@link ValidationEngine} in the cache with the passed in id as the key. If a null key is
|
||||
* passed in, a new key is generated and returned.
|
||||
|
@ -23,9 +32,6 @@ public interface SessionCache {
|
|||
*/
|
||||
String cacheSession(String sessionId, ValidationEngine validationEngine);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the passed in {@link String} id exists in the set of stored session id.
|
||||
* @param sessionId The {@link String} id to search for.
|
||||
|
@ -33,6 +39,13 @@ public interface SessionCache {
|
|||
*/
|
||||
boolean sessionExists(String sessionId);
|
||||
|
||||
/**
|
||||
* Removes the {@link ValidationEngine} associated with the passed in session id.
|
||||
* @param sessionId The {@link String} session id.
|
||||
* @return The {@link ValidationEngine} instance that was removed.
|
||||
*/
|
||||
ValidationEngine removeSession(String sessionId);
|
||||
|
||||
/**
|
||||
* Returns the stored {@link ValidationEngine} associated with the passed in session id, if one such instance exists.
|
||||
* @param sessionId The {@link String} session id.
|
||||
|
|
|
@ -489,9 +489,20 @@ public class ValidationService {
|
|||
if (sessionId != null) {
|
||||
System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator.");
|
||||
}
|
||||
ValidationEngine validationEngine = getValidationEngineFromCliContext(cliContext, definitions, tt);
|
||||
sessionId = sessionCache.cacheSession(validationEngine);
|
||||
System.out.println("Cached new session. Cache size = " + sessionCache.getSessionIds().size());
|
||||
|
||||
// Send a supplier instead of instantiating. This will permit the sessionCache to manage existing sessions (drop
|
||||
// or expire old sessions as needed)
|
||||
sessionId = sessionCache.cacheSession(() -> {
|
||||
|
||||
try {
|
||||
return buildValidationEngine(cliContext, definitions, tt);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
});
|
||||
} else {
|
||||
System.out.println("Cached session exists for session id " + sessionId + ", returning stored validator session id. Cache size = " + sessionCache.getSessionIds().size());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package org.hl7.fhir.validation.cli.services;
|
||||
|
||||
import org.hl7.fhir.validation.ValidationEngine;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class MaxSizeSessionCacheDecoratorTest {
|
||||
|
||||
private List<ValidationEngine> getMockedEngines(int count) {
|
||||
List<ValidationEngine> engines = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
engines.add(mock(ValidationEngine.class));
|
||||
}
|
||||
return engines;
|
||||
}
|
||||
|
||||
private LinkedHashMap<String, ValidationEngine> addMockedEngines(SessionCache cache, int count) {
|
||||
LinkedHashMap<String, ValidationEngine> engineMap = new LinkedHashMap<>();
|
||||
List<ValidationEngine> engines = getMockedEngines(count);
|
||||
for (ValidationEngine engine : engines) {
|
||||
String key = cache.cacheSession(engine);
|
||||
engineMap.put(key, engine);
|
||||
}
|
||||
return engineMap;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trivialCase() {
|
||||
|
||||
MaxSizeSessionCacheDecorator maxSizeSessionCacheDecorator = new MaxSizeSessionCacheDecorator(new PassiveExpiringSessionCache(), 4);
|
||||
|
||||
LinkedHashMap<String, ValidationEngine> initialEngines = addMockedEngines(maxSizeSessionCacheDecorator, 3);
|
||||
|
||||
Assertions.assertEquals(3, maxSizeSessionCacheDecorator.getSessionIds().size());
|
||||
|
||||
List<ValidationEngine> newEngines = getMockedEngines(2);
|
||||
|
||||
for (ValidationEngine engine : newEngines) {
|
||||
maxSizeSessionCacheDecorator.cacheSession(engine);
|
||||
}
|
||||
|
||||
Assertions.assertEquals(4, maxSizeSessionCacheDecorator.getSessionIds().size());
|
||||
|
||||
Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains()
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue