Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/etaty/rediscala
Non-blocking, Reactive Redis driver for Scala (with Sentinel support)
https://github.com/etaty/rediscala
Last synced: 9 days ago
JSON representation
Non-blocking, Reactive Redis driver for Scala (with Sentinel support)
- Host: GitHub
- URL: https://github.com/etaty/rediscala
- Owner: etaty
- License: apache-2.0
- Created: 2013-04-15T13:49:16.000Z (over 11 years ago)
- Default Branch: master
- Last Pushed: 2024-05-07T19:31:37.000Z (6 months ago)
- Last Synced: 2024-08-01T17:28:15.258Z (3 months ago)
- Language: Scala
- Homepage:
- Size: 4.51 MB
- Stars: 789
- Watchers: 40
- Forks: 144
- Open Issues: 86
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
- awesome-scala - rediscala ★ 642 ⧗ 0 - Non-blocking, Reactive Redis driver for Scala (with Sentinel support) (Database)
- awesome-scala - **rediscala** - blocking, Reactive Redis driver for Scala (with Sentinel support) | ![GitHub stars](https://img.shields.io/github/stars/etaty/rediscala) ![GitHub commit activity](https://img.shields.io/github/commit-activity/y/etaty/rediscala) (Table of Contents / Database)
README
rediscala [![Build Status](https://travis-ci.org/etaty/rediscala.svg)](https://travis-ci.org/etaty/rediscala) [![Coverage Status](https://img.shields.io/coveralls/etaty/rediscala.svg)](https://coveralls.io/r/etaty/rediscala?branch=master) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.etaty/rediscala_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.etaty/rediscala_2.12)
=========A [Redis](http://redis.io/) client for Scala with non-blocking and asynchronous I/O operations.
* Reactive : Redis requests/replies are wrapped in Futures.
* Typesafe : Redis types are mapped to Scala types.
* Fast : Rediscala uses redis pipelining. Blocking redis commands are moved into their own connection.
A worker actor handles I/O operations (I/O bounds), another handles decoding of Redis replies (CPU bounds).### Set up your project dependencies
If you use SBT, you just have to edit `build.sbt` and add the following:
From version 1.9.0:
* use akka 2.5.23 (java 1.8)
* released for scala
* 2.11
* 2.12
* 2.13
```scala
libraryDependencies += "com.github.etaty" %% "rediscala" % "1.9.0"
```From version 1.8.0:
* use akka 2.4.12 (java 1.8)
* released for scala 2.11 & 2.12
```scala
libraryDependencies += "com.github.etaty" %% "rediscala" % "1.8.0"
```From version 1.3.1:
* use akka 2.3
* released for scala 2.10 & 2.11
```scala
// new repo on maven.org
libraryDependencies += "com.github.etaty" %% "rediscala" % "1.7.0"// old repo on bintray (1.5.0 and inferior version)
resolvers += "rediscala" at "http://dl.bintray.com/etaty/maven"
libraryDependencies += "com.etaty.rediscala" %% "rediscala" % "1.5.0"
```For older rediscala versions (<= 1.3):
* use akka 2.2
* released for scala 2.10 only
* use github "repo"
```scala
resolvers += "rediscala" at "https://raw.github.com/etaty/rediscala-mvn/master/releases/"libraryDependencies += "com.etaty.rediscala" %% "rediscala" % "1.3"
```### Connect to the database
```scala
import redis.RedisClient
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.globalobject Main extends App {
implicit val akkaSystem = akka.actor.ActorSystem()val redis = RedisClient()
val futurePong = redis.ping()
println("Ping sent!")
futurePong.map(pong => {
println(s"Redis replied with a $pong")
})
Await.result(futurePong, 5 seconds)akkaSystem.shutdown()
}
```### Basic Example
https://github.com/etaty/rediscala-demo
You can fork with : `git clone [email protected]:etaty/rediscala-demo.git` then run it, with `sbt run`
### Redis Commands
All commands are supported :
* [Keys](http://redis.io/commands#generic) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Keys))
* [Strings](http://redis.io/commands#string) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Strings))
* [Hashes](http://redis.io/commands#hash) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Hashes))
* [Lists](http://redis.io/commands#list)
* non-blocking ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Lists))
* blocking ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.BLists))
* [Sets](http://redis.io/commands#set) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Sets))
* [Sorted Sets](http://redis.io/commands#sorted_set) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.SortedSets))
* [Pub/Sub](http://redis.io/commands#pubsub) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Publish))
* [Transactions](http://redis.io/commands#transactions) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Transactions))
* [Connection](http://redis.io/commands#connection) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Connection))
* [Scripting](http://redis.io/commands#scripting) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Scripting))
* [Server](http://redis.io/commands#server) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.Server))
* [HyperLogLog](http://redis.io/commands#hyperloglog) ([scaladoc](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.HyperLogLog))### Blocking commands
[RedisBlockingClient](http://etaty.github.io/rediscala/latest/api/index.html#redis.RedisBlockingClient) is the instance allowing access to blocking commands :
* blpop
* brpop
* brpopplush```scala
redisBlocking.blpop(Seq("workList", "otherKeyWithWork"), 5 seconds).map(result => {
result.map({
case (key, work) => println(s"list $key has work : ${work.utf8String}")
})
})
```
Full example: [ExampleRediscalaBlocking](https://github.com/etaty/rediscala-demo/blob/master/src/main/scala/ExampleRediscalaBlocking.scala)You can fork with: `git clone [email protected]:etaty/rediscala-demo.git` then run it, with `sbt run`
### Transactions
The idea behind transactions in Rediscala is to start a transaction outside of a redis connection.
We use the [TransactionBuilder](http://etaty.github.io/rediscala/latest/api/index.html#redis.commands.TransactionBuilder) to store call to redis commands (and for each command we give back a future).
When `exec` is called, `TransactionBuilder` will build and send all the commands together to the server. Then the futures will be completed.
By doing that we can use a normal connection with pipelining, and avoiding to trap a command from outside, in the transaction...```scala
val redisTransaction = redis.transaction() // new TransactionBuilder
redisTransaction.watch("key")
val set = redisTransaction.set("key", "abcValue")
val decr = redisTransaction.decr("key")
val get = redisTransaction.get("key")
redisTransaction.exec()
```Full example: [ExampleTransaction](https://github.com/etaty/rediscala-demo/blob/master/src/main/scala/ExampleTransaction.scala)
You can fork with : `git clone [email protected]:etaty/rediscala-demo.git` then run it, with `sbt run`
[TransactionsSpec](https://github.com/etaty/rediscala/blob/master/src/test/scala/redis/commands/TransactionsSpec.scala) will reveal even more gems of the API.
### Pub/Sub
You can use a case class with callbacks [RedisPubSub](http://etaty.github.io/rediscala/latest/api/index.html#redis.RedisPubSub)
or extend the actor [RedisSubscriberActor](http://etaty.github.io/rediscala/latest/api/index.html#redis.actors.RedisSubscriberActor) as shown in the example below```scala
object ExamplePubSub extends App {
implicit val akkaSystem = akka.actor.ActorSystem()val redis = RedisClient()
// publish after 2 seconds every 2 or 5 seconds
akkaSystem.scheduler.schedule(2 seconds, 2 seconds)(redis.publish("time", System.currentTimeMillis()))
akkaSystem.scheduler.schedule(2 seconds, 5 seconds)(redis.publish("pattern.match", "pattern value"))
// shutdown Akka in 20 seconds
akkaSystem.scheduler.scheduleOnce(20 seconds)(akkaSystem.shutdown())val channels = Seq("time")
val patterns = Seq("pattern.*")
// create SubscribeActor instance
akkaSystem.actorOf(Props(classOf[SubscribeActor], channels, patterns).withDispatcher("rediscala.rediscala-client-worker-dispatcher"))}
class SubscribeActor(channels: Seq[String] = Nil, patterns: Seq[String] = Nil) extends RedisSubscriberActor(channels, patterns) {
override val address: InetSocketAddress = new InetSocketAddress("localhost", 6379)def onMessage(message: Message) {
println(s"message received: $message")
}def onPMessage(pmessage: PMessage) {
println(s"pattern message received: $pmessage")
}
}
```Full example: [ExamplePubSub](https://github.com/etaty/rediscala-demo/blob/master/src/main/scala/ExamplePubSub.scala)
You can fork with : `git clone [email protected]:etaty/rediscala-demo.git` then run it, with `sbt run`
[RedisPubSubSpec](https://github.com/etaty/rediscala/blob/master/src/test/scala/redis/RedisPubSubSpec.scala) will reveal even more gems of the API.
### Scripting
`RedisScript` is a helper, you can put your LUA script inside and it will compute the hash.
You can use it with `evalshaOrEval` which run your script even if it wasn't already loaded.```scala
val redis = RedisClient()val redisScript = RedisScript("return 'rediscala'")
val r = redis.evalshaOrEval(redisScript).map({
case b: Bulk => println(b.toString())
})
Await.result(r, 5 seconds)
```Full example: [ExampleScripting](https://github.com/etaty/rediscala-demo/blob/master/src/main/scala/ExampleScripting.scala)
### Redis Sentinel
[SentinelClient](http://etaty.github.io/rediscala/latest/api/index.html#redis.SentinelClient) connect to a redis sentinel server.
[SentinelMonitoredRedisClient](http://etaty.github.io/rediscala/latest/api/index.html#redis.SentinelMonitoredRedisClient) connect to a sentinel server to find the master addresse then start a connection. In case the master change your RedisClient connection will automatically connect to the new master server.
If you are using a blocking client, you can use [SentinelMonitoredRedisBlockingClient](http://etaty.github.io/rediscala/latest/api/index.html#redis.SentinelMonitoredRedisBlockingClient)### Pool
[RedisClientPool](http://etaty.github.io/rediscala/latest/api/index.html#redis.RedisClientPool) connect to a pool of redis servers.
Redis commands are dispatched to redis connection in a round robin way.### Master Slave
[RedisClientMasterSlaves](http://etaty.github.io/rediscala/latest/api/index.html#redis.RedisClientMasterSlaves) connect to a master and a pool of slaves.
The `write` commands are sent to the master, while the read commands are sent to the slaves in the [RedisClientPool](http://etaty.github.io/rediscala/latest/api/index.html#redis.RedisClientPool)### Config Which Dispatcher to Use
By default, the actors in this project will use the dispatcher `rediscala.rediscala-client-worker-dispatcher`. If you want to use another dispatcher, just config the implicit value of `redisDispatcher`:
```scala
implicit val redisDispatcher = RedisDispatcher("akka.actor.default-dispatcher")
```### ByteStringSerializer ByteStringDeserializer ByteStringFormatter
[ByteStringSerializer](http://etaty.github.io/rediscala/latest/api/index.html#redis.ByteStringSerializer)
[ByteStringDeserializer](http://etaty.github.io/rediscala/latest/api/index.html#redis.ByteStringDeserializer)
[ByteStringFormatter](http://etaty.github.io/rediscala/latest/api/index.html#redis.ByteStringFormatter)
```scala
case class DumbClass(s1: String, s2: String)object DumbClass {
implicit val byteStringFormatter = new ByteStringFormatter[DumbClass] {
def serialize(data: DumbClass): ByteString = {
//...
}def deserialize(bs: ByteString): DumbClass = {
//...
}
}
}
//...val dumb = DumbClass("s1", "s2")
val r = for {
set <- redis.set("dumbKey", dumb)
getDumbOpt <- redis.get[DumbClass]("dumbKey")
} yield {
getDumbOpt.map(getDumb => {
assert(getDumb == dumb)
println(getDumb)
})
}
```Full example: [ExampleByteStringFormatter](https://github.com/etaty/rediscala-demo/blob/master/src/main/scala/ExampleByteStringFormatter.scala)
### Scaladoc
[Rediscala scaladoc API (version 1.9)](https://oss.sonatype.org/service/local/repositories/releases/archive/com/github/etaty/rediscala_2.12/1.9.0/rediscala_2.12-1.9.0-javadoc.jar/!/redis/index.html)
[Rediscala scaladoc API (version 1.8)](https://oss.sonatype.org/service/local/repositories/releases/archive/com/github/etaty/rediscala_2.11/1.8.0/rediscala_2.11-1.8.0-javadoc.jar/!/index.html#package)
[Rediscala scaladoc API (version 1.7)](https://oss.sonatype.org/service/local/repositories/releases/archive/com/github/etaty/rediscala_2.11/1.7.0/rediscala_2.11-1.7.0-javadoc.jar/!/index.html#package)
[Rediscala scaladoc API (version 1.6)](http://etaty.github.io/rediscala/1.6.0/api/index.html#package)
[Rediscala scaladoc API (version 1.5)](http://etaty.github.io/rediscala/1.5.0/api/index.html#package)
[Rediscala scaladoc API (version 1.4)](http://etaty.github.io/rediscala/1.4.0/api/index.html#package)
[Rediscala scaladoc API (version 1.3)](http://etaty.github.io/rediscala/1.3/api/index.html#package)
[Rediscala scaladoc API (version 1.2)](http://etaty.github.io/rediscala/1.2/api/index.html#package)
[Rediscala scaladoc API (version 1.1)](http://etaty.github.io/rediscala/1.1/api/index.html#package)
[Rediscala scaladoc API (version 1.0)](http://etaty.github.io/rediscala/1.0/api/index.html#package)
### Performance
More than 250 000 requests/second
* [benchmark result from scalameter](http://bit.ly/rediscalabench-1-1)
* [sources directory](https://github.com/etaty/rediscala/tree/master/src/benchmark/scala/redis/bench)The hardware used is a macbook retina (Intel Core i7, 2.6 GHz, 4 cores, 8 threads, 8GB) running the sun/oracle jvm 1.6
You can run the bench with :
1. clone the repo `git clone [email protected]:etaty/rediscala.git`
2. run `sbt bench:test`
3. open the bench report `rediscala/tmp/report/index.html`