Skip to content

Commit

Permalink
Fix (#199)
Browse files Browse the repository at this point in the history
* support future
  • Loading branch information
jxnu-liguobin authored Jun 11, 2022
1 parent da6d0fb commit aecb767
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 56 deletions.
30 changes: 16 additions & 14 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@ lazy val scala211 = "2.11.12"
lazy val scala213 = "2.13.8"
lazy val lastVersionForExamples = "0.5.2"

lazy val configVersion = "1.4.2"
lazy val scalatestVersion = "3.2.12"
lazy val zioVersion = "1.0.14"
lazy val zioLoggingVersion = "0.5.14"
lazy val caffeineVersion = "2.9.3"
lazy val zioRedisVersion = "0.0.0+381-86c20614-SNAPSHOT" // 实验性质的
lazy val zioSchemaVersion = "0.1.9"
lazy val scalaLoggingVersion = "3.9.5"
lazy val playJsonVersion = "2.7.4"
lazy val log4jVersion = "2.17.2"
lazy val jacksonScalaVersion = "2.13.3"
lazy val jraftVersion = "1.3.9"
lazy val configVersion = "1.4.2"
lazy val scalatestVersion = "3.2.12"
lazy val zioVersion = "1.0.14"
lazy val zioLoggingVersion = "0.5.14"
lazy val caffeineVersion = "2.9.3"
lazy val zioRedisVersion = "0.0.0+381-86c20614-SNAPSHOT" // 实验性质的
lazy val zioSchemaVersion = "0.1.9"
lazy val scalaLoggingVersion = "3.9.5"
lazy val playJsonVersion = "2.7.4"
lazy val log4jVersion = "2.17.2"
lazy val jacksonScalaVersion = "2.13.3"
lazy val jraftVersion = "1.3.9"
lazy val scalaCollectionCompatVersion = "2.7.0"

lazy val commonSettings =
Seq(
Expand Down Expand Up @@ -132,8 +133,9 @@ lazy val `smt-common` = (project in file("smt-common"))
lazy val `smt-cache` = (project in file("smt-cache"))
.settings(commonSettings)
.settings(
name := "smt-cache",
crossScalaVersions := List(scala213, scala212, scala211)
name := "smt-cache",
crossScalaVersions := List(scala213, scala212, scala211),
libraryDependencies += "org.scala-lang.modules" %% "scala-collection-compat" % scalaCollectionCompatVersion
)
.settings(Publishing.publishSettings)
.settings(paradise())
Expand Down
51 changes: 41 additions & 10 deletions smt-cache/src/main/scala/org/bitlap/cache/Cache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,68 @@ import org.bitlap.common.{ CaseClassExtractor, CaseClassField }
import java.util.concurrent.atomic.AtomicBoolean
import scala.reflect.ClassTag
import scala.reflect.runtime.universe._
import scala.concurrent.{ ExecutionContext, Future }

/** @author
* 梦境迷离
* @version 1.0,6/8/22
*/
object Cache {

def getCache[T <: Product](implicit
cache: Aux[String, T],
def getAsyncCache[T <: Product](implicit
cache: Aux[String, T, Future],
executionContext: ExecutionContext,
classTag: ClassTag[T],
typeTag: TypeTag[T]
): CacheRef[String, T] =
new CacheRef[String, cache.Out] {
): CacheRef[String, T, Future] =
new CacheRef[String, cache.Out, Future] {
private lazy val initFlag = new AtomicBoolean(false)

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

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

override def getT(key: String)(implicit keyBuilder: CacheKeyBuilder[String]): Future[Option[cache.Out]] =
cache.get(key)

override def putT(key: String, value: cache.Out)(implicit keyBuilder: CacheKeyBuilder[String]): Future[Unit] =
cache.put(key, value)

override def getTField(key: String, field: CaseClassField)(implicit
keyBuilder: CacheKeyBuilder[String]
): Future[Option[field.Field]] =
getT(key).map(opt => opt.flatMap(t => CaseClassExtractor.getFieldValueUnSafely[cache.Out](t, field)))
}

def getSyncCache[T <: Product](implicit
cache: Aux[String, T, Identity],
classTag: ClassTag[T],
typeTag: TypeTag[T]
): CacheRef[String, T, Identity] =
new CacheRef[String, cache.Out, Identity] {
private lazy val initFlag = new AtomicBoolean(false)

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

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

override def getT(key: String)(implicit keyBuilder: CacheKeyBuilder[String]): Option[cache.Out] = cache.get(key)
override def getT(key: String)(implicit keyBuilder: CacheKeyBuilder[String]): Identity[Option[cache.Out]] =
cache.get(key)

override def putT(key: String, value: cache.Out)(implicit keyBuilder: CacheKeyBuilder[String]): Unit =
override def putT(key: String, value: cache.Out)(implicit keyBuilder: CacheKeyBuilder[String]): Identity[Unit] =
cache.put(key, value)

override def getTField(key: String, field: CaseClassField)(implicit
keyBuilder: CacheKeyBuilder[String]
): Option[field.Field] =
): Identity[Option[field.Field]] =
getT(key).flatMap(t => CaseClassExtractor.getFieldValueUnSafely[cache.Out](t, field))
}

Expand Down
12 changes: 6 additions & 6 deletions smt-cache/src/main/scala/org/bitlap/cache/CacheRef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ import org.bitlap.common.CaseClassField
* 梦境迷离
* @version 1.0,6/8/22
*/
trait CacheRef[In, T <: Product] {
trait CacheRef[In, T <: Product, F[_]] {

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

def putTAll(map: => Map[String, T]): Unit
def putTAll(map: => Map[String, T]): F[Unit]

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

def putT(key: In, value: T)(implicit keyBuilder: CacheKeyBuilder[String]): Unit
def putT(key: In, value: T)(implicit keyBuilder: CacheKeyBuilder[String]): F[Unit]

def getTField(key: In, field: CaseClassField)(implicit keyBuilder: CacheKeyBuilder[In]): Option[field.Field]
def getTField(key: In, field: CaseClassField)(implicit keyBuilder: CacheKeyBuilder[In]): F[Option[field.Field]]
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ package org.bitlap.cache
* 梦境迷离
* @version 1.0,6/8/22
*/
trait CacheType
trait CacheStrategy

object CacheType {
object CacheStrategy {

case class Lru(maxSize: Int = 1000) extends CacheType
case object Normal extends CacheType
case class Lru(maxSize: Int = 1000) extends CacheStrategy
case object Normal extends CacheStrategy
}
71 changes: 59 additions & 12 deletions smt-cache/src/main/scala/org/bitlap/cache/GenericCache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,85 @@
*/

package org.bitlap.cache
import java.util
import java.util.Collections
import scala.concurrent.{ ExecutionContext, Future }
import scala.jdk.CollectionConverters._

/** @author
* 梦境迷离
* @version 1.0,6/8/22
*/
sealed trait GenericCache[K] {
sealed trait GenericCache[K, F[_]] {

type Out <: Product

def get(key: K)(implicit keyBuilder: CacheKeyBuilder[K]): Option[Out]
def get(key: K)(implicit keyBuilder: CacheKeyBuilder[K]): F[Option[Out]]

def put(key: K, value: Out)(implicit keyBuilder: CacheKeyBuilder[K]): Unit
def put(key: K, value: Out)(implicit keyBuilder: CacheKeyBuilder[K]): F[Unit]

}
def putAll(map: Map[K, Out])(implicit keyBuilder: CacheKeyBuilder[K]): F[Unit]

}
object GenericCache {

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

def apply[K, Out0 <: Product](cacheType: CacheType): Aux[K, Out0] = new GenericCache[K] {
private val typedCache = cacheType match {
case CacheType.Lru(maxSize) => new java.util.LinkedHashMap[String, Out0](maxSize, 0.75f, true)
case CacheType.Normal => new java.util.LinkedHashMap[String, Out0]()
}
def apply[K, Out0 <: Product](cacheStrategy: CacheStrategy): Aux[K, Out0, Identity] = new GenericCache[K, Identity] {

private val typedCache = getCacheByStrategy[Out0](cacheStrategy)
override type Out = Out0
override def get(key: K)(implicit keyBuilder: CacheKeyBuilder[K]): Option[Out] = {
override def get(
key: K
)(implicit
keyBuilder: CacheKeyBuilder[K]
): Identity[Option[Out]] = {
val v = typedCache.get(keyBuilder.generateKey(key))
if (v == null) None else Option(v)
}

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

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

def apply[K, Out0 <: Product](
cacheStrategy: CacheStrategy,
executionContext: ExecutionContext
): Aux[K, Out0, Future] =
new GenericCache[K, Future] {
implicit val ec = executionContext
private val typedCache = getCacheByStrategy[Out0](cacheStrategy)
override type Out = Out0

override def get(key: K)(implicit keyBuilder: CacheKeyBuilder[K]): Future[Option[Out]] =
Future {
val v = typedCache.get(keyBuilder.generateKey(key))
println(s"key => $key | value => $v")
if (v == null) None else Option(v)
}

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

override def putAll(map: Map[K, Out0])(implicit keyBuilder: CacheKeyBuilder[K]): Future[Unit] =
Future {
println(s"all map => ${map.mkString(" | ")}")
typedCache.putAll(map.map(kv => keyBuilder.generateKey(kv._1) -> kv._2).asJava)
}
}

private def getCacheByStrategy[Out0](cacheType: CacheStrategy): util.Map[String, Out0] =
cacheType match {
case CacheStrategy.Lru(maxSize) =>
Collections.synchronizedMap(new java.util.LinkedHashMap[String, Out0](maxSize, 0.75f, true))
case CacheStrategy.Normal => new java.util.concurrent.ConcurrentHashMap[String, Out0]()
// TODO other cache, redis cache, caffeine cache
}
}
30 changes: 30 additions & 0 deletions smt-cache/src/main/scala/org/bitlap/cache/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2022 bitlap
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package org.bitlap

/** @author
* 梦境迷离
* @version 1.0,6/10/22
*/
package object cache {
type Identity[O] = O
}
10 changes: 8 additions & 2 deletions smt-cache/src/test/scala/org/bitlap/cache/CacheImplicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@
*/

package org.bitlap.cache
import scala.concurrent.ExecutionContext

object CacheImplicits {

implicit lazy val testEntityCache: GenericCache.Aux[String, TestEntity] =
GenericCache[String, TestEntity](CacheType.Normal)
// NOTE:These are two completely different caches and the data is not shared
// If you don't use global, you should also make sure this place the same as getAsyncCache's
implicit lazy val testEntitySyncCache =
GenericCache[String, TestEntity](CacheStrategy.Normal)

implicit lazy val testEntityAsyncCache =
GenericCache[String, TestEntity](CacheStrategy.Normal, ExecutionContext.Implicits.global)

}
32 changes: 29 additions & 3 deletions smt-cache/src/test/scala/org/bitlap/cache/CacheSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ package org.bitlap.cache
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.bitlap.cache.CacheImplicits._
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt

/** @author
* 梦境迷离
Expand All @@ -37,14 +40,14 @@ class CacheSpec extends AnyFlatSpec with Matchers {
)

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

"cache2" should "get entity's field from cache successfully" in {
val cache: CacheRef[String, TestEntity] = Cache.getCache[TestEntity]
val cache = Cache.getSyncCache[TestEntity]
cache.init(data)
val result: Option[String] = cache.getTField("etc", TestEntity.key)
result shouldBe Some("world2")
Expand All @@ -54,7 +57,7 @@ class CacheSpec extends AnyFlatSpec with Matchers {
}

"cache3" should "get entity's field after refresh" in {
val cache: CacheRef[String, TestEntity] = Cache.getCache[TestEntity]
val cache = Cache.getSyncCache[TestEntity]
cache.init(data)
val newData = Map(
"btc" -> TestEntity("btc", "hello1", "world1"),
Expand All @@ -66,4 +69,27 @@ class CacheSpec extends AnyFlatSpec with Matchers {
val result: Option[TestEntity] = cache.getT("btc-zh-cn")
result shouldBe newData.get("btc-zh-cn")
}

"cache4" should "async cache" in {
val newData = Map(
"btc" -> TestEntity("btc", "id123", "btc_key123"),
"btc-zh-cn" -> TestEntity("btc", "id123", "btc_zh_key123")
)
val newData2 = Map(
"btc" -> TestEntity("btc", "id456", "bt_key456"),
"btc-zh-cn" -> TestEntity("btc", "id456", "btc_zh_key456"),
"eth" -> TestEntity("btc", "id456", "eth_key456")
)
val cache = Cache.getAsyncCache[TestEntity]

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

Await.result(ret, 3.seconds) shouldBe Option("btc_key123") -> Option("eth_key456")

}
}
Loading

0 comments on commit aecb767

Please sign in to comment.