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

com.twitter.finagle.memcached.protocol.text.Memcached.scala Maven / Gradle / Ivy

There is a newer version: 21.2.0
Show newest version
package com.twitter.finagle.memcached.protocol.text

import org.jboss.netty.buffer.ChannelBuffer
import org.jboss.netty.channel._

import scala.collection.immutable

import client.DecodingToResponse
import client.{Decoder => ClientDecoder}
import server.DecodingToCommand
import server.{Decoder => ServerDecoder}

import com.twitter.finagle._
import com.twitter.finagle.memcached.protocol._
import com.twitter.finagle.memcached.util.ChannelBufferUtils._
import com.twitter.finagle.stats.StatsReceiver
import com.twitter.finagle.tracing._
import com.twitter.io.Buf

object Memcached {
  def apply(): Memcached = new Memcached()
  def get() = apply()
}

object MemcachedClientPipelineFactory extends ChannelPipelineFactory {
  def getPipeline() = {
    val pipeline = Channels.pipeline()

    pipeline.addLast("decoder", new ClientDecoder)
    pipeline.addLast("decoding2response", new DecodingToResponse)

    pipeline.addLast("encoder", new Encoder)
    pipeline.addLast("command2encoding", new CommandToEncoding)
    pipeline
  }
}

object MemcachedServerPipelineFactory extends ChannelPipelineFactory {
  private val storageCommands = collection.Set[ChannelBuffer](
    "set", "add", "replace", "append", "prepend")

  def getPipeline() = {
    val pipeline = Channels.pipeline()

  //        pipeline.addLast("exceptionHandler", new ExceptionHandler)

    pipeline.addLast("decoder", new ServerDecoder(storageCommands))
    pipeline.addLast("decoding2command", new DecodingToCommand)

    pipeline.addLast("encoder", new Encoder)
    pipeline.addLast("response2encoding", new ResponseToEncoding)
    pipeline
  }
}
class Memcached extends CodecFactory[Command, Response] {

  def server = Function.const {
    new Codec[Command, Response] {
      def pipelineFactory = MemcachedServerPipelineFactory
    }
  }

  def client = Function.const {
    new Codec[Command, Response] {
      def pipelineFactory = MemcachedClientPipelineFactory

      // pass every request through a filter to create trace data
      override def prepareConnFactory(underlying: ServiceFactory[Command, Response], params: Stack.Params) =
        new MemcachedLoggingFilter(params[param.Stats].statsReceiver).andThen(underlying)

      override def newTraceInitializer = MemcachedTraceInitializer.Module
    }
  }

  override val protocolLibraryName: String = "memcached"
}

/**
 * Adds tracing information for each memcached request.
 * Including command name, when request was sent and when it was received.
 */
private class MemcachedTracingFilter extends SimpleFilter[Command, Response] {
  def apply(command: Command, service: Service[Command, Response]) = {
    Trace.recordServiceName("memcached")
    Trace.recordRpc(command.name)
    Trace.record(Annotation.ClientSend())

    val response = service(command)
    if (Trace.isActivelyTracing) {
      response onSuccess  {
        case Values(values) =>
          command match {
            case cmd: RetrievalCommand =>
              val keys: immutable.Set[String] = immutable.Set(cmd.keys map { case Buf.Utf8(s) => s }: _*)
              val hits = values.map {
                case value =>
                  val Buf.Utf8(keyStr) = value.key
                  Trace.recordBinary(keyStr, "Hit")
                  keyStr
              }
              val misses: immutable.Set[String] = keys -- hits
              misses foreach { k: String => Trace.recordBinary(k, "Miss") }
              case _ =>
          }
        case _  =>
      } ensure {
        Trace.record(Annotation.ClientRecv())
      }
    }
    response
  }
}

private class MemcachedLoggingFilter(stats: StatsReceiver)
  extends SimpleFilter[Command, Response] {

  private[this] val serviceName = "memcached"

  private[this] val error = stats.scope("error")
  private[this] val succ  = stats.scope("success")

  override def apply(command: Command, service: Service[Command, Response]) = {
    service(command) map { response =>
      response match {
        case NotFound()
          | Stored()
          | NotStored()
          | Exists()
          | Deleted()
          | NoOp()
          | Info(_, _)
          | InfoLines(_)
          | Values(_)
          | Number(_)      => succ.counter(command.name).incr()
        case Error(_)      => error.counter(command.name).incr()
        case _             => error.counter(command.name).incr()
      }
      response
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy