Skip to content
This repository was archived by the owner on Mar 24, 2025. It is now read-only.
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
39 changes: 20 additions & 19 deletions smt-cache/src/main/scala/org/bitlap/cache/Cache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ package org.bitlap.cache
import org.bitlap.cache.GenericCache.Aux
import org.bitlap.common.{ CaseClassExtractor, CaseClassField }

import java.util.concurrent.atomic.AtomicBoolean
import scala.concurrent.{ ExecutionContext, Future }

/** @author
Expand All @@ -39,15 +38,8 @@ object Cache {
keyBuilder: CacheKeyBuilder[K]
): CacheRef[K, T, Future] =
new CacheRef[K, cache.Out, Future] {
private lazy val initFlag = new AtomicBoolean(false)

override def init(initKvs: => Map[K, cache.Out]): Future[Unit] =
if (initFlag.compareAndSet(false, true)) {
putTAll(initKvs)
} else Future.successful(())

override def putTAll(map: => Map[K, cache.Out]): Future[Unit] =
cache.putAll(map)
override def batchPutT(data: => Map[K, cache.Out]): Future[Unit] =
cache.putAll(data)

override def getT(key: K): Future[Option[cache.Out]] =
cache.get(key)
Expand All @@ -62,23 +54,24 @@ object Cache {

override def clear(): Future[Unit] = cache.clear()

override def remove(key: K): Future[Unit] = cache.remove(key)

override def getAllT: Future[Map[K, cache.Out]] = cache.getAll

override def safeRefreshT(allNewData: Map[K, T]): Future[Unit] =
this.getAllT.map { t =>
val invalidData = t.keySet.filterNot(allNewData.keySet)
this.batchPutT(allNewData).map(_ => invalidData.foreach(this.remove))
}
}

def getSyncCache[K, T <: Product](implicit
cache: Aux[K, T, Identity],
keyBuilder: CacheKeyBuilder[K]
): CacheRef[K, T, Identity] =
new CacheRef[K, cache.Out, Identity] {
private lazy val initFlag = new AtomicBoolean(false)

override def init(initKvs: => Map[K, cache.Out]): Identity[Unit] =
if (initFlag.compareAndSet(false, true)) {
putTAll(initKvs)
} else ()

override def putTAll(map: => Map[K, cache.Out]): Identity[Unit] =
map.foreach(kv => cache.put(kv._1, kv._2))
override def batchPutT(data: => Map[K, cache.Out]): Identity[Unit] =
data.foreach(kv => cache.put(kv._1, kv._2))

override def getT(key: K): Identity[Option[cache.Out]] =
cache.get(key)
Expand All @@ -92,6 +85,14 @@ object Cache {
override def clear(): Identity[Unit] = cache.clear()

override def getAllT: Identity[Map[K, cache.Out]] = cache.getAll

override def remove(key: K): Identity[Unit] = cache.remove(key)

override def safeRefreshT(allNewData: Map[K, cache.Out]): Identity[Unit] = {
val invalidData = this.getAllT.keySet.filterNot(allNewData.keySet)
this.batchPutT(allNewData)
invalidData.foreach(this.remove)
}
}

}
13 changes: 10 additions & 3 deletions smt-cache/src/main/scala/org/bitlap/cache/CacheAdapter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ import java.util.Collections
* @version 1.0,2022/7/5
*/
trait CacheAdapter[V] {

def getAllKeys: Set[String]

def putAll(map: Map[String, V]): Unit
def batchPut(data: Map[String, V]): Unit

def put(k: String, v: V): Unit

def get(k: String): V

def clear(): Unit

def remove(k: String): Unit
}

object CacheAdapter {
Expand All @@ -60,27 +63,31 @@ object CacheAdapter {

override def getAllKeys: Set[String] = underlyingCache.keySet().asScala.toSet

override def putAll(map: Map[String, V]): Unit = underlyingCache.putAll(map.asJava)
override def batchPut(data: Map[String, V]): Unit = underlyingCache.putAll(data.asJava)

override def put(k: String, v: V): Unit = underlyingCache.put(k, v)

override def get(k: String): V = underlyingCache.get(k)

override def clear(): Unit = underlyingCache.clear()

override def remove(k: String): Unit = underlyingCache.remove(k)
}

class ConcurrentMapCacheAdapter[V](underlyingCache: java.util.concurrent.ConcurrentMap[String, V])
extends CacheAdapter[V] {

override def getAllKeys: Set[String] = underlyingCache.keySet().asScala.toSet

override def putAll(map: Map[String, V]): Unit = underlyingCache.putAll(map.asJava)
override def batchPut(data: Map[String, V]): Unit = underlyingCache.putAll(data.asJava)

override def put(k: String, v: V): Unit = underlyingCache.put(k, v)

override def get(k: String): V = underlyingCache.get(k)

override def clear(): Unit = underlyingCache.clear()

override def remove(k: String): Unit = underlyingCache.remove(k)
}

}
8 changes: 5 additions & 3 deletions smt-cache/src/main/scala/org/bitlap/cache/CacheRef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ import org.bitlap.common.CaseClassField
*/
trait CacheRef[In, T <: Product, F[_]] {

def init(initKvs: => Map[In, T]): F[Unit]

def putTAll(map: => Map[In, T]): F[Unit]
def batchPutT(data: => Map[In, T]): F[Unit]

def getT(key: In): F[Option[T]]

Expand All @@ -42,4 +40,8 @@ trait CacheRef[In, T <: Product, F[_]] {
def getAllT: F[Map[In, T]]

def clear(): F[Unit]

def remove(key: In): F[Unit]

def safeRefreshT(allNewData: Map[In, T]): F[Unit]
}
37 changes: 23 additions & 14 deletions smt-cache/src/main/scala/org/bitlap/cache/GenericCache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,16 @@ sealed trait GenericCache[K, F[_]] {

def clear(): F[Unit]

def remove(key: K)(implicit keyBuilder: CacheKeyBuilder[K]): F[Unit]

}

object GenericCache {

type Aux[K, Out0, F[_]] = GenericCache[K, F] { type Out = Out0 }

def apply[K, Out0 <: Product](cacheStrategy: CacheStrategy): Aux[K, Out0, Identity] = new GenericCache[K, Identity] {
private val adaptedCache: CacheAdapter[Out0] = CacheAdapter.adapted[Out0](cacheStrategy)
private val cacheAdapter: CacheAdapter[Out0] = CacheAdapter.adapted[Out0](cacheStrategy)

override type Out = Out0

Expand All @@ -57,27 +59,30 @@ object GenericCache {
)(implicit
keyBuilder: CacheKeyBuilder[K]
): Identity[Option[Out]] = {
val v = adaptedCache.get(keyBuilder.generateKey(key))
val v = cacheAdapter.get(keyBuilder.generateKey(key))
if (v == null) None else Option(v)
}

override def put(key: K, value: Out)(implicit
keyBuilder: CacheKeyBuilder[K]
): Identity[Unit] =
adaptedCache.put(keyBuilder.generateKey(key), value)
cacheAdapter.put(keyBuilder.generateKey(key), value)

override def putAll(map: Map[K, Out0])(implicit keyBuilder: CacheKeyBuilder[K]): Identity[Unit] =
adaptedCache.putAll(map.map(kv => keyBuilder.generateKey(kv._1) -> kv._2))
cacheAdapter.batchPut(map.map(kv => keyBuilder.generateKey(kv._1) -> kv._2))

override def clear(): Identity[Unit] = adaptedCache.clear()
override def clear(): Identity[Unit] = cacheAdapter.clear()

override def getAll(implicit keyBuilder: CacheKeyBuilder[K]): Identity[Map[K, Out0]] =
adaptedCache.getAllKeys
.map(key => keyBuilder.unGenerateKey(key) -> adaptedCache.get(key))
cacheAdapter.getAllKeys
.map(key => keyBuilder.unGenerateKey(key) -> cacheAdapter.get(key))
.collect {
case (k, out) if out != null => k -> out
}
.toMap

override def remove(key: K)(implicit keyBuilder: CacheKeyBuilder[K]): Identity[Unit] =
cacheAdapter.remove(keyBuilder.generateKey(key))
}

def apply[K, Out0 <: Product](
Expand All @@ -86,36 +91,40 @@ object GenericCache {
): Aux[K, Out0, Future] =
new GenericCache[K, Future] {
implicit val ec = executionContext
private val adaptedCache: CacheAdapter[Out0] = CacheAdapter.adapted[Out0](cacheStrategy)
private val cacheAdapter: CacheAdapter[Out0] = CacheAdapter.adapted[Out0](cacheStrategy)

override type Out = Out0

override def get(key: K)(implicit keyBuilder: CacheKeyBuilder[K]): Future[Option[Out]] =
Future {
val v = adaptedCache.get(keyBuilder.generateKey(key))
val v = cacheAdapter.get(keyBuilder.generateKey(key))
if (v == null) None else Option(v)
}

def put(key: K, value: Out)(implicit keyBuilder: CacheKeyBuilder[K]): Future[Unit] =
Future {
adaptedCache.put(keyBuilder.generateKey(key), value)
cacheAdapter.put(keyBuilder.generateKey(key), value)
}.map(_ => ())

override def putAll(map: Map[K, Out0])(implicit keyBuilder: CacheKeyBuilder[K]): Future[Unit] =
Future {
adaptedCache.putAll(map.map(kv => keyBuilder.generateKey(kv._1) -> kv._2))
cacheAdapter.batchPut(map.map(kv => keyBuilder.generateKey(kv._1) -> kv._2))
}

override def getAll(implicit keyBuilder: CacheKeyBuilder[K]): Future[Map[K, Out0]] =
Future {
adaptedCache.getAllKeys
.map(key => keyBuilder.unGenerateKey(key) -> adaptedCache.get(key))
cacheAdapter.getAllKeys
.map(key => keyBuilder.unGenerateKey(key) -> cacheAdapter.get(key))
.collect {
case (k, out) if out != null => k -> out
}
.toMap
}

override def clear(): Future[Unit] = Future.successful(adaptedCache.clear())
override def clear(): Future[Unit] = Future.successful(cacheAdapter.clear())

override def remove(key: K)(implicit keyBuilder: CacheKeyBuilder[K]): Future[Unit] = Future {
cacheAdapter.remove(keyBuilder.generateKey(key))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class CacheCustomCaseSpec extends AnyFlatSpec with Matchers {
"cache1" should "ok while uses lru cache" in {
implicit val lruCache = CacheImplicits.testEntitySyncLruCache
val cache = Cache.getSyncCache[String, TestEntity]
cache.init(data)
cache.batchPutT(data)

cache.putT("etc3", TestEntity("eth3", "hello3", "world2"))

Expand All @@ -54,7 +54,7 @@ class CacheCustomCaseSpec extends AnyFlatSpec with Matchers {
val result2: Option[TestEntity] = cache.getT("etc1")
result2 shouldBe None

cache.putTAll(data)
cache.batchPutT(data)

val result3: Option[TestEntity] = cache.getT("etc1")
result3 shouldBe Some(TestEntity("eth1", "hello1", "world2"))
Expand All @@ -70,16 +70,18 @@ class CacheCustomCaseSpec extends AnyFlatSpec with Matchers {

override def getAllKeys: Set[String] = underlyingCache.keySet().asScala.toSet

override def putAll(map: Map[String, TestEntity]): Unit = underlyingCache.putAll(map.asJava)
override def batchPut(data: Map[String, TestEntity]): Unit = underlyingCache.putAll(data.asJava)

override def put(k: String, v: TestEntity): Unit = underlyingCache.put(k, v)

override def get(k: String): TestEntity = underlyingCache.get(k)

override def clear(): Unit = underlyingCache.clear()

override def remove(k: String): Unit = underlyingCache.remove(k)
}))
val cache = Cache.getSyncCache[String, TestEntity]
cache.init(data)
cache.batchPutT(data)
val result: Option[TestEntity] = cache.getT("btc")
result shouldBe Some(TestEntity("btc", "hello1", "world1"))

Expand Down
34 changes: 26 additions & 8 deletions smt-cache/src/test/scala/org/bitlap/cache/CacheSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class CacheSpec extends AnyFlatSpec with Matchers {

"cache1" should "get entity from cache successfully" in {
val cache = Cache.getSyncCache[String, TestEntity]
cache.init(data)
cache.batchPutT(data)
val result: Option[TestEntity] = cache.getT("etc")
result shouldBe data.get("etc")

Expand All @@ -55,7 +55,7 @@ class CacheSpec extends AnyFlatSpec with Matchers {

"cache2" should "get entity's field from cache successfully" in {
val cache = Cache.getSyncCache[String, TestEntity]
cache.init(data)
cache.batchPutT(data)
val result = cache.getTField("etc", CaseClassField[TestEntity](_.key))
result shouldBe Some("world2")

Expand All @@ -65,14 +65,14 @@ class CacheSpec extends AnyFlatSpec with Matchers {

"cache3" should "get entity's field after refresh" in {
val cache = Cache.getSyncCache[String, TestEntity]
cache.init(data)
cache.batchPutT(data)
val newData = Map(
"btc" -> TestEntity("btc", "hello1", "world1"),
"btc-zh-cn" -> TestEntity("btc", "你好啊", "你好哦"),
"etc" -> TestEntity("eth", "hello2", "world2")
)
cache.clear()
cache.putTAll(newData)
cache.batchPutT(newData)

val result: Option[TestEntity] = cache.getT("btc-zh-cn")
result shouldBe newData.get("btc-zh-cn")
Expand All @@ -91,9 +91,9 @@ class CacheSpec extends AnyFlatSpec with Matchers {
val cache = Cache.getAsyncCache[String, TestEntity]

val ret = for {
_ <- cache.init(newData)
_ <- cache.batchPutT(newData)
btcKey <- cache.getTField("btc", CaseClassField[TestEntity](_.key))
_ <- cache.putTAll(newData2)
_ <- cache.batchPutT(newData2)
ethKey <- cache.getTField("eth", CaseClassField[TestEntity](_.key))
} yield btcKey -> ethKey

Expand All @@ -109,7 +109,7 @@ class CacheSpec extends AnyFlatSpec with Matchers {
val cache = Cache.getAsyncCache[String, TestEntity]

val ret = for {
_ <- cache.init(newData)
_ <- cache.batchPutT(newData)
btcKey <- cache.getTField("btc", CaseClassField[TestEntity](_.key))
_ <- cache.clear()
ethKey <- cache.getTField("eth", CaseClassField[TestEntity](_.key))
Expand All @@ -121,7 +121,7 @@ class CacheSpec extends AnyFlatSpec with Matchers {

"cache6" should "get entity with selectField successfully" in {
val cache = Cache.getSyncCache[String, TestEntity]
cache.init(data)
cache.batchPutT(data)

val id: Identity[Option[CaseClassField#Field]] =
cache.getTField("etc", CaseClassField[TestEntity](_.id))
Expand All @@ -133,4 +133,22 @@ class CacheSpec extends AnyFlatSpec with Matchers {
println(value)
value shouldBe data.get("etc").map(_.value)
}

"cache7" should "refresh successfully" in {
val cache = Cache.getSyncCache[String, TestEntity]
cache.batchPutT(data)
val result: Option[TestEntity] = cache.getT("etc")
result shouldBe data.get("etc")

val newData = Map(
"btc" -> TestEntity("btc", "id123", "btc_key123"),
"btc-zh-cn" -> TestEntity("btc", "id123", "btc_zh_key123")
)

cache.safeRefreshT(newData)

val result2 = cache.getT("eth")
result2 shouldBe None
}

}
Loading