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

scalacache.memcached.MemcachedCache.scala Maven / Gradle / Ivy

The newest version!
package scalacache.memcached

import net.spy.memcached.internal.{GetCompletionListener, GetFuture, OperationCompletionListener, OperationFuture}
import net.spy.memcached.ops.StatusCode
import net.spy.memcached.{AddrUtil, BinaryConnectionFactory, MemcachedClient}

import scalacache.logging.Logger
import scalacache.serialization.Codec
import scalacache.{AbstractCache, CacheConfig, Mode}
import scala.concurrent.duration.Duration
import scala.util.Success
import scala.language.higherKinds
import scala.util.control.NonFatal

class MemcachedException(message: String) extends Exception(message)

/**
  * Wrapper around spymemcached
  */
class MemcachedCache[V](
    val client: MemcachedClient,
    val keySanitizer: MemcachedKeySanitizer = ReplaceAndTruncateSanitizer()
)(implicit val config: CacheConfig, codec: Codec[V])
    extends AbstractCache[V]
    with MemcachedTTLConverter {

  override protected final val logger =
    Logger.getLogger(getClass.getName)

  override protected def doGet[F[_]](key: String)(implicit mode: Mode[F]): F[Option[V]] = {
    mode.M.async { cb =>
      val f = client.asyncGet(keySanitizer.toValidMemcachedKey(key))
      f.addListener(new GetCompletionListener {
        def onComplete(g: GetFuture[_]): Unit = {
          if (g.getStatus.isSuccess) {
            try {
              val bytes = g.get()
              val value = codec.decode(bytes.asInstanceOf[Array[Byte]]).right.map(Some(_))
              cb(value)
            } catch {
              case NonFatal(e) => cb(Left(e))
            }
          } else {
            g.getStatus.getStatusCode match {
              case StatusCode.ERR_NOT_FOUND => cb(Right(None))
              case _                        => cb(Left(new MemcachedException(g.getStatus.getMessage)))
            }

          }
        }
      })
    }
  }

  override protected def doPut[F[_]](key: String, value: V, ttl: Option[Duration])(implicit mode: Mode[F]): F[Any] = {
    mode.M.async { cb =>
      val valueToSend = codec.encode(value)
      val f           = client.set(keySanitizer.toValidMemcachedKey(key), toMemcachedExpiry(ttl), valueToSend)
      f.addListener(new OperationCompletionListener {
        def onComplete(g: OperationFuture[_]): Unit = {
          if (g.getStatus.isSuccess) {
            logCachePut(key, ttl)
            cb(Right(()))
          } else {
            cb(Left(new MemcachedException(g.getStatus.getMessage)))
          }
          Success(())
        }
      })
    }
  }

  override protected def doRemove[F[_]](key: String)(implicit mode: Mode[F]): F[Any] = {
    mode.M.async { cb =>
      val f = client.delete(key)
      f.addListener(new OperationCompletionListener {
        def onComplete(g: OperationFuture[_]): Unit = {
          if (g.getStatus.isSuccess)
            cb(Right(()))
          else
            cb(Left(new MemcachedException(g.getStatus.getMessage)))
        }
      })
    }
  }

  override protected def doRemoveAll[F[_]]()(implicit mode: Mode[F]): F[Any] = {
    mode.M.async { cb =>
      val f = client.flush()
      f.addListener(new OperationCompletionListener {
        def onComplete(g: OperationFuture[_]): Unit = {
          if (g.getStatus.isSuccess)
            cb(Right(()))
          else
            cb(Left(new MemcachedException(g.getStatus.getMessage)))
        }
      })
    }
  }

  override def close[F[_]]()(implicit mode: Mode[F]): F[Any] = mode.M.delay(client.shutdown())

}

object MemcachedCache {

  /**
    * Create a Memcached client connecting to localhost:11211 and use it for caching
    */
  def apply[V](implicit config: CacheConfig, codec: Codec[V]): MemcachedCache[V] =
    apply("localhost:11211")

  /**
    * Create a Memcached client connecting to the given host(s) and use it for caching
    *
    * @param addressString Address string, with addresses separated by spaces, e.g. "host1:11211 host2:22322"
    */
  def apply[V](addressString: String)(implicit config: CacheConfig, codec: Codec[V]): MemcachedCache[V] =
    apply(new MemcachedClient(new BinaryConnectionFactory(), AddrUtil.getAddresses(addressString)))

  /**
    * Create a cache that uses the given Memcached client
    *
    * @param client Memcached client
    */
  def apply[V](client: MemcachedClient)(implicit config: CacheConfig, codec: Codec[V]): MemcachedCache[V] =
    new MemcachedCache[V](client)

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy