Skip to content

Commit

Permalink
added cache
Browse files Browse the repository at this point in the history
  • Loading branch information
hohonuuli committed Jul 11, 2024
1 parent 128fd58 commit 62d74f4
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 12 deletions.
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ lazy val oni = project
),
libraryDependencies ++= Seq(
auth0,
caffeine,
circeCore,
circeGeneric,
circeParser,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import sttp.tapir.server.ServerEndpoint
import org.mbari.oni.domain.{ConceptCreate, ConceptDelete, ConceptMetadata, ConceptUpdate, ErrorMsg, ServerError}
import org.mbari.oni.etc.circe.CirceCodecs.given
import org.mbari.oni.etc.jwt.JwtService
import org.mbari.oni.services.{ConceptNameService, ConceptService}
import org.mbari.oni.services.{ConceptCache, ConceptNameService, ConceptService}
import sttp.shared.Identity

import scala.concurrent.{ExecutionContext, Future}
Expand All @@ -24,6 +24,7 @@ class ConceptEndpoints(entityManagerFactory: EntityManagerFactory)(using jwtServ

private val service = ConceptService(entityManagerFactory)
private val conceptNameService = ConceptNameService(entityManagerFactory)
private val conceptCache = ConceptCache(service, conceptNameService)
private val base = "concept"
private val tag = "Concept"

Expand All @@ -38,7 +39,7 @@ class ConceptEndpoints(entityManagerFactory: EntityManagerFactory)(using jwtServ
val allEndpointImpl: ServerEndpoint[Any, Future] = allEndpoint.serverLogic { _ =>
val limit = 10000
val offset = 0
handleErrorsAsync(conceptNameService.findAllNames(limit, offset))
handleErrorsAsync(conceptCache.findAllNames(limit, offset))
}

val createEndpoint: Endpoint[Option[String], ConceptCreate, ErrorMsg, ConceptMetadata, Any] = secureEndpoint
Expand Down Expand Up @@ -109,7 +110,7 @@ class ConceptEndpoints(entityManagerFactory: EntityManagerFactory)(using jwtServ
.tag(tag)

val findByNameImpl: ServerEndpoint[Any, Future] = findByName.serverLogic { name =>
handleErrorsAsync(service.findByName(name))
handleErrorsAsync(conceptCache.findByName(name))
}

val findByNameContaining: Endpoint[Unit, String, ErrorMsg, Seq[ConceptMetadata], Any] = openEndpoint
Expand Down
21 changes: 21 additions & 0 deletions oni/src/main/scala/org/mbari/oni/etc/jdk/CloseableLock.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) Monterey Bay Aquarium Research Institute 2024
*
* oni code is non-public software. Unauthorized copying of this file,
* via any medium is strictly prohibited. Proprietary and confidential.
*/

package org.mbari.oni.etc.jdk

import java.util.concurrent.locks.ReentrantLock

class CloseableLock extends ReentrantLock with AutoCloseable {

def lockAndGet: CloseableLock = {
lock();
this
}

override def close(): Unit = unlock()

}
20 changes: 11 additions & 9 deletions oni/src/main/scala/org/mbari/oni/jdbc/FastPhylogenyService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,17 @@ class FastPhylogenyService(entityManagerFactory: EntityManagerFactory):

lock.lock()
log.atDebug.log("Loading cache ...")
val cache = executeQuery()
if cache.nonEmpty then
val lu = cache.maxBy(_.lastUpdate.toEpochMilli)
lastUpdate = lu.lastUpdate

val r = MutableConcept.toTree(cache)
rootNode = r._1
allNodes = r._2
lock.unlock()
try
val cache = executeQuery()
if cache.nonEmpty then
val lu = cache.maxBy(_.lastUpdate.toEpochMilli)
lastUpdate = lu.lastUpdate

val r = MutableConcept.toTree(cache)
rootNode = r._1
allNodes = r._2
finally
lock.unlock()

def findLastUpdate(): Instant =
val attempt = Using(entityManagerFactory.createEntityManager) { entityManager =>
Expand Down
64 changes: 64 additions & 0 deletions oni/src/main/scala/org/mbari/oni/services/ConceptCache.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) Monterey Bay Aquarium Research Institute 2024
*
* oni code is non-public software. Unauthorized copying of this file,
* via any medium is strictly prohibited. Proprietary and confidential.
*/

package org.mbari.oni.services

import com.github.benmanes.caffeine.cache.{Cache, Caffeine}
import org.mbari.oni.domain.ConceptMetadata
import org.mbari.oni.etc.jdk.Loggers.{*, given}

import java.util.concurrent.TimeUnit

class ConceptCache(conceptService: ConceptService, conceptNameService: ConceptNameService) {

private val log = System.getLogger(getClass.getName)

private val nameCache: Cache[String, ConceptMetadata] = Caffeine
.newBuilder()
.expireAfterWrite(15, TimeUnit.MINUTES)
.build[String, ConceptMetadata]()

private val allNamesCache: Cache[String, Seq[String]] = Caffeine
.newBuilder()
.expireAfterWrite(15, TimeUnit.MINUTES)
.build[String, Seq[String]]()

def findByName(name: String): Either[Throwable, ConceptMetadata] = {
Option(nameCache.getIfPresent(name)) match {
case Some(node) => Right(node)
case None =>
conceptService.findByName(name) match
case Left(e) =>
log.atInfo.withCause(e).log(s"Failed to find concept by name: $name")
Left(e)
case Right(conceptNode) =>
nameCache.put(name, conceptNode)
Right(conceptNode)
}
}

def findAllNames(limit: Int, offset: Int): Either[Throwable, Seq[String]] = {
val allNames = Option(allNamesCache.getIfPresent(ConceptCache.AllNamesCacheKey))
if (allNames.isDefined && allNames.get.nonEmpty) {
Right(allNames.get.slice(offset, offset + limit))
}
else {
conceptNameService.findAllNames(1000000, 0) match
case Left(e) =>
log.atError.withCause(e).log("Failed to find all concept names")
Left(e)
case Right(names) =>
allNamesCache.put(ConceptCache.AllNamesCacheKey, names)
Right(names.slice(offset, offset + limit))
}
}

}

object ConceptCache {
val AllNamesCacheKey = "all-names"
}
1 change: 1 addition & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import sbt.*
object Dependencies {

lazy val auth0 = "com.auth0" % "java-jwt" % "4.4.0"
lazy val caffeine = "com.github.ben-manes.caffeine" % "caffeine" % "3.1.8"

val circeVersion = "0.14.9"
lazy val circeCore = "io.circe" %% "circe-core" % circeVersion
Expand Down

0 comments on commit 62d74f4

Please sign in to comment.