Skip to main content

Crate rustis

Crate rustis 

Source
Expand description

rustis is a Redis client for Rust.

§Philosophy

  • Low allocations
  • Full async library
  • Lock free implementation
  • Rust idiomatic API

§Features

§Optional Features

FeatureDescription
tokio-runtimeTokio runtime (default)
tokio-rustlsTokio Rustls TLS support
tokio-native-tlsTokio native_tls TLS support
jsonEnables JSON (de)serialization support via serde_json
client-cacheEnables client-side caching support
poolPooled client manager

tokio-rustls and tokio-native-tls are mutually exclusive: enabling both is a compile error. Each implies the corresponding backend-only feature (rustls, native-tls), which gates the TLS configuration types. Enabling a backend-only feature on its own is also a compile error: it brings the configuration types without the connection code that honours them.

The remaining features are for developing rustis itself and carry no stability guarantee: bench (exposes internal RESP entry points to the benchmarks and pulls in criterion, fred, redis and pprof), fuzzing (same, for the cargo-fuzz targets in fuzz/) and web-examples (axum / actix-web, for the examples).

§Protocol Compatibility

Rustis uses the RESP3 protocol exclusively.

The HELLO 3 command is automatically sent when establishing a connection. Therefore, your Redis server must support RESP3 (Redis ≥6.0+ with RESP3 enabled).

If you use Redis 5 or older, or your Redis 6+ server still defaults to RESP2, Rustis will not work.

To verify your server supports RESP3:

redis-cli --raw HELLO 3

If you see server info (role, version, etc.), you’re good to go. If you get an error, upgrade Redis.

§Basic Usage

use rustis::{
    client::Client,
    commands::{FlushingMode, ServerCommands, StringCommands},
    Result,
};

#[tokio::main]
async fn main() -> Result<()> {
    // Connect the client to a Redis server from its IP and port
    let client = Client::connect("127.0.0.1:6379").await?;

    // Flush all existing data in Redis
    client.flushdb(FlushingMode::Sync).await?;

    // sends the command SET to Redis. This command is defined in the StringCommands trait
    client.set("key", "value").await?;

    // sends the command GET to Redis. This command is defined in the StringCommands trait
    let value: String = client.get("key").await?;
    println!("value: {value:?}");

    Ok(())
}

§Client

See the module client to discover which are the 3 usages of the Client struct and how to configure it.

You will also learn how to use pipeline, pub/sub and transactions.

§RESP

RESP is the Redis Serialization Protocol.

See the module resp to discover how rustis allows programmers to communicate with Redis in a Rust idiomatic way.

You will learn how to:

  • Manipulate the rustis object model, the enum Value, which is a generic Rust data structure over RESP.
  • Convert Rust types into Rust Commands with the Command struct, whose arguments are any type implementing serde’s Serialize.
  • Convert Rust command responses into Rust type with serde and helpful marker traits.

§Commands

In order to send Commands to the Redis server, rustis offers two API levels:

  • High-level Built-in commands that implement all documented Redis commands up to and including Redis 8.8, plus the Redis Stack commands.
  • Low-level Generic command API to express any request that may not exist in rustis:
    • new official commands not yet implemented by rustis.
    • commands exposed by additional Redis modules not included in Redis Stack.

§Built-in commands

See the module commands to discover how Redis built-in commands are organized in different traits.

§Generic command API

To use the generic command API, you can use the cmd function to specify the name of the command, followed by one or multiple calls to CommandBuilder::arg to add arguments to the command, and to CommandBuilder::key to add arguments that are Redis keys.

This command can then be passed as a parameter to one of the following associated functions, depending on the client, transaction or pipeline struct used:

use rustis::{client::Client, resp::cmd, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let client = Client::connect("127.0.0.1:6379").await?;

    client
        .send::<()>(
            cmd("MSET")
                .key("{my}key1")
                .arg("value1")
                .key("{my}key2")
                .arg("value2")
                .key("{my}key3")
                .arg("value3")
                .key("{my}key4")
                .arg("value4"),
            None,
        )
        .await?;

    let values: Vec<String> = client
        .send(
            cmd("MGET")
                .key("{my}key1")
                .key("{my}key2")
                .key("{my}key3")
                .key("{my}key4"),
            None,
        )
        .await?;

    assert_eq!(vec!["value1", "value2", "value3", "value4"], values);

    Ok(())
}

§Warning: keys must be added with key, not arg

Only arguments added with key take part in Cluster slot computation. A command built with arg alone carries no slot and is sent to a random node of the cluster, with no error to tell you: a single-key command gets a MOVED reply, and the retry that follows the topology refresh picks a random node again. A multi-key command such as MSET fails with CROSSSLOT.

A multi-key command additionally requires all its keys to hash to the same slot, which is what the {my} hash tag guarantees in the example above.

This does not apply to the strongly typed command API (commands): those functions already mark their keys.

§Errors

Every fallible call returns Result<T>, whose error is Error. An Error is what went wrong, kind(), plus the command it belongs to, command():

use rustis::{Error, ErrorKind, Result};

fn report(result: Result<String>) {
    if let Err(e) = result {
        match e.kind() {
            ErrorKind::Timeout => eprintln!("{:?} timed out", e.command()),
            ErrorKind::Redis(redis_error) => eprintln!("the server refused it: {redis_error}"),
            _ => eprintln!("{e}"),
        }
    }
}

The command matters because a client multiplexes: a single connection carries hundreds of commands at once, so a bare “the operation timed out” names nothing the application can act on. It is set for every error the client raises on behalf of a command, and absent for the ones raised outside any — a connection timeout, for instance. Display appends it, so a logged error reads The I/O operation's timeout expired (while executing BLMPOP).

§Client-side caching

See the module cache to discover how you can implement client-side caching.

Re-exports§

pub use bb8;pool

Modules§

cacheclient-cache
Client-side caching support
client
Defines types related to the clients structs and their dependencies: Client, ExclusiveClient, PooledClientManager, Pipeline, Transaction and how to configure them
commands
Define Redis built-in commands in a set of traits
resp
Defines types related to the RESP protocol and their encoding/decoding

Structs§

Error
Any error raised by the client, and the command it belongs to.
ErrorContext
Identifies the command an Error belongs to.
RedisError
Error issued by the Redis server

Enums§

ClientError
Errors issued by the client
ErrorKind
What an Error is, independently of the command it belongs to.
RedisErrorKind
Redis server error kind

Type Aliases§

Future
Library general future type.
Result
Library general result type.