Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract session cache building into a factory + bump core and tests #192

Merged
merged 2 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ kotlin.code.style=official
kotlin.js.generate.executable.default=false

# versions
fhirCoreVersion= 6.3.27
fhirCoreVersion= 6.3.32

junitVersion=5.7.1
mockk_version=1.10.2
Expand Down
4 changes: 2 additions & 2 deletions http-client-tests/tests/preset-queries.http
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Content-Type: application/json
client.test("Issues are Correct", function() {
let issues = response.body.outcomes[0].issues
client.log("issues:" + issues.length)
client.assert(issues.length === 45);
client.assert(issues.length === 44);
client.assert(containsIssue(issues, 1, 2, "The Snomed CT code 373270004 (Substance with penicillin structure and antibacterial mechanism of action) is not a member of the IPS free set", "BUSINESSRULE", "INFORMATION"))
client.assert(containsIssue(issues, 1, 2, "The Snomed CT code 108774000 (Product containing anastrozole (medicinal product)) is not a member of the IPS free set", "BUSINESSRULE", "INFORMATION"))

Expand All @@ -112,7 +112,7 @@ Content-Type: application/json
client.test("Issues are Correct", function() {
let issues = response.body.outcomes[0].issues
client.log("issues:" + issues.length)
client.assert(issues.length === 53);
client.assert(issues.length === 49);
client.assert(containsIssue(issues, 1, 2, "The Snomed CT code 373270004 (Substance with penicillin structure and antibacterial mechanism of action) is not a member of the IPS free set", "BUSINESSRULE", "INFORMATION"))
client.assert(containsIssue(issues, 1, 2, "The Snomed CT code 108774000 (Product containing anastrozole (medicinal product)) is not a member of the IPS free set", "BUSINESSRULE", "INFORMATION"))
client.assert(containsIssue(issues, 314, 4, "This element does not match any known slice defined in the profile http://hl7.org.au/fhir/ips/StructureDefinition/Bundle-au-ips|0.0.1 (this may not be a problem, but you should check that it's not intended to match a slice)", "INFORMATIONAL", "INFORMATION"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,17 @@ import java.util.*
import java.util.concurrent.TimeUnit

class GuavaSessionCacheAdapter(cacheSize : Long, cacheDuration: Long) : SessionCache {
private val cache: Cache<String, ValidationEngine> = CacheBuilder.newBuilder().expireAfterAccess(cacheDuration, TimeUnit.MINUTES).maximumSize(cacheSize).build()
private val cache: Cache<String, ValidationEngine>;
init {
val cacheBuilder = CacheBuilder.newBuilder()
if (cacheDuration > 0) {
cacheBuilder.expireAfterAccess(cacheDuration, TimeUnit.MINUTES)
}
if (cacheSize >= 0) {
cacheBuilder.maximumSize(cacheSize)
}
cache = cacheBuilder.build<String, ValidationEngine>()
}

override fun cacheSession(validationEngine: ValidationEngine): String {
val generatedId = generateID()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package controller.validation

import org.hl7.fhir.validation.cli.services.SessionCache

interface SessionCacheFactory {
fun getSessionCache(): SessionCache
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package controller.validation

import org.hl7.fhir.validation.cli.services.PassiveExpiringSessionCache
import org.hl7.fhir.validation.cli.services.SessionCache
import java.util.concurrent.TimeUnit

private const val SESSION_CACHE_DURATION_ENV_KEY = "SESSION_CACHE_DURATION"
private const val SESSION_DEFAULT_DURATION: Long = 60

private const val SESSION_CACHE_SIZE_ENV_KEY = "SESSION_CACHE_SIZE"
private const val SESSION_DEFAULT_SIZE: Long = 4

private const val SESSION_CACHE_IMPLEMENTATION_ENV_KEY = "SESSION_CACHE_IMPLEMENTATION"

private const val GUAVA_SESSION_CACHE_IMPLEMENTATION = "GuavaSessionCacheAdapter"
private const val PASSIVE_EXPIRING_SESSION_CACHE_IMPLEMENTATION = "PassiveExpiringSessionCache"

private const val SESSION_DEFAULT_CACHE_IMPLEMENTATION: String = GUAVA_SESSION_CACHE_IMPLEMENTATION

class SessionCacheFactoryImpl : SessionCacheFactory {

override fun getSessionCache(): SessionCache {
/* Session cache configuration from environment variables.
*
* SESSION_CACHE_IMPLEMENTATION can be either the deprecated PassiveExpiringSessionCache or the preferred
* GuavaSessionCacheAdapter, and will be GuavaSessionCacheAdapter if unspecified.
*
* SESSION_CACHE_DURATION is the duration in minutes that a session will be kept in the cache. If negative,
* sessions will not expire. If unspecified, the default is 60 minutes.
*
* SESSION_CACHE_SIZE (only available in GuavaSessionCacheAdapter) is the maximum number of sessions that will
* be kept in the cache in last accessed last out order. If unspecified, the default is 4.
*
* TODO this should be encapsulated in a session cache configuration class, and use a cleaner method of
* configuration. These env vars are overloaded, and should be split per cache implementation.
*/
val sessionCacheDuration = System.getenv(SESSION_CACHE_DURATION_ENV_KEY)?.toLong() ?: SESSION_DEFAULT_DURATION;
val sessionCacheSize = System.getenv(SESSION_CACHE_SIZE_ENV_KEY)?.toLong() ?: SESSION_DEFAULT_SIZE
val sessionCacheImplementation =
System.getenv(SESSION_CACHE_IMPLEMENTATION_ENV_KEY) ?: SESSION_DEFAULT_CACHE_IMPLEMENTATION;
val sessionCache: SessionCache =
if (sessionCacheImplementation == PASSIVE_EXPIRING_SESSION_CACHE_IMPLEMENTATION) {
PassiveExpiringSessionCache(sessionCacheDuration, TimeUnit.MINUTES).setResetExpirationAfterFetch(true);
} else {
GuavaSessionCacheAdapter(sessionCacheSize, sessionCacheDuration)
}
return sessionCache
}
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
package controller.validation

import com.typesafe.config.ConfigFactory
import constants.Preset
import io.ktor.server.config.*
import model.PackageInfo
import org.hl7.fhir.validation.cli.services.PassiveExpiringSessionCache
import org.hl7.fhir.validation.cli.services.SessionCache
import org.hl7.fhir.validation.cli.services.ValidationService
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread


private const val SESSION_DEFAULT_DURATION: Long = 60
private const val SESSION_DEFAULT_SIZE: Long = 4
private const val SESSION_DEFAULT_CACHE_IMPLEMENTATION: String = "GuavaSessionCacheAdapter"

class ValidationServiceFactoryImpl : ValidationServiceFactory {
private var sessionCacheFactory: SessionCacheFactory
private var validationService: ValidationService

init {
sessionCacheFactory = SessionCacheFactoryImpl()
validationService = createValidationServiceInstance();

}

fun createValidationServiceInstance(): ValidationService {
val sessionCacheDuration = System.getenv("SESSION_CACHE_DURATION")?.toLong() ?: SESSION_DEFAULT_DURATION;
val sessionCacheSize = System.getenv("SESSION_CACHE_SIZE")?.toLong() ?: SESSION_DEFAULT_SIZE
val sessionCacheImplementation = System.getenv("SESSION_CACHE_IMPLEMENTATION") ?: SESSION_DEFAULT_CACHE_IMPLEMENTATION;
val sessionCache: SessionCache = if (sessionCacheImplementation == "PassiveExpiringSessionCache") {
PassiveExpiringSessionCache(sessionCacheDuration, TimeUnit.MINUTES).setResetExpirationAfterFetch(true);
} else {
GuavaSessionCacheAdapter(sessionCacheSize, sessionCacheDuration)
}
val sessionCache: SessionCache = sessionCacheFactory.getSessionCache()

val validationService = ValidationService(sessionCache);
thread {
Preset.values().forEach {
Expand All @@ -49,7 +34,9 @@ class ValidationServiceFactoryImpl : ValidationServiceFactory {
}
return validationService
}




override fun getValidationService() : ValidationService {
val engineReloadThreshold = (System.getenv("ENGINE_RELOAD_THRESHOLD") ?: "250000000").toLong()
if (java.lang.Runtime.getRuntime().freeMemory() < engineReloadThreshold) {
Expand Down
6 changes: 3 additions & 3 deletions src/jvmMain/resources/version.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#Generated by the Semver Plugin for Gradle
#Thu Aug 29 22:33:32 UTC 2024
#Mon Sep 23 16:48:02 UTC 2024
version.buildmeta=
version.major=1
version.minor=0
version.patch=56
version.patch=57
version.prerelease=SNAPSHOT
version.semver=1.0.56-SNAPSHOT
version.semver=1.0.57-SNAPSHOT
Loading