Async Redis client for MoonBit, built on `moonbitlang/async`.
Dependencies
moon add hackwaly/redisimport {
"hackwaly/redis"
}///|
pub async fn quick_start() -> Unit {
@async.with_task_group(group => {
let client = @redis.Client(
config=@redis.ClientConfig(host="127.0.0.1", port=6379),
)
let worker = group.spawn(() => client.work(), allow_failure=true)
defer worker.cancel()
assert_true(client.ping() == "PONG")
assert_true(client.set("moonbit:redis:hello", "world"))
assert_true(client.get("moonbit:redis:hello") is Some("world"))
})
}///|
pub fn example_config() -> @redis.ClientConfig {
@redis.ClientConfig(
host="127.0.0.1",
port=6379,
username="default",
password="secret",
database=0,
name="moonbit-app",
command_queue_max_length=1024,
)
}///|
pub async fn command_examples(client : @redis.Client) -> Unit {
assert_true(client.set("counter", "1"))
assert_true(client.incr("counter") == 2)
assert_true(client.mget(["counter", "missing"]) is [Some("2"), None])
assert_true(client.hset("user:1", "name", "MoonBit") == 1)
assert_true(client.hget("user:1", "name") is Some("MoonBit"))
}///|
pub async fn raw_command_example(client : @redis.Client) -> String {
client.execute(@redis.Command([b"PING"], value => value.as_string()))
}///|
pub async fn auto_pipeline_example(
client : @redis.Client,
) -> (String?, String?, Int) {
@async.with_task_group(group => {
let first = group.spawn(() => client.get("key:1"))
let second = group.spawn(() => client.get("key:2"))
let count = group.spawn(() => client.exists(["key:1", "key:2"]))
(first.wait(), second.wait(), count.wait())
})
}///|
pub async fn transaction_example(client : @redis.Client) -> Unit {
client.transaction(tx => {
tx.watch(["account:1"])
let multi = @redis.Multi()
let old_value = multi.get("account:1")
let wrote = multi.set("account:1", "updated")
guard tx.exec(multi) is Some(result) else {
fail("transaction was aborted")
}
ignore(result.get(old_value))
assert_true(result.get(wrote))
})
}///|
pub async fn pubsub_example() -> Unit {
@async.with_task_group(group => {
let publisher = @redis.Client(
config=@redis.ClientConfig(host="127.0.0.1", port=6379),
)
let publisher_worker = group.spawn(
() => publisher.work(),
allow_failure=true,
)
defer publisher_worker.cancel()
let subscriber = @redis.Client(
config=@redis.ClientConfig(host="127.0.0.1", port=6379),
)
let received = group.spawn(
() => {
subscriber.subscribe("events", msg => {
assert_true(msg.channel == "events")
assert_true(msg.payload.text() == "hello")
})
},
allow_failure=true,
)
ignore(publisher.publish("events", "hello"))
received.cancel()
})
}///|
pub async fn handle_error(client : @redis.Client) -> Unit {
try client.get("key") catch {
@redis.ServerError(message) => println("Redis error: \{message}")
_ => println("Client error")
} noraise {
_ => ()
}
}moon test./scripts/redis-integration-test.shpub suberror ClientError {
ServerError(String)
TransportError(Error)
HandshakeError(String)
UnexpectedResponse(String)
InvalidUtf8(Bytes)
IntegerOverflow(Int64)
ClientAlreadyWorking
}try client.get("key") catch {
@redis.ServerError(message) => println(message)
@redis.ClientAlreadyWorking => println("client is already running")
_ => println("redis client error")
} noraise {
_ => ()
}@async.with_task_group(group => {
let client = @redis.Client()
let worker = group.spawn(() => client.work(), allow_failure=true)
defer worker.cancel()
ignore(client.ping())
})let pong = client.execute(@redis.Command([b"PING"], value => value.as_string()))assert_true(client.get("missing") is None)assert_true(client.hget("user:1", "name") is Some("MoonBit"))let fields = client.hgetall("user:1")ignore(client.hset("user:1", "name", "MoonBit"))ignore(client.set("count", "1"))
assert_true(client.incr("count") == 2)ignore(client.lpush("jobs", ["a", "b"]))let values = client.lrange("jobs", 0, -1)let values = client.mget(["name", "missing"])
assert_true(values is [Some("MoonBit"), None])assert_true(client.ping() == "PONG")
assert_true(client.ping(message="hello") == "hello")async fn Client::psubscribe(self : Client, pattern : String, callback : async (PatternMessage) -> Unit) -> Unitgroup.spawn(() => {
client.psubscribe("events:*", msg => {
println(msg.channel)
})
}, allow_failure=true)let receivers = client.publish("events", "hello")ignore(client.sadd("tags", ["moonbit", "redis"]))assert_true(client.set("name", "MoonBit"))
assert_true(client.set("token", "abc", ex=60, nx=true))let members = client.smembers("tags")group.spawn(() => {
client.subscribe("events", msg => {
println(msg.payload.text())
})
}, allow_failure=true)client.transaction(tx => {
tx.watch(["account:1"])
let multi = @redis.Multi()
let wrote = multi.set("account:1", "updated")
guard tx.exec(multi) is Some(result) else {
fail("transaction aborted")
}
result.get(wrote)
})@async.with_task_group(group => {
let client = @redis.Client()
let worker = group.spawn(() => client.work(), allow_failure=true)
defer worker.cancel()
assert_true(client.set("hello", "world"))
})ignore(client.zadd("rank", [("alice", 10.0), ("bob", 8.0)]))let top = client.zrange("rank", 0, 9)pub struct ClientConfig {
name : String?
host : String
port : Int
username : String
password : String?
database : Int
read_buffer_size : Int
write_buffer_size : Int
resp_max_depth : Int
connect_timeout : Double
reconnect_strategy : RetryMethod
command_queue_max_length : Int?
}
fn ClientConfig::ClientConfig(name? : String, host? : String, port? : Int, username? : String, password? : String, database? : Int, read_buffer_size? : Int, write_buffer_size? : Int, resp_max_depth? : Int, connect_timeout? : Double, reconnect_strategy? : RetryMethod, command_queue_max_length? : Int) -> ClientConfiglet config = @redis.ClientConfig(host="127.0.0.1", port=6379, password="secret")
let client = @redis.Client(config~)let info = client.execute(
@redis.Command([b"INFO", b"server"], value => value.as_string()),
)msg.channel
msg.payload.text()let multi = @redis.Multi()
let old_value = multi.get("account:1")
let wrote = multi.set("account:1", "updated")fn Multi::hincrbyfloat(self : Multi, key : String, field : String, increment : Double) -> MultiToken[Double]fn Multi::hmget(self : Multi, key : String, fields : ArrayView[String]) -> MultiToken[Array[String?]]fn Multi::hmset(self : Multi, key : String, fields : ArrayView[(String, String)]) -> MultiToken[Unit]fn Multi::hscan(self : Multi, key : String, cursor : String, pattern? : String, count? : Int) -> MultiToken[(String, Map[String, String])]fn Multi::linsert_after(self : Multi, key : String, pivot : String, value : String) -> MultiToken[Int]fn Multi::linsert_before(self : Multi, key : String, pivot : String, value : String) -> MultiToken[Int]fn Multi::psetex(self : Multi, key : String, milliseconds : Int, value : String) -> MultiToken[Unit]fn[T] Multi::raw_command(self : Multi, args : ArrayView[BytesView], decode : (RawValue) -> T raise) -> MultiToken[T]let multi = @redis.Multi()
let size = multi.raw_command([b"DBSIZE"], value => value.as_int())fn Multi::scan(self : Multi, cursor : String, pattern? : String, count? : Int) -> MultiToken[(String, Array[String])]fn Multi::sdiffstore(self : Multi, destination : String, keys : ArrayView[String]) -> MultiToken[Int]fn Multi::set(self : Multi, key : String, value : String, ex? : Int, px? : Int, nx? : Bool, xx? : Bool) -> MultiToken[Bool]fn Multi::set_get(self : Multi, key : String, value : String, ex? : Int, px? : Int, nx? : Bool, xx? : Bool) -> MultiToken[String?]fn Multi::sinterstore(self : Multi, destination : String, keys : ArrayView[String]) -> MultiToken[Int]fn Multi::smismember(self : Multi, key : String, members : ArrayView[String]) -> MultiToken[Array[Bool]]fn Multi::smove(self : Multi, source : String, destination : String, value : String) -> MultiToken[Bool]fn Multi::sscan(self : Multi, key : String, cursor : String, pattern? : String, count? : Int) -> MultiToken[(String, HashSet[String])]fn Multi::sunionstore(self : Multi, destination : String, keys : ArrayView[String]) -> MultiToken[Int]fn Multi::zincrby(self : Multi, key : String, increment : Double, value : String) -> MultiToken[Double]fn Multi::zrange_with_scores(self : Multi, key : String, start : Int, stop : Int) -> MultiToken[Array[(String, Double)]]fn Multi::zrangebyscore(self : Multi, key : String, min : Double, max : Double, offset? : Int, count? : Int) -> MultiToken[Array[String]] raisefn Multi::zrangebyscore_with_scores(self : Multi, key : String, min : Double, max : Double, offset? : Int, count? : Int) -> MultiToken[Array[(String, Double)]] raisefn Multi::zremrangebyscore(self : Multi, key : String, min : Double, max : Double) -> MultiToken[Int]fn Multi::zrevrange(self : Multi, key : String, start : Int, stop : Int) -> MultiToken[Array[String]]fn Multi::zrevrange_with_scores(self : Multi, key : String, start : Int, stop : Int) -> MultiToken[Array[(String, Double)]]fn Multi::zscan(self : Multi, key : String, cursor : String, pattern? : String, count? : Int) -> MultiToken[(String, Array[(String, Double)])]type MultiResultlet value = result.get(old_value)type MultiToken[T]msg.pattern
msg.channel
msg.payload.text()type RawValue@redis.Command([b"DBSIZE"], value => value.as_int())@redis.Command([b"GET", b"blob"], value => value.as_bytes())@redis.Command([b"DBSIZE"], value => value.as_int())@redis.Command([b"GET", b"name"], value => {
value.as_option(item => item.as_string())
})@redis.Command([b"PING"], value => value.as_string())@redis.Command([b"HGETALL", b"user:1"], value => value.as_string_map())type Transactionasync fn Transaction::hincrby(self : Transaction, key : String, field : String, increment : Int) -> Intasync fn Transaction::hincrbyfloat(self : Transaction, key : String, field : String, increment : Double) -> Doubleasync fn Transaction::hmget(self : Transaction, key : String, fields : ArrayView[String]) -> Array[String?]async fn Transaction::hmset(self : Transaction, key : String, fields : ArrayView[(String, String)]) -> Unitasync fn Transaction::hscan(self : Transaction, key : String, cursor : String, pattern? : String, count? : Int) -> (String, Map[String, String])async fn Transaction::linsert_after(self : Transaction, key : String, pivot : String, value : String) -> Intasync fn Transaction::linsert_before(self : Transaction, key : String, pivot : String, value : String) -> Intasync fn Transaction::lrange(self : Transaction, key : String, start : Int, stop : Int) -> Array[String]async fn Transaction::psetex(self : Transaction, key : String, milliseconds : Int, value : String) -> Unitasync fn Transaction::scan(self : Transaction, cursor : String, pattern? : String, count? : Int) -> (String, Array[String])async fn Transaction::sdiffstore(self : Transaction, destination : String, keys : ArrayView[String]) -> Intasync fn Transaction::set(self : Transaction, key : String, value : String, ex? : Int, px? : Int, nx? : Bool, xx? : Bool) -> Boolasync fn Transaction::set_get(self : Transaction, key : String, value : String, ex? : Int, px? : Int, nx? : Bool, xx? : Bool) -> String?async fn Transaction::setex(self : Transaction, key : String, seconds : Int, value : String) -> Unitasync fn Transaction::setrange(self : Transaction, key : String, offset : Int, value : String) -> Intasync fn Transaction::sinterstore(self : Transaction, destination : String, keys : ArrayView[String]) -> Intasync fn Transaction::smismember(self : Transaction, key : String, members : ArrayView[String]) -> Array[Bool]async fn Transaction::smove(self : Transaction, source : String, destination : String, value : String) -> Boolasync fn Transaction::srandmember_count(self : Transaction, key : String, count : Int) -> Array[String]async fn Transaction::sscan(self : Transaction, key : String, cursor : String, pattern? : String, count? : Int) -> (String, HashSet[String])async fn Transaction::sunionstore(self : Transaction, destination : String, keys : ArrayView[String]) -> Intasync fn Transaction::zadd(self : Transaction, key : String, items : ArrayView[(String, Double)]) -> Intasync fn Transaction::zincrby(self : Transaction, key : String, increment : Double, value : String) -> Doubleasync fn Transaction::zpopmax(self : Transaction, key : String, count? : Int) -> Array[(String, Double)]async fn Transaction::zpopmin(self : Transaction, key : String, count? : Int) -> Array[(String, Double)]async fn Transaction::zrange(self : Transaction, key : String, start : Int, stop : Int) -> Array[String]async fn Transaction::zrange_with_scores(self : Transaction, key : String, start : Int, stop : Int) -> Array[(String, Double)]async fn Transaction::zrangebyscore(self : Transaction, key : String, min : Double, max : Double, offset? : Int, count? : Int) -> Array[String]async fn Transaction::zrangebyscore_with_scores(self : Transaction, key : String, min : Double, max : Double, offset? : Int, count? : Int) -> Array[(String, Double)]async fn Transaction::zremrangebyrank(self : Transaction, key : String, start : Int, stop : Int) -> Intasync fn Transaction::zremrangebyscore(self : Transaction, key : String, min : Double, max : Double) -> Intasync fn Transaction::zrevrange(self : Transaction, key : String, start : Int, stop : Int) -> Array[String]async fn Transaction::zrevrange_with_scores(self : Transaction, key : String, start : Int, stop : Int) -> Array[(String, Double)]async fn Transaction::zscan(self : Transaction, key : String, cursor : String, pattern? : String, count? : Int) -> (String, Array[(String, Double)])Install
Download zipAsync Redis client for MoonBit, built on `moonbitlang/async`.
Dependencies