Connecting to a sharded Valkey™ cluster
To connect to a sharded Valkey™ cluster, in connection strings, specify the FQDNs of master hosts in each shard.
Encrypted connection is supported via port 6380 and unencrypted via port 6379.
Warning
When using SSL, you can only connect to clusters with TLS support enabled.
Connecting from graphical IDEs
The connection was tested in the following environment:
- MacOS Big Sur 11.3.
- DBeaver Enterprise:
21.0.
You can only use graphical IDEs to connect to cluster hosts through an SSL tunnel using a created VM. Before connecting, prepare a certificate.
To avoid connection errors, save the certificate
Connections to Valkey™ clusters are only available in DBeaver business editions
To connect to a cluster:
- Create a new DB connection:
- In the Database menu, select New connection.
- Select Valkey™ from the DB list.
- Click Next.
- Specify the connection parameters on the Main tab:
-
Host: Specify comma-separated FQDNs of master hosts in each shard.
To learn how to get a host FQDN, see this guide.
-
Port:
6379for a regular cluster or6380for a cluster with SSL encryption enabled. -
Under Authentication, specify the cluster password.
-
- On the SSH tab:
- Enable the Use SSL tunnel setting.
- Specify the SSH tunnel parameters:
- Host/IP: Public IP address of the VM for connection.
- Username: Username for connecting to the VM.
- Authentication method:
Public key. - Secret key: Path to the file with the private key used for connecting to the VM.
- Passphrase: Private key password.
- On the SSL tab:
- Enable the Use SSL and Skip hostname validation settings.
- Under Parameters:
- Select Method: Set of certificates.
- In the Root certificate field, specify the path to the saved SSL certificate file.
- Click Test Connection ... to test the DB connection. If the connection is successful, you will see the connection status and information about the DBMS and driver.
- Click Ready to save the database connection settings.
Before you connect from a Docker container
To connect to a Yandex Managed Service for Valkey™ cluster from a Docker container, add the following lines to the Dockerfile:
# Build the redis-tools utility with TLS support manually.
RUN apt-get update && \
apt-get install make gcc libssl-dev --yes && \
wget https://download.redis.io/redis-stable.tar.gz && \
tar -xzvf redis-stable.tar.gz && \
cd redis-stable && \
make BUILD_TLS=yes MALLOC=libc && \
make install && \
cp ./src/redis-cli /usr/bin/
# Build the redis-tools utility with TLS support manually.
RUN apt-get update && \
apt-get install wget make gcc libssl-dev --yes && \
wget https://download.redis.io/redis-stable.tar.gz && \
tar -xzvf redis-stable.tar.gz && \
cd redis-stable && \
make BUILD_TLS=yes MALLOC=libc && \
make install && \
cp ./src/redis-cli /usr/bin/ && \
# Get an SSL certificate.
mkdir --parents ~/.redis && \
wget "https://storage.yandexcloud.net/cloud-certs/CA.pem" \
--output-document ~/.redis/YandexInternalRootCA.crt && \
chmod 0655 ~/.redis/YandexInternalRootCA.crt
Examples of connection strings
The Linux examples were tested in the following environment:
- Yandex Cloud virtual machine running Ubuntu 20.04 LTS.
- Bash:
5.0.16. - Python:
3.8.2; pip3:20.0.2. - PHP:
7.4.3. - OpenJDK:
11.0.8; Maven:3.6.3. - Node.JS:
10.19.0, npm:6.14.4. - Go:
1.13.8. - Ruby:
2.7.0p0. - unixODBC:
2.3.6.
The Windows examples were tested in the following environment:
- A local machine with Windows 10 Pro build
19042.1052. - PowerShell:
5.1.19041. - cURL:
7.55.1 WinSSL.
To see code examples with the host FQDN filled in, open the cluster page in the management console
Bash
Before connecting, install the dependencies:
sudo apt update && sudo apt install -y redis-tools
Connecting directly to the master host:
Specify the FQDN of the master host in the desired shard:
redis-cli \
-c \
-h <FQDN_of_master_host_in_required_shard> \
-a <password>
Before connecting, install the dependencies:
Build the redis-tools utility with TLS support in one of two ways:
-
From a repository
-
Connect a repository:
sudo apt-add-repository ppa:redislabs/redisPackages in this repository have already been built with the
BUILD_TLS=yesflag. -
Install the utility:
sudo apt update && sudo apt install -y redis-tools
-
-
Manually
Go to the directory you want to download the distribution to. Download the stable version of the utility, then build and install it:
wget https://download.redis.io/redis-stable.tar.gz && \ tar -xzvf redis-stable.tar.gz && \ cd redis-stable && \ make BUILD_TLS=yes && \ sudo make install && \ sudo cp ./src/redis-cli /usr/bin/
Connecting directly to the master host:
Specify the FQDN of the master host in the desired shard:
redis-cli \
-c \
-h <FQDN_of_master_host_in_required_shard> \
-a <password> \
-p 6380 \
--tls \
--cacert ~/.redis/YandexInternalRootCA.crt \
To learn how to get a host FQDN, see this guide.
When you are connected to the cluster, run the commands:
SET foo bar
GET foo
If the GET request returns nil, it means that the entry for the foo key has been moved to a different shard. Connect to it and repeat the request: it will return bar.
C#
Before connecting:
-
Install the dependencies:
sudo apt update && sudo apt install --yes apt-transport-https dotnet-sdk-6.0 -
Create a directory for the project:
cd ~/ && mkdir cs-project && cd cs-project -
Create a configuration file:
RedisClient.csproj
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="StackExchange.Redis" Version="2.8.58" /> </ItemGroup> </Project> -
To connect with SSL, get an SSL certificate.
Program.cs
using System;
using System.Threading.Tasks;
using StackExchange.Redis;
namespace RedisClient
{
class Program
{
// Configuration constants
private const string TEST_KEY = "test-key";
private const string TEST_VALUE = "test-value";
private const string USERNAME = "default";
private const string PASSWORD = "<password>";
static async Task<int> Main(string[] args)
{
try
{
var masterOptions = new ConfigurationOptions
{
EndPoints = {
"<FQDN_of_master_host_in_shard_1>:6379",
...
"<FQDN_of_master_host_in_shard_N>:6379"
},
User = USERNAME,
Password = PASSWORD
};
var connection = await ConnectionMultiplexer.ConnectAsync(masterOptions);
var db = connection.GetDatabase();
// Send SET command
bool setResult = await db.StringSetAsync(TEST_KEY, TEST_VALUE);
if (!setResult)
{
Console.WriteLine($"SET failed for key {TEST_KEY}");
return 1;
}
Console.WriteLine($"Successfully set {TEST_KEY} = {TEST_VALUE}");
// Send GET command
var getResult = await db.StringGetAsync(TEST_KEY);
if (!getResult.HasValue)
{
Console.WriteLine($"GET failed: Key {TEST_KEY} not found");
return 1;
}
string retrievedValue = getResult.ToString();
if (retrievedValue != TEST_VALUE)
{
Console.WriteLine($"GET failed. Expected: '{TEST_VALUE}', Actual: '{retrievedValue}'");
return 1;
}
Console.WriteLine($"Successfully retrieved {TEST_KEY} = {retrievedValue}");
return 0;
}
catch (Exception ex)
{
Console.WriteLine($"Operation failed: {ex.Message}");
return 1;
}
}
}
}
Program.cs
using System;
using System.Threading.Tasks;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using StackExchange.Redis;
namespace RedisClient
{
class Program
{
// Configuration constants
private const string TEST_KEY = "test-key";
private const string TEST_VALUE = "test-value";
private const string USERNAME = "default";
private const string PASSWORD = "<password>";
private const string CERT = "/home/<home_directory>/.redis/YandexInternalRootCA.crt"
static async Task<int> Main(string[] args)
{
try
{
var masterOptions = new ConfigurationOptions
{
EndPoints = {
"<FQDN_of_master_host_in_shard_1>:6380",
...
"<FQDN_of_master_host_in_shard_N>:6380"
},
User = USERNAME,
Password = PASSWORD,
Ssl = true
};
masterOptions.CertificateValidation += (
object sender,
X509Certificate? certificate,
X509Chain? chain,
SslPolicyErrors sslPolicyErrors) =>
{
if (certificate == null) {
return false;
}
var ca = new X509Certificate2(CERT);
bool verdict = (certificate.Issuer == ca.Subject);
if (verdict) {
return true;
}
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
return false;
}
var connection = await ConnectionMultiplexer.ConnectAsync(masterOptions);
var db = connection.GetDatabase();
// Send SET command
bool setResult = await db.StringSetAsync(TEST_KEY, TEST_VALUE);
if (!setResult)
{
Console.WriteLine($"SET failed for key {TEST_KEY}");
return 1;
}
Console.WriteLine($"Successfully set {TEST_KEY} = {TEST_VALUE}");
// Send GET command
var getResult = await db.StringGetAsync(TEST_KEY);
if (!getResult.HasValue)
{
Console.WriteLine($"GET failed: Key {TEST_KEY} not found");
return 1;
}
string retrievedValue = getResult.ToString();
if (retrievedValue != TEST_VALUE)
{
Console.WriteLine($"GET failed. Expected: '{TEST_VALUE}', Actual: '{retrievedValue}'");
return 1;
}
Console.WriteLine($"Successfully retrieved {TEST_KEY} = {retrievedValue}");
return 0;
}
catch (Exception ex)
{
Console.WriteLine($"Operation failed: {ex.Message}");
return 1;
}
}
}
}
To learn how to get a host FQDN, see this guide.
Connecting:
cd ~/cs-project && \
dotnet build && dotnet run bin/Debug/net6.0/RedisClient
If the connection to the cluster and the test query are successful, the following strings will be output:
Successfully set test-key = test-value
Successfully retrieved test-key = test-value
Go
Before connecting, install the following dependencies:
sudo apt update && sudo apt install --yes golang git && \
go mod init github.com/go-redis/redis && \
go get github.com/redis/go-redis/v9
connect.go
package main
import (
"fmt"
"github.com/go-redis/redis/v7"
"time"
)
func main() {
hostports := []string{
"<FQDN_of_master_host_in_shard_1>:6379",
...
"<FQDN_of_master_host_in_shard_N>:6379",
}
options := redis.UniversalOptions{
Addrs: hostports,
DB: 0,
ReadOnly: false,
DialTimeout: 5 * time.Second,
Password: "<password>",
}
client := redis.NewUniversalClient(&options)
err := client.Set("foo", "bar", 0).Err()
if err != nil {
panic(err)
}
result, err := client.Get("foo").Result()
if err != nil {
panic(err)
}
fmt.Println(result)
client.Close()
}
connect.go
package main
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net"
"os"
"strings"
"time"
"github.com/redis/go-redis/v9"
)
func main() {
caCert, err := os.ReadFile("/home/<home_directory>/.redis/YandexInternalRootCA.crt")
if err != nil {
panic(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
hostports := []string{
"<FQDN_of_master_host_in_shard_1>:6380",
...
"<FQDN_of_master_host_in_shard_N>:6380",
}
options := redis.UniversalOptions{
Addrs: hostports,
MaxRedirects: 1,
Password: "password",
DB: 0,
ReadOnly: false,
DialTimeout: 5 * time.Second,
TLSConfig: &tls.Config{
RootCAs: caCertPool,
ServerName: "c-<cluster_ID>.rw.mdb.yandexcloud.net",
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
certs := make([]*x509.Certificate, len(rawCerts))
for i := 0; i < len(rawCerts); i++ {
cert, err := x509.ParseCertificate(rawCerts[i])
if err != nil {
return fmt.Errorf("error parsing certificate: %+v", err)
}
certs[i] = cert
}
opts := x509.VerifyOptions{
Roots: caCertPool,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
for i := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(certs[i])
}
_, err := certs[0].Verify(opts)
return err
},
},
}
options.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
parts := strings.Split(addr, ":")
newAddr := addr
if len(parts) > 1 && !strings.HasPrefix(parts[0], "[") {
newAddr = "[" + strings.Join(parts[:len(parts)-1], ":") + "]:" + parts[len(parts)-1]
}
netDialer := &net.Dialer{
Timeout: options.DialTimeout,
KeepAlive: 5 * time.Minute,
}
return tls.DialWithDialer(netDialer, network, newAddr, options.TLSConfig)
}
ctx := context.Background()
client := redis.NewUniversalClient(&options)
err = client.Set(ctx, "foo", "bar", 0).Err()
if err != nil {
panic(err)
}
get := client.Get(ctx, "foo")
err = get.Err()
if err != nil {
panic(err)
}
fmt.Println(get.String())
}
To learn how to get a host FQDN, see this guide.
Connecting:
go run connect.go
If the connection to the cluster and the test query are successful, the bar string is output.
Java
Before connecting:
-
Install the dependencies:
sudo apt update && sudo apt install --yes default-jdk maven -
Create a folder for the Maven project:
cd ~/ && mkdir --parents project/src/java/com/example && cd project/ -
Create a configuration file for Maven:
pom.xml
<?xml version="1.0" encoding="utf-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>app</artifactId> <packaging>jar</packaging> <version>0.1.0</version> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.7.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.30</version> </dependency> </dependencies> <build> <finalName>${project.artifactId}-${project.version}</finalName> <sourceDirectory>src</sourceDirectory> <resources> <resource> <directory>src</directory> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <executions> <execution> <goals> <goal>attached</goal> </goals> <phase>package</phase> <configuration> <descriptorRefs> <descriptorRef> jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.example.App</mainClass> </manifest> </archive> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.0</version> <configuration> <archive> <manifest> <mainClass>com.example.App</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> </project>Up-to-date versions of dependencies for Maven:
-
To connect using SSL:
-
Create secure storage for certificates:
keytool -importcert \ -alias YARootCrt \ -file ~/.redis/YandexInternalRootCA.crt \ -keystore ~/.redis/YATrustStore \ -storepass <password_of_secure_certificate_storage> \ --noprompt && chmod 0655 ~/.redis/YATrustStore
src/java/com/example/App.java
package com.example;
import java.util.HashSet;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
public class App {
public static void main(String[] args) {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
HashSet<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
jedisClusterNodes.add(new HostAndPort("<FQDN_of_master_host_in_shard_1>", 6379));
...
jedisClusterNodes.add(new HostAndPort("<FQDN_of_master_host_in_shard_N>", 6379));
DefaultJedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder().
password("<password>").
build();
try {
JedisCluster jc = new JedisCluster(jedisClusterNodes, jedisClientConfig, 5, jedisPoolConfig);
jc.set("foo", "bar");
System.out.println(jc.get("foo"));
jc.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
src/java/com/example/App.java
package com.example;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import javax.net.ssl.SSLParameters;
import java.util.HashSet;
import java.util.Set;
public class App {
public static void main(String[] args) {
System.setProperty("javax.net.ssl.trustStore", "/home/<home_directory>/.redis/YATrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "<secure_certificate_storage_password>");
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
SSLParameters sslParameters = new SSLParameters();
jedisClusterNodes.add(new HostAndPort("<FQDN_of_master_host_in_shard_1>", 6380));
...
jedisClusterNodes.add(new HostAndPort("<FQDN_of_master_host_in_shard_N>", 6380));
DefaultJedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder().
password("<cluster_password>").
ssl(true).
sslParameters(sslParameters).
build();
try {
JedisCluster jc = new JedisCluster(jedisClusterNodes, jedisClientConfig, 5, jedisPoolConfig);
jc.set("foo", "bar");
System.out.println(jc.get("foo"));
jc.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
To learn how to get a host FQDN, see this guide.
Connecting:
mvn clean package && \
java -jar target/app-0.1.0-jar-with-dependencies.jar
If the connection to the cluster and the test query are successful, the bar string is output.
Node.js
Before connecting, install the dependencies:
sudo apt update && sudo apt install -y nodejs npm && \
npm install ioredis
app.js
"use strict";
const Redis = require("ioredis");
const cluster = new Redis.Cluster(
[
{
host: "<FQDN_of_master_host_in_shard_1>",
port: 6379
},
...
{
host: "<FQDN_of_master_host_in_shard_N>",
port: 6379
}
],
{
redisOptions: {
password: "<password>"
}
}
);
cluster.on("ready", () => {
Promise.all([
cluster.set("foo", "bar"),
cluster.get("foo")
]).then(
(result) => {
console.log(result[1]); // result == ["OK", "bar"]
cluster.disconnect();
},
(reject) => {
console.log(reject);
cluster.disconnect();
}
);
});
app.js
"use strict";
const Redis = require("ioredis");
const fs = require("fs");
const cluster = new Redis.Cluster(
[
{
host: "<FQDN_of_master_host_in_shard_1>",
port: 6380
},
...
{
host: "<FQDN_of_master_host_in_shard_N>",
port: 6380
},
],
{
redisOptions: {
password: "<password>",
tls: {
ca: [fs.readFileSync("/home/<home_directory>/.redis/YandexInternalRootCA.crt")],
checkServerIdentity: () => {
return null;
}
}
}
}
);
cluster.on("ready", () => {
Promise.all([
cluster.set("foo", "bar"),
cluster.get("foo")
]).then(
(result) => {
console.log(result[1]); // result == [ "OK", "bar"]
cluster.disconnect();
},
(reject) => {
console.log(reject);
cluster.disconnect();
}
);
});
To learn how to get a host FQDN, see this guide.
Connecting:
node app.js
If the connection to the cluster and the test query are successful, the bar string is output.
PHP
Before connecting, install the dependencies:
sudo apt update && sudo apt install -y php php-dev php-pear && \
sudo pear channel-discover pear.nrk.io && \
sudo pear install nrk/Predis
connect.php
<?php
require "Predis/Autoloader.php";
Predis\Autoloader::register();
$hosts = [
"tcp://<FQDN_of_master_host_in_shard_1>:6379",
...
"tcp://<FQDN_of_master_host_in_shard_N>:6379",
];
$options = [
"cluster" => "redis",
"parameters" => [
"password" => "<password>",
],
];
$conn = new Predis\Client($hosts, $options);
$conn->set("foo", "bar");
var_dump($conn->get("foo"));
$conn->disconnect();
?>
connect.php
<?php
require "Predis/Autoloader.php";
Predis\Autoloader::register();
$hosts = [
'tls://<FQDN_of_master_host_in_shard_1>:6380?ssl[cafile]=/home/<home_directory>/.redis/YandexInternalRootCA.crt',
...
'tls://<FQDN_of_master_host_in_shard_N>:6380?ssl[cafile]=/home/<home_directory>/.redis/YandexInternalRootCA.crt',
];
$options = [
'cluster' => 'predis',
'parameters' => [
'password' => '<password>',
],
];
$conn = new Predis\Client($hosts, $options);
$conn->set('foo', 'bar');
var_dump($conn->get("foo"));
$conn->disconnect();
?>
To learn how to get a host FQDN, see this guide.
Connecting:
php connect.php
If the connection to the cluster and the test query are successful, the bar string is output.
Python
Before connecting, install the required dependencies:
sudo apt update && sudo apt install -y python3 python3-pip python3-venv && \
python3 -m venv env && \
source env/bin/activate && \
pip install pip -U && \
pip install pyopenssl redis-py-cluster setuptools_rust
connect.py
from rediscluster import RedisCluster
startup_nodes = [
{"host": "<FQDN_of_master_host_in_shard_1>", "port": 6379},
...
{"host": "<FQDN_of_master_host_in_shard_N>", "port": 6379},
]
rc = RedisCluster(
startup_nodes=startup_nodes,
decode_responses=True,
skip_full_coverage_check=True,
password="<password>",
)
rc.set("foo", "bar")
print(rc.get("foo"))
connect.py
import OpenSSL
from rediscluster import RedisCluster
startup_nodes = [
{"host": "<FQDN_of_master_host_in_shard_1>", "port": 6380},
...
{"host": "<FQDN_of_master_host_in_shard_N>", "port": 6380},
]
rc = RedisCluster(
startup_nodes=startup_nodes,
decode_responses=True,
skip_full_coverage_check=True,
password="<password>",
ssl=True,
ssl_ca_certs="/home/<home_directory>/.redis/YandexInternalRootCA.crt",
)
rc.set("foo", "bar")
print(rc.get("foo"))
To learn how to get a host FQDN, see this guide.
Connecting:
python3 connect.py
If the connection to the cluster and the test query are successful, the bar string is output.
Ruby
Before connecting, install the dependencies:
sudo apt update && sudo apt install -y ruby && \
sudo gem install redis
connect.rb
# coding: utf-8
require 'redis'
nodes = [
{ host: '<FQDN_of_master_host_in_shard_1>', port: 6379 },
...
{ host: '<FQDN_of_master_host_in_shard_N>', port: 6379 }
]
conn = Redis.new(
cluster: nodes,
password: '<password>'
)
conn.set('foo', 'bar')
puts conn.get('foo')
conn.close
connect.rb
# coding: utf-8
require 'redis'
nodes = [
{ host: '<FQDN_of_master_host_in_shard_1>', port: 6380 },
...
{ host: '<FQDN_of_master_host_in_shard_N>', port: 6380 }
]
conn = Redis.new(
cluster: nodes,
password: '<password>',
ssl: true,
ssl_params: {
ca_file: '/home/<home_directory>/.redis/YandexInternalRootCA.crt',
verify_hostname: false
}
)
conn.set('foo', 'bar')
puts conn.get('foo')
conn.close
To learn how to get a host FQDN, see this guide.
Connecting:
ruby connect.rb
If the connection to the cluster and the test query are successful, the bar string is output.