Создание параметризованного ScalaCache общего с конфигурацией времени выполнения - PullRequest
0 голосов
/ 05 октября 2018

Git-репозиторий, содержащий проблему, можно найти здесь https://github.com/mdedetrich/scalacache-example

Проблема, с которой я сталкиваюсь в настоящее время, заключается в том, что я пытаюсь настроить независимый бэкэнд моего ScalaCache, настраивая его во время выполнения, используя typesafe config.

Проблема, с которой я столкнулся, заключается в том, что ScalaCache параметризует конструкторы кэша, т.е. для создания кэша кофеина вы должны сделать

ScalaCache(CaffeineCache())

, где, как для SentinelRedisCache вы бы сделали

ScalaCache(SentinelRedisCache("", Set.empty, ""))

В моем случае я создал универсальную оболочку кэша с именем MyCache, как показано ниже

import scalacache.ScalaCache
import scalacache.serialization.Codec

final case class MyCache[CacheRepr](scalaCache: ScalaCache[CacheRepr])(
  implicit stringCodec: Codec[Int, CacheRepr]) {

  def putInt(value: Int) = scalaCache.cache.put[Int]("my_int", value, None)
}

Нам нужно нестивместе с CacheRepr, потому что именно так ScalaCache знает, как сериализовать любой тип T.CaffeineCache использует CacheRepr, что составляет InMemoryRepr, где SentinelRedisCache использует CacheRepr, что составляет Array[Byte].

И вот где суть проблемы, у меня есть Config, который просто хранит, какой кэш используется, то есть

import scalacache.Cache
import scalacache.caffeine.CaffeineCache
import scalacache.redis.SentinelRedisCache

final case class ApplicationConfig(cache: Cache[_])

Причина, по которой он Cache[_], заключается в том, что во время компиляции мы не знаем, какой кеш используется, ApplicationConfig будетсоздается во время выполнения с помощью либо CaffeineCache / SentinelRedisCache.

И в этом суть проблемы, Scala не может найти неявное Codec для подстановочного типа, если мы просто используем applicationConfig.cache как конструктор, т.е. https://github.com/mdedetrich/scalacache-example/blob/master/src/main/scala/Main.scala#L17

Если мы раскомментируем вышеприведенную строку, мы получим

[error] /Users/mdedetrich/github/scalacache-example/src/main/scala/Main.scala:17:37: Could not find any Codecs for type Int and _$1. Please provide one or import scalacache._
[error] Error occurred in an application involving default arguments.
[error]   val myCache3: MyCache[_] = MyCache(ScalaCache(applicationConfig.cache)) // This doesn't

Кто-нибудь знает, как решить эту проблему, по сути, я хочу указать это в моемApplicationConfig, кеш имеет тип Cache[InMemoryRepr | Array[Byte]], а не просто Cache[_] (так что компилятор Scala знает, что искать значения InMemoryRepr or Array[Byte] и для MyCache можно определить примерно так

final case class MyCache[CacheRepr <: InMemoryRepr | Array[Byte]](scalaCache: ScalaCache[CacheRepr])

1 Ответ

0 голосов
/ 05 октября 2018

Вы, похоже, просите компилятор разрешить неявные значения на основе выбора типа кэша во время выполнения.Это невозможно, потому что компилятор больше не работает к моменту запуска кода приложения.

Необходимо разрешить тип во время компиляции, а не во время выполнения.Таким образом, вам нужно определить trait, представляющий абстрактный интерфейс кеша, и предоставить фабричную функцию, которая возвращает конкретный экземпляр на основе параметра в ApplicationConfig.Это может выглядеть примерно так (не проверено):

sealed trait MyScalaCache {
  def putInt(value: Int)
}

object MyScalaCache {
  def apply(): MyScalaCache =
    if (ApplicationConfig.useCaffine) {
      MyCache(ScalaCache(CaffeineCache())
    } else {
      MyCache(ScalaCache(SentinelRedisCache("", Set.empty, ""))
    }
}

final case class MyCache[CacheRepr](scalaCache: ScalaCache[CacheRepr]) extends MyScalaCache (
  implicit stringCodec: Codec[Int, CacheRepr]) {

  def putInt(value: Int) = scalaCache.cache.put[Int]("my_int", value, None)
}

Компилятор разрешит неявное в MyCache во время компиляции, когда два конкретных экземпляра указаны в apply.

...