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

com.twitter.finagle.redis.util.TestServer.scala Maven / Gradle / Ivy

There is a newer version: 21.2.0
Show newest version
package com.twitter.finagle.redis.util

import java.lang.ProcessBuilder
import java.net.InetSocketAddress
import java.io.{BufferedWriter, FileWriter, PrintWriter, File}
import com.twitter.finagle.Redis
import com.twitter.finagle.redis.Client
import com.twitter.util.RandomSocket
import scala.collection.JavaConverters._
import scala.util.Random

// Helper classes for spinning up a little redis cluster
object RedisCluster { self =>
  import collection.mutable.{Stack => MutableStack}
  val instanceStack = MutableStack[ExternalRedis]()

  def address: Option[InetSocketAddress] = instanceStack.head.address
  def address(i: Int) = instanceStack(i).address
  def addresses: Seq[Option[InetSocketAddress]] = instanceStack.map { i => i.address }

  def hostAddresses(from: Int = 0, until: Int = instanceStack.size): String = {
    require(instanceStack.length > 0)
    addresses.slice(from, until).map { address =>
      val addy = address.get
      "%s:%d".format("127.0.0.1", addy.getPort())
    }.sorted.mkString(",")
  }

  def start(count: Int = 1, mode: RedisMode = RedisMode.Standalone): Seq[ExternalRedis] = {
    (0 until count).map { i =>
      start(new ExternalRedis(mode))
    }
  }

  def start(instance: ExternalRedis): ExternalRedis = {
    instance.start()
    instanceStack.push(instance)
    instance
  }

  def stop(): ExternalRedis = {
    val instance = instanceStack.pop()
    instance.stop()
    instance
  }

  def stopAll() {
    instanceStack.foreach { i => i.stop() }
    instanceStack.clear
  }

  // Make sure the process is always killed eventually
  Runtime.getRuntime().addShutdownHook(new Thread {
    override def run() {
      self.instanceStack.foreach { instance => instance.stop() }
    }
  });
}

sealed trait RedisMode
object RedisMode {
  case object Standalone extends RedisMode
  case object Sentinel extends RedisMode
  case object Cluster extends RedisMode
}
class ExternalRedis(mode: RedisMode = RedisMode.Standalone) {
  private[this] val rand = new Random
  private[this] var process: Option[Process] = None
  private[this] val forbiddenPorts = 6300.until(7300)
  var address: Option[InetSocketAddress] = None

  private[this] def assertRedisBinaryPresent() {
    val p = new ProcessBuilder("redis-server", "--help").start()
    p.waitFor()
    val exitValue = p.exitValue()
    require(exitValue == 0 || exitValue == 1, "redis-server binary must be present.")
  }

  private[this] def findAddress() {
    var tries = 100
    while (address == None && tries >= 0) {
      address = Some(RandomSocket.nextAddress())
      if (forbiddenPorts.contains(address.get.getPort)) {
        address = None
        tries -= 1
        Thread.sleep(5)
      }
    }
    address.getOrElse { sys.error("Couldn't get an address for the external redis instance") }
  }

  protected def createConfigFile(port: Int): File = {
    val f = File.createTempFile("redis-"+rand.nextInt(1000), ".tmp")
    f.deleteOnExit()
    val out = new PrintWriter(new BufferedWriter(new FileWriter(f)))
    val conf = "port %s".format(port)
    out.write(conf)
    out.println()
    out.close()
    f
  }

  def start() {
    val port = address.get.getPort()
    val conf = createConfigFile(port).getAbsolutePath
    val cmd: Seq[String] = if (mode == RedisMode.Sentinel) {
      Seq("redis-server", conf, "--sentinel")
    } else {
      Seq("redis-server", conf)
    }
    val builder = new ProcessBuilder(cmd.asJava)
    process = Some(builder.start())
    Thread.sleep(200)
  }

  def stop() {
    process.foreach { p =>
      p.destroy()
      p.waitFor()
    }
  }

  def restart() {
    stop()
    start()
  }

  def newClient(): Client = Redis.newRichClient(s"127.0.0.1:${address.get.getPort}")

  def withClient[T](f: Client => T): T = {
    val client = newClient
    try f(client) finally client.close()
  }

  assertRedisBinaryPresent()
  findAddress()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy