Skip to content

Commit

Permalink
Extract session cache building into a factory + bump core and tests (#…
Browse files Browse the repository at this point in the history
…192)

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

* Update src/jvmMain/kotlin/controller/validation/GuavaSessionCacheAdapter.kt

Co-authored-by: Dylan Hall <dehall@mitre.org>

---------

Co-authored-by: Dylan Hall <dehall@mitre.org>
  • Loading branch information
dotasek and dehall authored Oct 17, 2024
1 parent 0a8de58 commit 158e8ec
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 27 deletions.
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

0 comments on commit 158e8ec

Please sign in to comment.