All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalacache.redis.RedisCache.scala Maven / Gradle / Ivy

There is a newer version: 0.28.0
Show newest version
package scalacache.redis

import scalacache.{ LoggingSupport, Cache }
import scala.concurrent.duration._
import com.typesafe.scalalogging.StrictLogging
import redis.clients.jedis.{ JedisPool, Jedis }
import java.nio.charset.Charset
import scala.concurrent.{ Future, ExecutionContext, blocking }

/**
 * Thin wrapper around Jedis
 * @param customClassloader a classloader to use when deserializing objects from the cache.
 *                          If you are using Play, you should pass in `app.classloader`.
 */
class RedisCache(jedisPool: JedisPool, override val customClassloader: Option[ClassLoader] = None)(implicit execContext: ExecutionContext = ExecutionContext.global)
    extends Cache
    with RedisSerialization
    with LoggingSupport
    with StrictLogging {

  import RedisCache.StringWithUtf8Bytes

  /**
   * Get the value corresponding to the given key from the cache
   * @param key cache key
   * @tparam V the type of the corresponding value
   * @return the value, if there is one
   */
  override def get[V](key: String) = Future {
    blocking {
      withJedisClient { client =>
        val resultBytes = Option(client.get(key.utf8bytes))
        val result = resultBytes.map(deserialize[V])
        logCacheHitOrMiss(key, result)
        result
      }
    }
  }

  /**
   * Insert the given key-value pair into the cache, with an optional Time To Live.
   * @param key cache key
   * @param value corresponding value
   * @param ttl Time To Live
   * @tparam V the type of the corresponding value
   */
  override def put[V](key: String, value: V, ttl: Option[Duration]) = Future {
    blocking {
      withJedisClient { client =>
        val keyBytes = key.utf8bytes
        val valueBytes = serialize(value)
        ttl match {
          case None => client.set(keyBytes, valueBytes)
          case Some(Duration.Zero) => client.set(keyBytes, valueBytes)
          case Some(d) if d < 1.second => {
            logger.warn("Because Redis (pre 2.6.12) does not support sub-second expiry, TTL of $d will be rounded up to 1 second")
            client.setex(keyBytes, 1, valueBytes)
          }
          case Some(d) => client.setex(keyBytes, d.toSeconds.toInt, valueBytes)
        }
      }
    }
  }

  /**
   * Remove the given key and its associated value from the cache, if it exists.
   * If the key is not in the cache, do nothing.
   * @param key cache key
   */
  override def remove(key: String) = Future {
    blocking {
      withJedisClient { client =>
        client.del(key.utf8bytes)
      }
    }
  }

  private def withJedisClient[T](f: Jedis => T): T = {
    val jedis = jedisPool.getResource()
    try {
      f(jedis)
    } finally {
      jedis.close()
    }
  }

}

object RedisCache {

  /**
   * Create a Redis client connecting to the given host and use it for caching
   */
  def apply(host: String, port: Int): RedisCache = apply(new JedisPool(host, port))

  /**
   * Create a cache that uses the given Jedis client pool
   * @param jedisPool a Jedis pool
   * @param customClassloader a classloader to use when deserializing objects from the cache.
   *                          If you are using Play, you should pass in `app.classloader`.
   */
  def apply(jedisPool: JedisPool, customClassloader: Option[ClassLoader] = None): RedisCache =
    new RedisCache(jedisPool, customClassloader)

  private val utf8 = Charset.forName("UTF-8")

  /**
   * Enrichment class to convert String to UTF-8 byte array
   */
  private implicit class StringWithUtf8Bytes(val string: String) extends AnyVal {
    def utf8bytes = string.getBytes(utf8)
  }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy