![JAR search and dependency download from the Maven repository](/logo.png)
com.redis.cluster.RedisCluster.scala Maven / Gradle / Ivy
package com.redis.cluster
import java.util.zip.CRC32
import scala.collection.immutable.TreeSet
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
import com.redis._
import serialization._
/**
* Consistent hashing distributes keys across multiple servers. But there are situations
* like sorting or computing set intersections or operations like rpoplpush
* in redis that require all keys to be collocated on the same server.
*
* One of the techniques that redis encourages for such forced key locality is called
* key tagging. See for reference.
*
* The trait KeyTag defines a method tag that takes a key and returns
* the part of the key on which we hash to determine the server on which it will be located.
* If it returns None then we hash on the whole key, otherwise we hash only on the
* returned part.
*
* redis-rb implements a regex based trick to achieve key-tagging. Here is the technique
* explained in redis FAQ:
*
* A key tag is a special pattern inside a key that, if preset, is the only part of the key
* hashed in order to select the server for this key. For example in order to hash the key
* "foo" I simply perform the CRC32 checksum of the whole string, but if this key has a
* pattern in the form of the characters {...} I only hash this substring. So for example
* for the key "foo{bared}" the key hashing code will simply perform the CRC32 of "bared".
* This way using key tags you can ensure that related keys will be stored on the same Redis
* instance just using the same key tag for all this keys. Redis-rb already implements key tags.
*
*/
trait KeyTag {
def tag(key: Seq[Byte]): Option[Seq[Byte]]
}
import scala.util.matching.Regex
object RegexKeyTag extends KeyTag {
val tagStart = '{'.toByte
val tagEnd = '}'.toByte
def tag(key: Seq[Byte]) = {
val start = key.indexOf(tagStart) + 1
if (start > 0) {
val end = key.indexOf(tagEnd, start)
if (end > -1) Some(key.slice(start,end)) else None
} else None
}
}
object NoOpKeyTag extends KeyTag {
def tag(key: Seq[Byte]) = Some(key)
}
abstract class RedisCluster(hosts: String*) extends RedisClient {
// not needed at cluster level
// override val host = null
// override val port = 0
// abstract val
val keyTag: Option[KeyTag]
// default in libmemcached
val POINTS_PER_SERVER = 160 // default in libmemcached
// instantiating a cluster will automatically connect participating nodes to the server
val clients = hosts.toList.map {h =>
val hp = h.split(":")
new RedisClientPool(hp(0), hp(1).toInt)
}
// the hash ring will instantiate with the nodes up and added
val hr = HashRing[RedisClientPool](clients, POINTS_PER_SERVER)
// get node for the key
def nodeForKey(key: Any)(implicit format: Format) = {
val bKey = format(key)
hr.getNode(keyTag.flatMap(_.tag(bKey)).getOrElse(bKey)).withClient { client => client }
}
// add a server
def addServer(server: String) = {
val hp = server.split(":")
hr addNode new RedisClientPool(hp(0), hp(1).toInt)
}
/**
* Operations
*/
override def keys[A](pattern: Any = "*")(implicit format: Format, parse: Parse[A]) =
Some(hr.cluster.toList.map(_.withClient(_.keys[A](pattern))).flatten.flatten)
def onAllConns[T](body: RedisClient => T) =
hr.cluster.map(p => p.withClient { client => body(client) }) // .forall(_ == true)
override def flushdb = onAllConns(_.flushdb) forall(_ == true)
override def flushall = onAllConns(_.flushall) forall(_ == true)
override def quit = onAllConns(_.quit) forall(_ == true)
def close = hr.cluster.map(_.close)
override def rename(oldkey: Any, newkey: Any)(implicit format: Format): Boolean = nodeForKey(oldkey).rename(oldkey, newkey)
override def renamenx(oldkey: Any, newkey: Any)(implicit format: Format): Boolean = nodeForKey(oldkey).renamenx(oldkey, newkey)
override def dbsize: Option[Int] =
Some(onAllConns(_.dbsize).foldLeft(0)((a, b) => b.map(a+).getOrElse(a)))
override def exists(key: Any)(implicit format: Format): Boolean = nodeForKey(key).exists(key)
override def del(key: Any, keys: Any*)(implicit format: Format): Option[Int] =
Some((key :: keys.toList).groupBy(nodeForKey).foldLeft(0) { case (t,(n,ks)) => n.del(ks.head,ks.tail:_*).map(t+).getOrElse(t) })
override def getType(key: Any)(implicit format: Format) = nodeForKey(key).getType(key)
override def expire(key: Any, expiry: Int)(implicit format: Format) = nodeForKey(key).expire(key, expiry)
override def select(index: Int) = throw new UnsupportedOperationException("not supported on a cluster")
/**
* NodeOperations
*/
override def save = onAllConns(_.save) forall(_ == true)
override def bgsave = onAllConns(_.bgsave) forall(_ == true)
override def shutdown = onAllConns(_.shutdown) forall(_ == true)
override def bgrewriteaof = onAllConns(_.bgrewriteaof) forall(_ == true)
override def lastsave = throw new UnsupportedOperationException("not supported on a cluster")
override def monitor = throw new UnsupportedOperationException("not supported on a cluster")
override def info = throw new UnsupportedOperationException("not supported on a cluster")
override def slaveof(options: Any) = throw new UnsupportedOperationException("not supported on a cluster")
override def move(key: Any, db: Int)(implicit format: Format) = throw new UnsupportedOperationException("not supported on a cluster")
override def auth(secret: Any)(implicit format: Format) = throw new UnsupportedOperationException("not supported on a cluster")
/**
* StringOperations
*/
override def set(key: Any, value: Any)(implicit format: Format) = nodeForKey(key).set(key, value)
override def get[A](key: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).get(key)
override def getset[A](key: Any, value: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).getset(key, value)
override def setnx(key: Any, value: Any)(implicit format: Format) = nodeForKey(key).setnx(key, value)
override def incr(key: Any)(implicit format: Format) = nodeForKey(key).incr(key)
override def incrby(key: Any, increment: Int)(implicit format: Format) = nodeForKey(key).incrby(key, increment)
override def decr(key: Any)(implicit format: Format) = nodeForKey(key).decr(key)
override def decrby(key: Any, increment: Int)(implicit format: Format) = nodeForKey(key).decrby(key, increment)
override def mget[A](key: Any, keys: Any*)(implicit format: Format, parse: Parse[A]): Option[List[Option[A]]] = {
val keylist = (key :: keys.toList)
val kvs = for {
(n, ks) <- keylist.groupBy(nodeForKey)
vs <- n.mget[A](ks.head, ks.tail: _*).toList
kv <- (ks).zip(vs)
} yield kv
Some(keylist.map(kvs))
}
override def mset(kvs: (Any, Any)*)(implicit format: Format) = kvs.toList.map{ case (k, v) => set(k, v) }.forall(_ == true)
override def msetnx(kvs: (Any, Any)*)(implicit format: Format) = kvs.toList.map{ case (k, v) => setnx(k, v) }.forall(_ == true)
/**
* ListOperations
*/
override def lpush(key: Any, value: Any)(implicit format: Format) = nodeForKey(key).lpush(key, value)
override def rpush(key: Any, value: Any)(implicit format: Format) = nodeForKey(key).rpush(key, value)
override def llen(key: Any)(implicit format: Format) = nodeForKey(key).llen(key)
override def lrange[A](key: Any, start: Int, end: Int)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).lrange[A](key, start, end)
override def ltrim(key: Any, start: Int, end: Int)(implicit format: Format) = nodeForKey(key).ltrim(key, start, end)
override def lindex[A](key: Any, index: Int)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).lindex(key, index)
override def lset(key: Any, index: Int, value: Any)(implicit format: Format) = nodeForKey(key).lset(key, index, value)
override def lrem(key: Any, count: Int, value: Any)(implicit format: Format) = nodeForKey(key).lrem(key, count, value)
override def lpop[A](key: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).lpop[A](key)
override def rpop[A](key: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).rpop[A](key)
override def rpoplpush[A](srcKey: Any, dstKey: Any)(implicit format: Format, parse: Parse[A]) =
inSameNode(srcKey, dstKey) {n => n.rpoplpush[A](srcKey, dstKey)}
private def inSameNode[T](keys: Any*)(body: RedisClient => T)(implicit format: Format): T = {
val nodes = keys.toList.map(nodeForKey(_))
nodes.forall(_ == nodes.head) match {
case true => body(nodes.head) // all nodes equal
case _ =>
throw new UnsupportedOperationException("can only occur if both keys map to same node")
}
}
/**
* SetOperations
*/
override def sadd(key: Any, value: Any)(implicit format: Format) = nodeForKey(key).sadd(key, value)
override def srem(key: Any, value: Any)(implicit format: Format) = nodeForKey(key).srem(key, value)
override def spop[A](key: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).spop[A](key)
override def smove(sourceKey: Any, destKey: Any, value: Any)(implicit format: Format) =
inSameNode(sourceKey, destKey) {n => n.smove(sourceKey, destKey, value)}
override def scard(key: Any)(implicit format: Format) = nodeForKey(key).scard(key)
override def sismember(key: Any, value: Any)(implicit format: Format) = nodeForKey(key).sismember(key, value)
override def sinter[A](key: Any, keys: Any*)(implicit format: Format, parse: Parse[A]) =
inSameNode((key :: keys.toList): _*) {n => n.sinter[A](key, keys: _*)}
override def sinterstore(key: Any, keys: Any*)(implicit format: Format) =
inSameNode((key :: keys.toList): _*) {n => n.sinterstore(key, keys: _*)}
override def sunion[A](key: Any, keys: Any*)(implicit format: Format, parse: Parse[A]) =
inSameNode((key :: keys.toList): _*) {n => n.sunion[A](key, keys: _*)}
override def sunionstore(key: Any, keys: Any*)(implicit format: Format) =
inSameNode((key :: keys.toList): _*) {n => n.sunionstore(key, keys: _*)}
override def sdiff[A](key: Any, keys: Any*)(implicit format: Format, parse: Parse[A]) =
inSameNode((key :: keys.toList): _*) {n => n.sdiff[A](key, keys: _*)}
override def sdiffstore(key: Any, keys: Any*)(implicit format: Format) =
inSameNode((key :: keys.toList): _*) {n => n.sdiffstore(key, keys: _*)}
override def smembers[A](key: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).smembers(key)
override def srandmember[A](key: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).srandmember(key)
import Commands._
import RedisClient._
/**
* SortedSetOperations
*/
override def zadd(key: Any, score: Double, member: Any)(implicit format: Format) = nodeForKey(key).zadd(key, score, member)
override def zrem(key: Any, member: Any)(implicit format: Format) = nodeForKey(key).zrem(key, member)
override def zincrby(key: Any, incr: Double, member: Any)(implicit format: Format) = nodeForKey(key).zincrby(key, incr, member)
override def zcard(key: Any)(implicit format: Format) = nodeForKey(key).zcard(key)
override def zscore(key: Any, element: Any)(implicit format: Format) = nodeForKey(key).zscore(key, element)
override def zrange[A](key: Any, start: Int = 0, end: Int = -1, sortAs: SortOrder )(implicit format: Format, parse: Parse[A]) =
nodeForKey(key).zrange[A](key, start, end, sortAs)
override def zrangeWithScore[A](key: Any, start: Int = 0, end: Int = -1, sortAs: SortOrder = ASC)(implicit format: Format, parse: Parse[A]) =
nodeForKey(key).zrangeWithScore[A](key, start, end, sortAs)
override def zrangebyscore[A](key: Any, min: Double = Double.NegativeInfinity, minInclusive: Boolean = true, max: Double = Double.PositiveInfinity, maxInclusive: Boolean = true, limit: Option[(Int, Int)])(implicit format: Format, parse: Parse[A]) =
nodeForKey(key).zrangebyscore[A](key, min, minInclusive, max, maxInclusive, limit)
/**
* HashOperations
*/
override def hset(key: Any, field: Any, value: Any)(implicit format: Format) = nodeForKey(key).hset(key, field, value)
override def hget[A](key: Any, field: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).hget[A](key, field)
override def hmset(key: Any, map: Iterable[Product2[Any, Any]])(implicit format: Format) = nodeForKey(key).hmset(key, map)
override def hmget[K,V](key: Any, fields: K*)(implicit format: Format, parseV: Parse[V]) = nodeForKey(key).hmget[K,V](key, fields:_*)
override def hincrby(key: Any, field: Any, value: Int)(implicit format: Format) = nodeForKey(key).hincrby(key, field, value)
override def hexists(key: Any, field: Any)(implicit format: Format) = nodeForKey(key).hexists(key, field)
override def hdel(key: Any, field: Any)(implicit format: Format) = nodeForKey(key).hdel(key, field)
override def hlen(key: Any)(implicit format: Format) = nodeForKey(key).hlen(key)
override def hkeys[A](key: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).hkeys[A](key)
override def hvals[A](key: Any)(implicit format: Format, parse: Parse[A]) = nodeForKey(key).hvals[A](key)
override def hgetall[K,V](key: Any)(implicit format: Format, parseK: Parse[K], parseV: Parse[V]) = nodeForKey(key).hgetall[K,V](key)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy