title: redis-rb guide (Ruby)

summary: Connect your Ruby application to a Redis database

title: Install

role: setup

text:
To install `redis-rb`, run the following command:

[code example]

title: Connect and test

text:
Connect to localhost on port 6379:

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Asynchronous), C# (Synchronous), Go, Java, Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Asynchronous):**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Ruby:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

Store and retrieve a simple string.

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Asynchronous), C# (Synchronous), Go, Java, Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Asynchronous):**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Ruby:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

Store and retrieve a dict.

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Asynchronous), C# (Synchronous), Go, Java, Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Asynchronous):**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Ruby:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

Close the connection when you're done.

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Asynchronous), C# (Synchronous), Go, Java, Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Asynchronous):**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Ruby:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

title: More information

text:
The
[GitHub repository](https://github.com/redis/redis-rb) for `redis-rb` has a
set of [examples](https://github.com/redis/redis-rb/tree/master/examples)
and further information about using redis-rb.

code: gem install redis

section_id: install

code:
#include <stdio.h>
#include <stdlib.h>

#include <hiredis/hiredis.h>

int main() {
    // The `redisContext` type represents the connection
    // to the Redis server. Here, we connect to the
    // default host and port.
    redisContext *c = redisConnect("127.0.0.1", 6379);

    // Check if the context is null or if a specific
    // error occurred.
    if (c == NULL || c->err) {
        if (c != NULL) {
            printf("Error: %s\n", c->errstr);
            // handle error
        } else {
            printf("Can't allocate redis context\n");
        }

        exit(1);
    }

    // Set a string key.
    redisReply *reply = redisCommand(c, "SET foo bar");
    printf("Reply: %s\n", reply->str); // >>> Reply: OK
    freeReplyObject(reply);

    // Get the key we have just stored.
    reply = redisCommand(c, "GET foo");
    printf("Reply: %s\n", reply->str); // >>> Reply: bar
    freeReplyObject(reply);

    // Close the connection.
    redisFree(c);
}

section_id: connect-and-test

code:
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)

code: r = Redis.new

code:
r.set('foo', 'bar')
# True
r.get('foo')
# bar

code:
r.set 'foo', 'bar'
value = r.get('foo')
puts value # >>> bar

code:
using NRedisStack;
using NRedisStack.RedisStackCommands;
using StackExchange.Redis;

public class AsyncLandingExample
{
    public async Task Run()
    {
        var muxer = await ConnectionMultiplexer.ConnectAsync("localhost:6379");
        var db = muxer.GetDatabase();

        await db.StringSetAsync("foo", "bar");
        string? fooResult = await db.StringGetAsync("foo");
        Console.WriteLine(fooResult); // >>> bar

        var hash = new HashEntry[] {
            new HashEntry("name", "John"),
            new HashEntry("surname", "Smith"),
            new HashEntry("company", "Redis"),
            new HashEntry("age", "29"),
            };
        await db.HashSetAsync("user-session:123", hash);

        var hashFields = await db.HashGetAllAsync("user-session:123");
        Console.WriteLine(String.Join("; ", hashFields));
        // >>> name: John; surname: Smith; company: Redis; age: 29
    }
}

code:
using NRedisStack;
using NRedisStack.RedisStackCommands;
using StackExchange.Redis;

public class SyncLandingExample
{
    public void Run()
    {
        var muxer = ConnectionMultiplexer.Connect("localhost:6379");
        var db = muxer.GetDatabase();

        db.StringSet("foo", "bar");
        Console.WriteLine(db.StringGet("foo")); // >>> bar

        var hash = new HashEntry[] {
            new HashEntry("name", "John"),
            new HashEntry("surname", "Smith"),
            new HashEntry("company", "Redis"),
            new HashEntry("age", "29"),
        };
        db.HashSet("user-session:123", hash);

        var hashFields = db.HashGetAll("user-session:123");
        Console.WriteLine(String.Join("; ", hashFields));
        // >>> name: John; surname: Smith; company: Redis; age: 29
    }
}

code:
package main

import (
	"context"
	"fmt"

	"github.com/redis/go-redis/v9"
)

func main() {
	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password
		DB:       0,  // use default DB
		Protocol: 2,
	})

	ctx := context.Background()

	err := rdb.Set(ctx, "foo", "bar", 0).Err()
	if err != nil {
		panic(err)
	}

	val, err := rdb.Get(ctx, "foo").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println("foo", val) // >>> foo bar

	hashFields := []string{
		"model", "Deimos",
		"brand", "Ergonom",
		"type", "Enduro bikes",
		"price", "4972",
	}

	res1, err := rdb.HSet(ctx, "bike:1", hashFields).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res1) // >>> 4

	res2, err := rdb.HGet(ctx, "bike:1", "model").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res2) // >>> Deimos

	res3, err := rdb.HGet(ctx, "bike:1", "price").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res3) // >>> 4972

	res4, err := rdb.HGetAll(ctx, "bike:1").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res4)
	// >>> map[brand:Ergonom model:Deimos price:4972 type:Enduro bikes]

	type BikeInfo struct {
		Model string `redis:"model"`
		Brand string `redis:"brand"`
		Type  string `redis:"type"`
		Price int    `redis:"price"`
	}

	var res4a BikeInfo
	err = rdb.HGetAll(ctx, "bike:1").Scan(&res4a)

	if err != nil {
		panic(err)
	}

	fmt.Printf("Model: %v, Brand: %v, Type: %v, Price: $%v\n",
		res4a.Model, res4a.Brand, res4a.Type, res4a.Price)
	// >>> Model: Deimos, Brand: Ergonom, Type: Enduro bikes, Price: $4972

	rdb.Close()
}

code:
import io.lettuce.core.*;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;

public class ConnectBasicTest {

    public void connectBasic() {
        RedisURI uri = RedisURI.Builder
                .redis("localhost", 6379)
                .build();

        RedisClient client = RedisClient.create(uri);
        StatefulRedisConnection<String, String> connection = client.connect();
        RedisCommands<String, String> commands = connection.sync();

        commands.set("foo", "bar");
        String result = commands.get("foo");
        System.out.println(result); // >>> bar

        connection.close();
        client.shutdown();
    }
}

code:
import redis.clients.jedis.RedisClient;
import java.util.HashMap;
import java.util.Map;

public class LandingExample {

    public void run() {
        RedisClient jedis = new RedisClient("redis://localhost:6379");

        String res1 = jedis.set("bike:1", "Deimos");
        System.out.println(res1); // >>> OK

        String res2 = jedis.get("bike:1");
        System.out.println(res2); // >>> Deimos

        Map<String, String> hash = new HashMap<>();
        hash.put("name", "John");
        hash.put("surname", "Smith");
        hash.put("company", "Redis");
        hash.put("age", "29");

        Long res3 = jedis.hset("user-session:123", hash);
        System.out.println(res3); // >>> 4

        Map<String, String> res4 = jedis.hgetAll("user-session:123");
        System.out.println(res4);
        // >>> {name=John, surname=Smith, company=Redis, age=29}

        jedis.close();
    }
}

code:
import { createClient } from 'redis';

const client = createClient();

client.on('error', err => console.log('Redis Client Error', err));

await client.connect();

await client.set('key', 'value');
const value = await client.get('key');
console.log(value); // >>> value

await client.hSet('user-session:123', {
    name: 'John',
    surname: 'Smith',
    company: 'Redis',
    age: 29
})

let userSession = await client.hGetAll('user-session:123');
console.log(JSON.stringify(userSession, null, 2));
/* >>>
{
  "surname": "Smith",
  "name": "John",
  "company": "Redis",
  "age": "29"
}
 */

await client.quit();

code:
import { Redis } from 'ioredis';

const redis = new Redis();

await redis.set('key', 'value');
const value = await redis.get('key');
console.log(value); // >>> value

await redis.hset('user-session:123', {
    name: 'John',
    surname: 'Smith',
    company: 'Redis',
    age: 29
});

const userSession = await redis.hgetall('user-session:123');
console.log(JSON.stringify(userSession, null, 2));
/* >>>
{
  "surname": "Smith",
  "name": "John",
  "company": "Redis",
  "age": "29"
}
 */

redis.disconnect();

code:
<?php

require 'vendor/autoload.php';

use Predis\Client as PredisClient;

$r = new PredisClient([
                'scheme'   => 'tcp',
                'host'     => '127.0.0.1',
                'port'     => 6379,
                'password' => '',
                'database' => 0,
            ]);

echo $r->set('foo', 'bar'), PHP_EOL;
// >>> OK

echo $r->get('foo'), PHP_EOL;
// >>> bar

$r->hset('user-session:123', 'name', 'John');
$r->hset('user-session:123', 'surname', 'Smith');
$r->hset('user-session:123', 'company', 'Redis');
$r->hset('user-session:123', 'age', 29);

echo var_export($r->hgetall('user-session:123')), PHP_EOL;
/* >>>
array (
  'name' => 'John',
  'surname' => 'Smith',
  'company' => 'Redis',
  'age' => '29',
)
*/

code:
r.hset('user-session:123', mapping={
    'name': 'John',
    "surname": 'Smith',
    "company": 'Redis',
    "age": 29
})
# True

r.hgetall('user-session:123')
# {'surname': 'Smith', 'name': 'John', 'company': 'Redis', 'age': '29'}

code:
r.hset 'user-session:123', 'name', 'John'
r.hset 'user-session:123', 'surname', 'Smith'
r.hset 'user-session:123', 'company', 'Redis'
r.hset 'user-session:123', 'age', 29

hash_value = r.hgetall('user-session:123')
puts hash_value
# >>> {"name"=>"John", "surname"=>"Smith", "company"=>"Redis", "age"=>"29"}

code:
mod tests {
    use redis::AsyncCommands;

    async fn run() {
        let mut r = match redis::Client::open("redis://127.0.0.1") {
            Ok(client) => {
                match client.get_multiplexed_async_connection().await {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                }
            },
            Err(e) => {
                println!("Failed to create Redis client: {e}");
                return;
            }
        };

        if let Ok(res) = r.set("foo", "bar").await {
            let res: String = res;
            println!("{res}"); // >>> OK
        } else {
            println!("Error setting foo");
        }

        match r.get("foo").await {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> bar
            },
            Err(e) => {
                println!("Error getting foo: {e}");
                return;
            }
        };

        let hash_fields = [
            ("model", "Deimos"),
            ("brand", "Ergonom"),
            ("type", "Enduro bikes"),
            ("price", "4972"),
        ];

        if let Ok(res) = r.hset_multiple("bike:1", &hash_fields).await {
            let res: String = res;
            println!("{res}"); // >>> OK
        } else {
            println!("Error setting bike:1");
        }

        match r.hget("bike:1", "model").await {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> Deimos
            },
            Err(e) => {
                println!("Error getting bike:1 model: {e}");
                return;
            }
        }

        match r.hget("bike:1", "price").await {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> 4972
            },
            Err(e) => {
                println!("Error getting bike:1 price: {e}");
                return;
            }
        }

        match r.hgetall("bike:1").await {
            Ok(res) => {
                let res: Vec<(String, String)> = res;

                for (key, value) in res {
                    println!("{key}: {value}");
                }
                // >>> model: Deimos
                // >>> brand: Ergonom
                // >>> type: Enduro bikes
                // >>> price: 4972
            },
            Err(e) => {
                println!("Error getting bike:1: {e}");
                return;
            }
        }
    }
}

code:
mod landing_tests {
    use redis::Commands;

    fn run() {
        let mut r = match redis::Client::open("redis://127.0.0.1") {
            Ok(client) => {
                match client.get_connection() {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                }
            },
            Err(e) => {
                println!("Failed to create Redis client: {e}");
                return;
            }
        };

        if let Ok(res) = r.set("foo", "bar") {
            let res: String = res;
            println!("{res}");    // >>> OK
        } else {
            println!("Error setting foo");
        }

        match r.get("foo") {
            Ok(res) => {
                let res: String = res;
                println!("{res}");   // >>> bar
            },
            Err(e) => {
                println!("Error getting foo: {e}");
                return;
            }
        };

        let hash_fields = [
            ("model", "Deimos"),
            ("brand", "Ergonom"),
            ("type", "Enduro bikes"),
            ("price", "4972"),
        ];

        if let Ok(res) = r.hset_multiple("bike:1", &hash_fields) {
            let res: String = res;
            println!("{res}"); // >>> OK
        } else {
            println!("Error setting bike:1");
        }

        match r.hget("bike:1", "model") {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> Deimos
            },
            Err(e) => {
                println!("Error getting bike:1 model: {e}");
                return;
            }
        }

        match r.hget("bike:1", "price") {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> 4972
            },
            Err(e) => {
                println!("Error getting bike:1 price: {e}");
                return;
            }
        }

        match r.hgetall("bike:1") {
            Ok(res) => {
                let res: Vec<(String, String)> = res;

                for (key, value) in res {
                    println!("{key}: {value}");
                }
                // >>> model: Deimos
                // >>> brand: Ergonom
                // >>> type: Enduro bikes
                // >>> price: 4972
            },
            Err(e) => {
                println!("Error getting bike:1: {e}");
                return;
            }
        }
    }
}

code:
connection.close();
        client.shutdown();

code: await client.quit();


title: Connect to Azure Managed Redis

summary: Learn how to authenticate to an Azure Managed Redis (AMR) database

page_type: content

title: Overview

role: overview

text:
The `entra-id` feature lets you authenticate your app to
[Azure Managed Redis (AMR)](https://azure.microsoft.com/en-us/products/managed-redis)
using [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/).
You can authenticate using a system-assigned or user-assigned
[managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview)
or a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals),
letting the `redis-rs` connection fetch and renew the authentication tokens for you automatically.

title: Install

text:
Add the `entra-id` feature to your `Cargo.toml` file:

[code example]

title: Create a `EntraIdCredentialsProvider` instance

role: content

text:
A `EntraIdCredentialsProvider` object obtains the authentication credentials you
need when you connect to Redis. See the sections below to learn how
to create the `EntraIdCredentialsProvider` instances for AMR
using the factory methods that the class provides.

title: `EntraIdCredentialsProvider` for a service principal

text:
Use the `new_client_secret()` factory method to create a
`EntraIdCredentialsProvider` that authenticates to AMR using a
service principal (see the
[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals) to learn more about service principals).

You will need the following details of your service principal to make the connection:

- Client ID
- Client secret
- Tenant ID

The example below shows how to import the required modules and call
`new_client_secret()`:

[code example]

For extra security, you can also supply a certificate:

[code example]

title: `EntraIdCredentialsProvider` for a managed identity

text:
`EntraIdCredentialsProvider` provides two factory methods that authenticate to AMR using a
managed identity:

- `new_system_assigned_managed_identity()` for system-assigned managed identities
- `new_user_assigned_managed_identity()` for user-assigned managed identities

See the
[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) to learn more about managed identities.

The example below shows how to import the required modules and call both methods for
Azure-hosted applications:

[code example]

You can also create a user-assigned managed identity with custom scopes and
identity specification:

[code example]

title: Advanced configuration

text:
The examples above use the default `RetryConfig` when starting the provider.
However, the `RetryConfig` class provides configuration methods that let you customise
the way the the provider retries token requests:

[code example]

`RetryConfig` provides the following configuration methods:

| Method | Description |
| --- | --- |
| `set_number_of_retries()` | The maximum number of times to retry a token request before aborting. |
| `set_min_delay()` | Minimum time to wait before retrying a token request after a failed attempt. This provides a mechanism to request throttling to prevent an excessive number of token requests. |
| `set_max_delay()` | Maximum time to wait before retrying a token request after a failed attempt. |
| `set_exponent_base()` | An `f64` value representing the fraction of a token's lifetime that should elapse before attempting to refresh it. For example, a value of 0.75 means that you want to refresh the token after 75% of its lifetime has passed. |

title: Connect

text:
When you have created your `EntraIdCredentialsProvider` instance, you are ready to
connect to AMR. Create a connection configuration with the credentials provider and
use it to create a connection, as shown in the example below.

[code example]

code:
[dependencies]
redis = { version = "1.0.4", features = ["entra-id"] }

code:
use redis::{EntraIdCredentialsProvider, RetryConfig, RedisResult};

fn example() -> RedisResult<()> {
    let mut provider = EntraIdCredentialsProvider::new_client_secret(
        "your-tenant-id".to_string(),
        "your-client-id".to_string(),
        "your-client-secret".to_string(),
    )?;

    provider.start(RetryConfig::default());
    Ok(())
}

code:
use redis::{
    ClientCertificate, EntraIdCredentialsProvider,
    RetryConfig, RedisResult
};
use std::fs;

fn example() -> RedisResult<()> {
    // Load certificate from file
    let certificate_base64 = fs::read_to_string(
            "path/to/base64_pkcs12_certificate"
        )
        .expect("Base64 PKCS12 certificate not found.")
        .trim()
        .to_string();

    // Create the credentials provider using service principal with
    // client certificate
    let mut provider = EntraIdCredentialsProvider::new_client_certificate(
        "your-tenant-id".to_string(),
        "your-client-id".to_string(),
        ClientCertificate {
            base64_pkcs12: certificate_base64, // Base64 encoded PKCS12 data
            password: None,
        },
    )?;
    provider.start(RetryConfig::default());
    Ok(())
}

code:
use redis::{EntraIdCredentialsProvider, RetryConfig, RedisResult};

fn example() -> RedisResult<()> {
    // System-assigned managed identity
    let mut provider = EntraIdCredentialsProvider::new_system_assigned_managed_identity()?;
    provider.start(RetryConfig::default());

    // User-assigned managed identity
    let mut provider = EntraIdCredentialsProvider::new_user_assigned_managed_identity()?;
    provider.start(RetryConfig::default());
    Ok(())
}

code:
use redis::{EntraIdCredentialsProvider, RetryConfig, RedisResult};
use azure_identity::{ManagedIdentityCredentialOptions, UserAssignedId};

fn example() -> RedisResult<()> {
    let mut provider = EntraIdCredentialsProvider::new_user_assigned_managed_identity_with_scopes(
        vec!["your-scope".to_string()],
        Some(ManagedIdentityCredentialOptions {
            // Specify the user-assigned identity using one of:
            user_assigned_id: Some(UserAssignedId::ClientId("your-client-id".to_string())),
            // or: user_assigned_id: Some(UserAssignedId::ObjectId("your-object-id".to_string())),
            // or: user_assigned_id: Some(UserAssignedId::ResourceId("your-resource-id".to_string())),
            ..Default::default()
        }),
    )?;

    provider.start(RetryConfig::default());
    Ok(())
}

code:
use redis::{EntraIdCredentialsProvider, RetryConfig, RedisResult};
use std::time::Duration;

fn example() -> RedisResult<()> {
    let mut provider = EntraIdCredentialsProvider::new_system_assigned_managed_identity()?;

    let retry_config = RetryConfig::default()
        .set_number_of_retries(3)
        .set_min_delay(Duration::from_millis(100))
        .set_max_delay(Duration::from_secs(30))
        .set_exponent_base(2.0);

    provider.start(retry_config);
    Ok(())
}

section_id: advanced-configuration

code:
use redis::{
    Client, EntraIdCredentialsProvider,
    RetryConfig, AsyncConnectionConfig
};

async fn example() -> redis::RedisResult<()> {
    // Create the credentials provider.
    let mut provider = EntraIdCredentialsProvider::new_system_assigned_managed_identity()?;
    provider.start(RetryConfig::default());

    // Create Redis client.
    let client = Client::open("redis://your-redis-instance.com:6380")?;

    // Create a connection configuration with the credentials provider.
    let config = AsyncConnectionConfig::new().set_credentials_provider(provider);

    // Get a multiplexed connection with the configuration.
    let mut r = client.get_multiplexed_async_connection_with_config(&config).await?;

    // Use the connection.
    r.set("foo", "bar").await?;

    let res: String = r.get("foo").await?;
    println!("foo={res}");

    Ok(())
}

section_id: connect


title: Error handling

summary: Learn how to handle errors when using redis-rs

title: Overview

text:
redis-rs uses **Result types** following Rust's idiomatic error handling pattern. Code examples in the documentation often omit error handling for brevity,
but it is essential in production code.
This page explains how error handling works in redis-rs and how to apply
some common error handling patterns. For an overview of error types and handling
strategies, see [Error handling]().

title: Error handling in Rust

role: errors

text:
In Rust, functions return errors using the `Result<T, E>` type. You can check for errors explicitly with code like the following:

[code example]

title: Error types

text:
redis-rs provides a `RedisError` type with various error kinds. Common error kinds include:

| Error Kind | When it occurs | Recoverable | Recommended action |
|---|---|---|---|
| `ErrorKind::Io` | Network or I/O error | ✅ | Retry with backoff or fall back to alternative |
| `ErrorKind::AuthenticationFailed` | Invalid credentials | ❌ | Fix credentials and fail |
| `ErrorKind::UnexpectedReturnType` | Type mismatch | ❌ | Fix the command or type conversion |
| `ErrorKind::Server` | Redis server error response | ⚠ | Check specific server error; some are retryable |
| `ErrorKind::Parse` | Failed to parse server response | ❌ | Report as a bug |

See [Categories of errors]()
for a more detailed discussion of these errors and their causes.

title: Applying error handling patterns

text:
The [Error handling]() overview
describes four main patterns. The sections below show how to implement them in
redis-rs:

title: Pattern 1: Fail fast

text:
Return the error immediately if it represents an unrecoverable situation (see
[Pattern 1: Fail fast]()
for a full description):

[code example]

title: Pattern 2: Graceful degradation

text:
Check for specific errors and fall back to an alternative (see
[Pattern 2: Graceful degradation]()
for a full description):

[code example]

title: Pattern 3: Retry with backoff

text:
Retry on temporary errors such as timeouts (see
[Pattern 3: Retry with backoff]()
for a full description):

[code example]

title: Pattern 4: Log and continue

text:
Log non-critical errors and continue (see
[Pattern 4: Log and continue]()
for a full description):

[code example]

title: Async error handling

text:
Error handling works the same way when you use async/await with redis-rs,
as shown in the example below:

[code example]

title: See also

role: related

text:
- [Error handling]()
- [redis-rs documentation](https://docs.rs/redis/latest/redis/)

code:
use redis::Commands;

let client = redis::Client::open("redis://127.0.0.1/")?;
let mut con = client.get_connection()?;

match con.get::<_, String>("key") {
    Ok(value) => println!("Value: {}", value),
    Err(e) => {
        // Handle the error
        eprintln!("Error: {}", e);
    }
}

section_id: error-handling-in-rust

code:
use redis::{Commands, RedisResult};

fn get_value(con: &mut redis::Connection, key: &str) -> RedisResult<String> {
    // Using ? operator to propagate errors immediately
    let value: String = con.get(key)?;
    Ok(value)
}

code:
use redis::{Commands, ErrorKind};

fn get_with_fallback(
    con: &mut redis::Connection,
    key: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    match con.get::<_, String>(key) {
        Ok(value) => Ok(value),
        Err(e) => {
            if e.kind() == ErrorKind::Io {
                // Network error, use fallback
                log::warn!("Cache unavailable, using database");
                database_get(key)
            } else {
                Err(Box::new(e))
            }
        }
    }
}

code:
use redis::{Commands, ErrorKind};
use std::thread;
use std::time::Duration;

fn get_with_retry(
    con: &mut redis::Connection,
    key: &str,
    max_attempts: u32,
) -> redis::RedisResult<String> {
    let mut delay = Duration::from_millis(100);

    for attempt in 0..max_attempts {
        match con.get::<_, String>(key) {
            Ok(value) => return Ok(value),
            Err(e) => {
                if attempt < max_attempts - 1 && e.kind() == ErrorKind::Io {
                    thread::sleep(delay);
                    delay *= 2; // Exponential backoff
                    continue;
                }
                return Err(e);
            }
        }
    }

    unreachable!()
}

code:
use redis::{Commands, ErrorKind};

fn cache_value(con: &mut redis::Connection, key: &str, value: &str) {
    if let Err(e) = con.set_ex::<_, _, _, ()>(key, value, 3600) {
        if e.kind() == ErrorKind::Io {
            log::warn!("Failed to cache {}, continuing without cache", key);
            // Application continues normally
        } else {
            log::error!("Unexpected error caching {}: {}", key, e);
        }
    }
}

code:
use redis::AsyncCommands;

async fn get_with_fallback_async(
    con: &mut redis::aio::MultiplexedConnection,
    key: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    match con.get::<_, String>(key).await {
        Ok(value) => Ok(value),
        Err(e) => {
            if e.kind() == redis::ErrorKind::Io {
                log::warn!("Cache unavailable");
                database_get_async(key).await
            } else {
                Err(Box::new(e))
            }
        }
    }
}

section_id: async-error-handling


title: redis-rs guide (Rust)

summary: Connect your Rust application to a Redis database

title: Connect to Azure Managed Redis

title: Error handling

summary: Learn how to use Redis pipelines and transactions

title: Pipelines and transactions

title: Install

text:
To use the synchronous API, add the `redis` crate as a dependency in your
`Cargo.toml` file:

[code example]

If you want to use the asynchronous API, you should also enable either
[`tokio`](https://tokio.rs/) or [`smol`](https://crates.io/crates/smol)
as your async platform:

[code example]

title: Connect

text:
Start by importing the `Commands` or `AsyncCommands` trait from the `redis` crate:

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Asynchronous), C# (Synchronous), Go, Java, Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Asynchronous):**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Ruby:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

The following example shows the simplest way to connect to a Redis server:

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Asynchronous), C# (Synchronous), Go, Java, Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Asynchronous):**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Ruby:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

After connecting, you can test the connection by  storing and retrieving
a simple [string]():

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Asynchronous), C# (Synchronous), Go, Java, Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Asynchronous):**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Ruby:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

You can also easily store and retrieve a [hash]():

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Asynchronous), C# (Synchronous), Go, Java, Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Asynchronous):**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Ruby:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

title: More information

text:
See the [`redis-rs`](https://docs.rs/redis/latest/redis/) documentation
and the [GitHub repository](https://github.com/redis-rs/redis-rs) for more
information and examples.

code:
[dependencies]
redis = "1.0.4"

code:
[dependencies]
# if you use tokio
tokio = { version = "1.32.0", features = ["full"] }
redis = { version = "1.0.4", features = ["tokio-comp"] }

# if you use smol
smol = "2.0.2"
redis = { version = "1.0.4", features = ["smol-comp"] }

code:
#include <stdio.h>
#include <stdlib.h>

#include <hiredis/hiredis.h>

int main() {
    // The `redisContext` type represents the connection
    // to the Redis server. Here, we connect to the
    // default host and port.
    redisContext *c = redisConnect("127.0.0.1", 6379);

    // Check if the context is null or if a specific
    // error occurred.
    if (c == NULL || c->err) {
        if (c != NULL) {
            printf("Error: %s\n", c->errstr);
            // handle error
        } else {
            printf("Can't allocate redis context\n");
        }

        exit(1);
    }

    // Set a string key.
    redisReply *reply = redisCommand(c, "SET foo bar");
    printf("Reply: %s\n", reply->str); // >>> Reply: OK
    freeReplyObject(reply);

    // Get the key we have just stored.
    reply = redisCommand(c, "GET foo");
    printf("Reply: %s\n", reply->str); // >>> Reply: bar
    freeReplyObject(reply);

    // Close the connection.
    redisFree(c);
}

code:
using NRedisStack;
using NRedisStack.RedisStackCommands;
using StackExchange.Redis;

code:
import (
	"context"
	"fmt"

	"github.com/redis/go-redis/v9"
)

code:
import redis.clients.jedis.RedisClient;
import java.util.HashMap;
import java.util.Map;

code:
import { createClient } from 'redis';

const client = createClient();

client.on('error', err => console.log('Redis Client Error', err));

await client.connect();

await client.set('key', 'value');
const value = await client.get('key');
console.log(value); // >>> value

await client.hSet('user-session:123', {
    name: 'John',
    surname: 'Smith',
    company: 'Redis',
    age: 29
})

let userSession = await client.hGetAll('user-session:123');
console.log(JSON.stringify(userSession, null, 2));
/* >>>
{
  "surname": "Smith",
  "name": "John",
  "company": "Redis",
  "age": "29"
}
 */

await client.quit();

code:
import { Redis } from 'ioredis';

const redis = new Redis();

await redis.set('key', 'value');
const value = await redis.get('key');
console.log(value); // >>> value

await redis.hset('user-session:123', {
    name: 'John',
    surname: 'Smith',
    company: 'Redis',
    age: 29
});

const userSession = await redis.hgetall('user-session:123');
console.log(JSON.stringify(userSession, null, 2));
/* >>>
{
  "surname": "Smith",
  "name": "John",
  "company": "Redis",
  "age": "29"
}
 */

redis.disconnect();

code:
<?php

require 'vendor/autoload.php';

use Predis\Client as PredisClient;

$r = new PredisClient([
                'scheme'   => 'tcp',
                'host'     => '127.0.0.1',
                'port'     => 6379,
                'password' => '',
                'database' => 0,
            ]);

echo $r->set('foo', 'bar'), PHP_EOL;
// >>> OK

echo $r->get('foo'), PHP_EOL;
// >>> bar

$r->hset('user-session:123', 'name', 'John');
$r->hset('user-session:123', 'surname', 'Smith');
$r->hset('user-session:123', 'company', 'Redis');
$r->hset('user-session:123', 'age', 29);

echo var_export($r->hgetall('user-session:123')), PHP_EOL;
/* >>>
array (
  'name' => 'John',
  'surname' => 'Smith',
  'company' => 'Redis',
  'age' => '29',
)
*/

code:
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)

r.set('foo', 'bar')
# True
r.get('foo')
# bar

r.hset('user-session:123', mapping={
    'name': 'John',
    "surname": 'Smith',
    "company": 'Redis',
    "age": 29
})
# True

r.hgetall('user-session:123')
# {'surname': 'Smith', 'name': 'John', 'company': 'Redis', 'age': '29'}

r.close()

code: require 'redis'

code: use redis::AsyncCommands;

code: use redis::Commands;

code:
var muxer = await ConnectionMultiplexer.ConnectAsync("localhost:6379");
        var db = muxer.GetDatabase();

code:
var muxer = ConnectionMultiplexer.Connect("localhost:6379");
        var db = muxer.GetDatabase();

code:
rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password
		DB:       0,  // use default DB
		Protocol: 2,
	})

	ctx := context.Background()

code: RedisClient jedis = new RedisClient("redis://localhost:6379");

code: r = Redis.new

code:
let mut r = match redis::Client::open("redis://127.0.0.1") {
            Ok(client) => {
                match client.get_multiplexed_async_connection().await {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                }
            },
            Err(e) => {
                println!("Failed to create Redis client: {e}");
                return;
            }
        };

code:
let mut r = match redis::Client::open("redis://127.0.0.1") {
            Ok(client) => {
                match client.get_connection() {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                }
            },
            Err(e) => {
                println!("Failed to create Redis client: {e}");
                return;
            }
        };

code:
await db.StringSetAsync("foo", "bar");
        string? fooResult = await db.StringGetAsync("foo");
        Console.WriteLine(fooResult); // >>> bar

code:
db.StringSet("foo", "bar");
        Console.WriteLine(db.StringGet("foo")); // >>> bar

code:
err := rdb.Set(ctx, "foo", "bar", 0).Err()
	if err != nil {
		panic(err)
	}

	val, err := rdb.Get(ctx, "foo").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println("foo", val) // >>> foo bar

code:
String res1 = jedis.set("bike:1", "Deimos");
        System.out.println(res1); // >>> OK

        String res2 = jedis.get("bike:1");
        System.out.println(res2); // >>> Deimos

code:
if let Ok(res) = r.set("foo", "bar").await {
            let res: String = res;
            println!("{res}"); // >>> OK
        } else {
            println!("Error setting foo");
        }

        match r.get("foo").await {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> bar
            },
            Err(e) => {
                println!("Error getting foo: {e}");
                return;
            }
        };

code:
if let Ok(res) = r.set("foo", "bar") {
            let res: String = res;
            println!("{res}");    // >>> OK
        } else {
            println!("Error setting foo");
        }

        match r.get("foo") {
            Ok(res) => {
                let res: String = res;
                println!("{res}");   // >>> bar
            },
            Err(e) => {
                println!("Error getting foo: {e}");
                return;
            }
        };

code:
var hash = new HashEntry[] {
            new HashEntry("name", "John"),
            new HashEntry("surname", "Smith"),
            new HashEntry("company", "Redis"),
            new HashEntry("age", "29"),
            };
        await db.HashSetAsync("user-session:123", hash);

        var hashFields = await db.HashGetAllAsync("user-session:123");
        Console.WriteLine(String.Join("; ", hashFields));
        // >>> name: John; surname: Smith; company: Redis; age: 29

code:
var hash = new HashEntry[] {
            new HashEntry("name", "John"),
            new HashEntry("surname", "Smith"),
            new HashEntry("company", "Redis"),
            new HashEntry("age", "29"),
        };
        db.HashSet("user-session:123", hash);

        var hashFields = db.HashGetAll("user-session:123");
        Console.WriteLine(String.Join("; ", hashFields));
        // >>> name: John; surname: Smith; company: Redis; age: 29

code:
hashFields := []string{
		"model", "Deimos",
		"brand", "Ergonom",
		"type", "Enduro bikes",
		"price", "4972",
	}

	res1, err := rdb.HSet(ctx, "bike:1", hashFields).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res1) // >>> 4

	res2, err := rdb.HGet(ctx, "bike:1", "model").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res2) // >>> Deimos

	res3, err := rdb.HGet(ctx, "bike:1", "price").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res3) // >>> 4972

	res4, err := rdb.HGetAll(ctx, "bike:1").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res4)
	// >>> map[brand:Ergonom model:Deimos price:4972 type:Enduro bikes]

code:
import io.lettuce.core.*;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;

public class ConnectBasicTest {

    public void connectBasic() {
        RedisURI uri = RedisURI.Builder
                .redis("localhost", 6379)
                .build();

        RedisClient client = RedisClient.create(uri);
        StatefulRedisConnection<String, String> connection = client.connect();
        RedisCommands<String, String> commands = connection.sync();

        commands.set("foo", "bar");
        String result = commands.get("foo");
        System.out.println(result); // >>> bar

        connection.close();
        client.shutdown();
    }
}

code:
Map<String, String> hash = new HashMap<>();
        hash.put("name", "John");
        hash.put("surname", "Smith");
        hash.put("company", "Redis");
        hash.put("age", "29");

        Long res3 = jedis.hset("user-session:123", hash);
        System.out.println(res3); // >>> 4

        Map<String, String> res4 = jedis.hgetAll("user-session:123");
        System.out.println(res4);
        // >>> {name=John, surname=Smith, company=Redis, age=29}

code:
require 'redis'

r = Redis.new

r.set 'foo', 'bar'
value = r.get('foo')
puts value # >>> bar

r.hset 'user-session:123', 'name', 'John'
r.hset 'user-session:123', 'surname', 'Smith'
r.hset 'user-session:123', 'company', 'Redis'
r.hset 'user-session:123', 'age', 29

hash_value = r.hgetall('user-session:123')
puts hash_value
# >>> {"name"=>"John", "surname"=>"Smith", "company"=>"Redis", "age"=>"29"}

r.close()

code:
let hash_fields = [
            ("model", "Deimos"),
            ("brand", "Ergonom"),
            ("type", "Enduro bikes"),
            ("price", "4972"),
        ];

        if let Ok(res) = r.hset_multiple("bike:1", &hash_fields).await {
            let res: String = res;
            println!("{res}"); // >>> OK
        } else {
            println!("Error setting bike:1");
        }

        match r.hget("bike:1", "model").await {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> Deimos
            },
            Err(e) => {
                println!("Error getting bike:1 model: {e}");
                return;
            }
        }

        match r.hget("bike:1", "price").await {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> 4972
            },
            Err(e) => {
                println!("Error getting bike:1 price: {e}");
                return;
            }
        }

        match r.hgetall("bike:1").await {
            Ok(res) => {
                let res: Vec<(String, String)> = res;

                for (key, value) in res {
                    println!("{key}: {value}");
                }
                // >>> model: Deimos
                // >>> brand: Ergonom
                // >>> type: Enduro bikes
                // >>> price: 4972
            },
            Err(e) => {
                println!("Error getting bike:1: {e}");
                return;
            }

code:
let hash_fields = [
            ("model", "Deimos"),
            ("brand", "Ergonom"),
            ("type", "Enduro bikes"),
            ("price", "4972"),
        ];

        if let Ok(res) = r.hset_multiple("bike:1", &hash_fields) {
            let res: String = res;
            println!("{res}"); // >>> OK
        } else {
            println!("Error setting bike:1");
        }

        match r.hget("bike:1", "model") {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> Deimos
            },
            Err(e) => {
                println!("Error getting bike:1 model: {e}");
                return;
            }
        }

        match r.hget("bike:1", "price") {
            Ok(res) => {
                let res: String = res;
                println!("{res}"); // >>> 4972
            },
            Err(e) => {
                println!("Error getting bike:1 price: {e}");
                return;
            }
        }

        match r.hgetall("bike:1") {
            Ok(res) => {
                let res: Vec<(String, String)> = res;

                for (key, value) in res {
                    println!("{key}: {value}");
                }
                // >>> model: Deimos
                // >>> brand: Ergonom
                // >>> type: Enduro bikes
                // >>> price: 4972
            },
            Err(e) => {
                println!("Error getting bike:1: {e}");
                return;
            }
        }


title: Pipelines and transactions

title: Execute a pipeline

text:
To execute commands in a pipeline, you first create a pipeline object
and then add commands to it using methods that resemble the standard
command methods (for example, `set()` and `get()`). The commands are
buffered in the pipeline and only execute when you call the `exec()`
method on the pipeline object. If you need the results from the
commands, use the `query()` method, which returns
the results from all the commands in order.

Note that the command methods for a pipeline always return the original
pipeline object, so you can "chain" several commands together, as the
example below shows:

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Synchronous - Jedis), Python, Rust (Synchronous)

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**Python:**

[code example]

**Rust (Synchronous):**

[code example]

title: Execute a transaction

text:
You can execute a simple transaction by adding the `atomic()` method to a pipeline.

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Synchronous - Jedis), Python, Rust (Synchronous)

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**Python:**

[code example]

**Rust (Synchronous):**

[code example]

title: Watch keys for changes

text:
Redis supports *optimistic locking* to avoid inconsistent updates
to different keys. The basic idea is to watch for changes to any
keys that you use in a transaction while you are processing the
updates. If the watched keys do change, you must restart the updates
with the latest data from the keys. See
[Transactions]()
for more information about optimistic locking.

The example below shows how to use the `transaction()` function to
automatically retry a transaction when watched keys are modified.
Pass the list of keys you want to watch and a closure representing the transaction.
The closure receives the original connection and a pipeline as parameters.
Use the connection to read the latest values from the watched keys,
but always use the pipeline to add all the commands that make up the watched transaction.
If the watched keys are modified during the transaction, the `transaction()` function
automatically retries the transaction until it succeeds.

#### Code Examples

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Synchronous - Jedis), Python, Rust (Synchronous)

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**Python:**

[code example]

**Rust (Synchronous):**

[code example]

code:
var pipeline = new Pipeline(db);

        for (int i = 0; i < 5; i++)
        {
            _ = pipeline.Db.StringSetAsync($"seat:{i}", $"#{i}");
        }
        pipeline.Execute();

        var resp1 = db.StringGet("seat:0");
        Console.WriteLine(resp1); // >>> #0

        var resp2 = db.StringGet("seat:3");
        Console.WriteLine(resp2); // >>> #3

        var resp3 = db.StringGet("seat:4");
        Console.WriteLine(resp2); // >>> #4

section_id: execute-a-pipeline

code:
pipe := rdb.Pipeline()

	for i := 0; i < 5; i++ {
		pipe.Set(ctx, fmt.Sprintf("seat:%v", i), fmt.Sprintf("#%v", i), 0)
	}

	cmds, err := pipe.Exec(ctx)

	if err != nil {
		panic(err)
	}

	for _, c := range cmds {
		fmt.Printf("%v;", c.(*redis.StatusCmd).Val())
	}

	fmt.Println("")
	// >>> OK;OK;OK;OK;OK;

	pipe = rdb.Pipeline()

	get0Result := pipe.Get(ctx, "seat:0")
	get3Result := pipe.Get(ctx, "seat:3")
	get4Result := pipe.Get(ctx, "seat:4")

	cmds, err = pipe.Exec(ctx)

	// The results are available only after the pipeline
	// has finished executing.
	fmt.Println(get0Result.Val()) // >>> #0
	fmt.Println(get3Result.Val()) // >>> #3
	fmt.Println(get4Result.Val()) // >>> #4

code:
// Make sure you close the pipeline after use to release resources
        // and return the connection to the pool.
        try (AbstractPipeline pipe = jedis.pipelined()) {

            for (int i = 0; i < 5; i++) {
                pipe.set(String.format("seat:%d", i), String.format("#%d", i));
            }

            pipe.sync();
        }

        try (AbstractPipeline pipe = jedis.pipelined()) {

            Response<String> resp0 = pipe.get("seat:0");
            Response<String> resp3 = pipe.get("seat:3");
            Response<String> resp4 = pipe.get("seat:4");

            pipe.sync();

            // Responses are available after the pipeline has executed.
            System.out.println(resp0.get()); // >>> #0
            System.out.println(resp3.get()); // >>> #3
            System.out.println(resp4.get()); // >>> #4

        }

code:
// Check the success of the pipeline without checking the results
        // individually.
        match redis::pipe()
            .set("seat:0", "#0")
            .set("seat:1", "#1")
            .set("seat:2", "#2")
            .set("seat:3", "#3")
            .set("seat:4", "#4")
            .exec(&mut r)
        {
            Ok(_) => {
                println!("Pipe executed successfully");
            },
            Err(e) => {
                println!("Error executing pipe: {e}");
                return;
            }
        };

        // Check the success of the pipeline and the results individually.
        let (seat_0, seat_1, seat_2, seat_3, seat_4) :
        (String, String, String, String, String) = match redis::pipe()
            .get("seat:0")
            .get("seat:1")
            .get("seat:2")
            .get("seat:3")
            .get("seat:4")
            .query(&mut r) {
                Ok(res) => res,
                Err(e) => {
                    println!("Error executing pipe: {e}");
                    return;
                }
            };

        println!("{seat_0}, {seat_1}, {seat_2}, {seat_3}, {seat_4}");
        // >>> #0, #1, #2, #3, #4

        // Use `ignore()` to ignore the result of specific commands.
        let (seat_5, seat_6, seat_7) :
            (String, String, String) = match redis::pipe()
            .set("seat:5", "#5").ignore()
            .set("seat:6", "#6").ignore()
            .set("seat:7", "#7").ignore()
            .get("seat:5")
            .get("seat:6")
            .get("seat:7")
            .query(&mut r) {
                Ok(res) => res,
                Err(e) => {
                    println!("Error executing pipe: {e}");
                    return;
                }
            };

        println!("{seat_5}, {seat_6}, {seat_7}");
        // >>> #5, #6, #7

code:
var trans = new Transaction(db);

        _ = trans.Db.StringIncrementAsync("counter:1", 1);
        _ = trans.Db.StringIncrementAsync("counter:2", 2);
        _ = trans.Db.StringIncrementAsync("counter:3", 3);

        trans.Execute();

        var resp4 = db.StringGet("counter:1");
        Console.WriteLine(resp4); // >>> 1

        var resp5 = db.StringGet("counter:2");
        Console.WriteLine(resp5); // >>> 2

        var resp6 = db.StringGet("counter:3");
        Console.WriteLine(resp6);  // >>> 3

section_id: execute-a-transaction

code:
trans := rdb.TxPipeline()

	trans.IncrBy(ctx, "counter:1", 1)
	trans.IncrBy(ctx, "counter:2", 2)
	trans.IncrBy(ctx, "counter:3", 3)

	cmds, err = trans.Exec(ctx)

	for _, c := range cmds {
		fmt.Println(c.(*redis.IntCmd).Val())
	}
	// >>> 1
	// >>> 2
	// >>> 3

code:
try ( AbstractTransaction trans = jedis.multi()) {

           trans.incrBy("counter:1", 1);
           trans.incrBy("counter:2", 2);
           trans.incrBy("counter:3", 3);

           trans.exec();
        }
        System.out.println(jedis.get("counter:1")); // >>> 1
        System.out.println(jedis.get("counter:2")); // >>> 2
        System.out.println(jedis.get("counter:3")); // >>> 3

code:
"""
Code samples for vector database quickstart pages:
    https://redis.io/docs/latest/develop/get-started/vector-database/
"""
import redis

r = redis.Redis(decode_responses=True)

pipe = r.pipeline()

for i in range(5):
    pipe.set(f"seat:{i}", f"#{i}")

set_5_result = pipe.execute()
print(set_5_result)  # >>> [True, True, True, True, True]

pipe = r.pipeline()

# "Chain" pipeline commands together.
get_3_result = pipe.get("seat:0").get("seat:3").get("seat:4").execute()
print(get_3_result)  # >>> ['#0', '#3', '#4']

r.set("shellpath", "/usr/syscmds/")

with r.pipeline() as pipe:
    # Repeat until successful.
    while True:
        try:
            # Watch the key we are about to change.
            pipe.watch("shellpath")

            # The pipeline executes commands directly (instead of
            # buffering them) from immediately after the `watch()`
            # call until we begin the transaction.
            current_path = pipe.get("shellpath")
            new_path = current_path + ":/usr/mycmds/"

            # Start the transaction, which will enable buffering
            # again for the remaining commands.
            pipe.multi()

            pipe.set("shellpath", new_path)

            pipe.execute()

            # The transaction succeeded, so break out of the loop.
            break
        except redis.WatchError:
            # The transaction failed, so continue with the next attempt.
            continue

get_path_result = r.get("shellpath")
print(get_path_result)  # >>> '/usr/syscmds/:/usr/mycmds/'

r.set("shellpath", "/usr/syscmds/")

def watched_sequence(pipe):
    current_path = pipe.get("shellpath")
    new_path = current_path + ":/usr/mycmds/"

    pipe.multi()

    pipe.set("shellpath", new_path)

trans_result = r.transaction(watched_sequence, "shellpath")
print(trans_result)  # True

get_path_result = r.get("shellpath")
print(get_path_result)  # >>> '/usr/syscmds/:/usr/mycmds/'

code:
match redis::pipe()
            .atomic()
            .incr("counter:1", 1)
            .incr("counter:2", 2)
            .incr("counter:3", 3)
            .exec(&mut r)
        {
            Ok(_) => {
                println!("Transaction executed successfully");
            },
            Err(e) => {
                println!("Error executing transaction: {e}");
                return;
            }
        };

code:
var watchedTrans = new Transaction(db);

        watchedTrans.AddCondition(Condition.KeyNotExists("customer:39182"));

        _ = watchedTrans.Db.HashSetAsync(
            "customer:39182",
            [
                new("name", "David"),
                new("age", "27")
            ]
        );

        bool succeeded = watchedTrans.Execute();
        Console.WriteLine(succeeded); // >>> true

section_id: watch-keys-for-changes

code:
// Set initial value of `shellpath`.
	rdb.Set(ctx, "shellpath", "/usr/syscmds/", 0)

	const maxRetries = 1000

	// Retry if the key has been changed.
	for i := 0; i < maxRetries; i++ {
		err := rdb.Watch(ctx,
			func(tx *redis.Tx) error {
				currentPath, err := rdb.Get(ctx, "shellpath").Result()
				newPath := currentPath + ":/usr/mycmds/"

				_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
					pipe.Set(ctx, "shellpath", newPath, 0)
					return nil
				})

				return err
			},
			"shellpath",
		)

		if err == nil {
			// Success.
			break
		} else if err == redis.TxFailedErr {
			// Optimistic lock lost. Retry the transaction.
			continue
		} else {
			// Panic for any other error.
			panic(err)
		}
	}

	fmt.Println(rdb.Get(ctx, "shellpath").Val())
	// >>> /usr/syscmds/:/usr/mycmds/

code:
// Set initial value of `shellpath`.
        jedis.set("shellpath", "/usr/syscmds/");

        // Start the transaction and watch the key we are about to update.
        try (AbstractTransaction trans = jedis.transaction(false)) { // create a Transaction object without sending MULTI command
            trans.watch("shellpath"); // send WATCH command(s)
            trans.multi(); // send MULTI command

            String currentPath = jedis.get("shellpath");
            String newPath = currentPath + ":/usr/mycmds/";

            // Commands added to the `trans` object
            // will be buffered until `trans.exec()` is called.
            Response<String> setResult = trans.set("shellpath", newPath);
            List<Object> transResults = trans.exec();

            // The `exec()` call returns null if the transaction failed.
            if (transResults != null) {
                // Responses are available if the transaction succeeded.
                System.out.println(setResult.get()); // >>> OK

                // You can also get the results from the list returned by
                // `trans.exec()`.
                for (Object item: transResults) {
                    System.out.println(item);
                }
                // >>> OK

                System.out.println(jedis.get("shellpath"));
                // >>> /usr/syscmds/:/usr/mycmds/
            }
        }

code:
let key = "shellpath";
        let _: () = r.set(key, "/usr/syscmds/").unwrap();

        let Ok(_,): Result<((),), _> = redis::transaction(&mut r, &[key], |r, pipe| {
            let mut path: String = r.get(key).unwrap();
            path.push_str(":/usr/mycmds/");
            pipe.set(key, path).query(r)
        }) else {
            println!("Error executing transaction");
            return;
        };

        match r.get("shellpath") {
            Ok(res) => {
                let res: String = res;
                println!("{res}");
                // >>> /usr/syscmds/:/usr/mycmds/
            },
            Err(e) => {
                println!("Error getting shellpath: {e}");
                return;
            }
        };


title: Smart client handoffs

summary: Learn how to avoid disruptions during Redis server maintenance.

title: Overview

text:
*Smart client handoffs (SCH)* is a feature of Redis Cloud and
Redis Software servers that lets them actively notify clients
about planned server maintenance shortly before it happens. This
lets a client reconnect or otherwise respond gracefully without significant
interruptions in service.

SCH is primarily useful when server software or hardware upgrades
are required. It provides two main features to help the
client avoid disruptions in service during the maintenance period:

-   **Relaxed timeouts**: Upgrades tend to impact the general performance of the server.
    Advance notification of the upgrade lets a client adjust its command
    timeouts to take this into account and avoid aborting commands too soon.
-   **Pre-handoffs**: Upgrades also involve migrating
    Redis shards to new nodes, which inevitably disconnects clients from
    existing nodes. However, with some advance warning of the disconnection,
    a client can buffer commands, connect to a new node, and then resume
    the buffered commands without aborting any of them. As a result, users
    see no disruption in service. These transparent reconnections to new endpoints
    are known as *pre-handoffs*.

SCH does not work with blocking connections.
These include connections used for blocking operations like
[`BLPOP`]() and also
[pub/sub]() subscriptions.
All non-blocking operations are safe to use with SCH.

title: SCH support in Redis client libraries

text:
SCH is enabled automatically on the client side during connection
if you select the [RESP3]()
protocol, which is a requirement for SCH. However, you can
configure some parameters, such as the timeouts to use
during maintenance.
See the pages linked below to learn how to configure SCH for:

- [redis-py]()
- [node-redis]()
- [Lettuce]()
- [go-redis]()

title: SCH support in Redis server products

title: Redis Cloud

text:
SCH is fully supported and enabled by default on Redis Cloud, except when you
are using one of the following options:

- [AWS PrivateLink]()
- [Google Cloud Private Service Connect]()

These services don't currently allow for pre-handoffs, but you still get the
benefit of relaxed timeouts during database version upgrades. All other
configurations have full support for both relaxed timeouts and pre-handoffs.

title: Redis Software

text:
You must enable SCH explicitly on self-managed Redis Software servers by using the
[v1/cluster]()
REST API request to set the `client_maint_notifications` option to `true`.
The example below shows how to do this using the
[`curl`](https://curl.se/) command line utility:

[code example]

Redis Software uses relaxed timeouts to avoid command failures during
database version upgrades. The support for pre-handoffs depends on
the specific upgrade method you use, as detailed in the table below.

| Upgrade method | SCH support | Expected behavior |
| --- | --- | --- |
| [Rolling upgrade]() | Full | New nodes and old ones removed sequentially. SCH pre-handoffs and relaxed timeouts greatly reduce disruptions during the upgrade. |
| [In-place upgrade]() | Partial | Relaxed timeouts reduce errors but there are no pre-handoffs. Disconnections occur when processes are replaced during the upgrade, so clients should rely on auto-reconnect, which will cause brief lapses in service. |
| [Maintenance mode]() | Full | SCH is fully supported during hardware or OS patching operations. Pre-handoffs and relaxed timeouts minimize application impact. |

title: Redis Enterprise for Kubernetes

text: SCH is not currently supported for [Kubernetes]() clusters.

title: Redis Open Source

text: SCH is not currently supported for [Redis Open Source]().

code:
curl -k -X PUT -H "accept: application/json" \
    -H "content-type: application/json" \
    -u "test@redis.com:test123" \
    -d '{ "client_maint_notifications": true }' \
    https://localhost:9443/v1/cluster

section_id: redis-software


title: Redis bitfields

summary: Introduction to Redis bitfields

title: Bitmap/bitfield command summary

text:
**7 commands in this group:**

[View all bitmap commands](https://redis.io/commands/?group=bitmap)

| Command | Summary | Complexity | Since |
|---------|---------|------------|-------|
| [BITCOUNT](https://redis.io/commands/bitcount/) | Counts the number of set bits (population counting) in a string. | O(N) | 2.6.0 |
| [BITFIELD](https://redis.io/commands/bitfield/) | Performs arbitrary bitfield integer operations on strings. | O(1) for each subcommand specified | 3.2.0 |
| [BITFIELD_RO](https://redis.io/commands/bitfield_ro/) | Performs arbitrary read-only bitfield integer operations on strings. | O(1) for each subcommand specified | 6.0.0 |
| [BITOP](https://redis.io/commands/bitop/) | Performs bitwise operations on multiple strings, and stores the result. | O(N) | 2.6.0 |
| [BITPOS](https://redis.io/commands/bitpos/) | Finds the first set (1) or clear (0) bit in a string. | O(N) | 2.8.7 |
| [GETBIT](https://redis.io/commands/getbit/) | Returns a bit value by offset. | O(1) | 2.2.0 |
| [SETBIT](https://redis.io/commands/setbit/) | Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist. | O(1) | 2.2.0 |

Redis bitfields let you set, increment, and get integer values of arbitrary bit length.
For example, you can operate on anything from unsigned 1-bit integers to signed 63-bit integers.

These values are stored using binary-encoded Redis strings.
Bitfields support atomic read, write and increment operations, making them a good choice for managing counters and similar numerical values.

title: Example

role: example

text:
Suppose you want to maintain two metrics for various bicycles: the current price and the number of owners over time. You can represent these counters with a 32-bit wide bitfield for each bike.

* Bike 1 initially costs 1,000 (counter in offset 0) and has never had an owner. After being sold, it's now considered used and the price instantly drops to reflect its new condition, and it now has an owner (offset 1). After quite some time, the bike becomes a classic. The original owner sells it for a profit, so the price goes up and the number of owners does as well.Finally, you can look at the bike's current price and number of owners.

#### Code Examples

Bitfield operations: Use BITFIELD to atomically set, increment, and get integer values of arbitrary bit length when you need to manage multiple counters efficiently

**Difficulty:** Intermediate

**Commands:** BITFIELD

**Complexity:**
- BITFIELD: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python

**Go:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**Python:**

[code example]

title: Performance

role: performance

text: [`BITFIELD`]() is O(n), where _n_ is the number of counters accessed.

code:
> BITFIELD bike:1:stats SET u32 #0 1000
1) (integer) 0
> BITFIELD bike:1:stats INCRBY u32 #0 -50 INCRBY u32 #1 1
1) (integer) 950
2) (integer) 1
> BITFIELD bike:1:stats INCRBY u32 #0 500 INCRBY u32 #1 1
1) (integer) 1450
2) (integer) 2
> BITFIELD bike:1:stats GET u32 #0 GET u32 #1
1) (integer) 1450
2) (integer) 2

section_id: example

code:
res1, err := rdb.BitField(ctx, "bike:1:stats",
		"set", "u32", "#0", "1000",
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res1) // >>> [0]

	res2, err := rdb.BitField(ctx,
		"bike:1:stats",
		"incrby", "u32", "#0", "-50",
		"incrby", "u32", "#1", "1",
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res2) // >>> [950 1]

	res3, err := rdb.BitField(ctx,
		"bike:1:stats",
		"incrby", "u32", "#0", "500",
		"incrby", "u32", "#1", "1",
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res3) // >>> [1450 2]

	res4, err := rdb.BitField(ctx, "bike:1:stats",
		"get", "u32", "#0",
		"get", "u32", "#1",
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res4) // >>> [1450 2]

code:
List<Long> res1 = jedis.bitfield("bike:1:stats", "SET", "u32", "#0", "1000");
        System.out.println(res1);   // >>> [0]

        List<Long> res2 = jedis.bitfield("bike:1:stats", "INCRBY", "u32", "#0", "-50", "INCRBY", "u32", "#1", "1");
        System.out.println(res2);   // >>> [950, 1]

        List<Long> res3 = jedis.bitfield("bike:1:stats", "INCRBY", "u32", "#0", "500", "INCRBY", "u32", "#1", "1");
        System.out.println(res3);   // >>> [1450, 2]

        List<Long> res4 = jedis.bitfield("bike:1:stats", "GET", "u32", "#0", "GET", "u32", "#1");
        System.out.println(res4);   // >>> [1450, 2]

code:
let res1 = await client.bitField("bike:1:stats", [{
  operation: 'SET',
  encoding: 'u32',
  offset: '#0',
  value: 1000
}]);
console.log(res1);  // >>> [0]

let res2 = await client.bitField('bike:1:stats', [
  {
    operation: 'INCRBY',
    encoding: 'u32',
    offset: '#0',
    increment: -50
  },
  {
    operation: 'INCRBY',
    encoding: 'u32',
    offset: '#1',
    increment: 1
  }
]);
console.log(res2);  // >>> [950, 1]

let res3 = await client.bitField('bike:1:stats', [
  {
    operation: 'INCRBY',
    encoding: 'u32',
    offset: '#0',
    increment: 500
  },
  {
    operation: 'INCRBY',
    encoding: 'u32',
    offset: '#1',
    increment: 1
  }
]);
console.log(res3);  // >>> [1450, 2]

let res4 = await client.bitField('bike:1:stats', [
  {
    operation: 'GET',
    encoding: 'u32',
    offset: '#0'
  },
  {
    operation: 'GET',
    encoding: 'u32',
    offset: '#1'
  }
]);
console.log(res4);  // >>> [1450, 2]