Skip to content

Library for starting an embedded Valkey server or cluster in JVM projects

License

tobi-laa/embedded-valkey

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

embedded-valkey

This library provides an easy way to run an embedded Valkey server in your JVM projects, mainly for integration testing purposes.

Usage

To use the library, add the following dependency to your pom.xml:

<dependency>
    <groupId>io.github.tobi-laa</groupId>
    <artifactId>embedded-valkey</artifactId>
    <version><!-- insert newest version here --></version>
    <scope>test</scope>
</dependency>

You could then, for example, use ValkeyStandalone to start and stop an embedded Valkey server:

ValkeyStandalone valkey = ValkeyStandalone.builder().build();
valkey.start();
// do some work
valkey.stop();

This will, by default, start a Valkey server on a (more or less) random, free port. It will also attempt to download a fitting Valkey distribution for your operating system.

You can also provide a Valkey distributions yourself, for example:

ValkeyStandalone valkey = ValkeyStandalone.builder()
        .installationSupplier(OperatingSystem.MAC_OS_ARM64, downloadAndInstallMacOsPackageFromMacports(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("my.proxy.com", 8080)), OperatingSystem.MAC_OS_ARM64))
        .installationSupplier(OperatingSystem.LINUX_X86_64, installValkeyIoLinuxPackageFromClasspath("/bundles/valkey-8.1.3-jammy-x86_64.tar.gz", OperatingSystem.LINUX_X86_64))
        .installationSupplier(OperatingSystem.WINDOWS_X86_64, installWinX64MemuraiPackageFromClasspath("/bundles/memuraideveloper.4.1.6.nupkg"))
        .build();

All builders support a fluent API to configure the servers or clusters. For example:

ValkeyInstallationSupplier customInstallationProvider = null;
ValkeyStandalone valkey = ValkeyStandalone.builder()
        .installationSupplier(OperatingSystem.MAC_OS_ARM64, customInstallationProvider)
        .port(6379)
        .replicaOf("locahost", 6378)
        .build();

All embedded Valkey instances will generate a valkey.conf on the fly, based on sensible defaults. You can override any configuration directive, some of them are even available as first-class citizens in the builders. For example:

ValkeyInstallationSupplier customInstallationProvider = null;
ValkeyStandalone valkey = ValkeyStandalone.builder()
        .installationSupplier(OperatingSystem.MAC_OS_ARM64, customInstallationProvider)
        .importConf(Paths.get("/path/to/your/base/valkey.conf"))
        // everything that follows will override settings in the imported configuration
        .bind("127.0.0.1") // good for local development on Windows to prevent security popups
        .port(6379)
        .replicaOf("locahost", 6378)
        .directive("daemonize", "no")
        .directive("appendonly", "no")
        .directive("maxmemory", "128M")
        .build();

Setting up a cluster

The library supports setting up the following types of Valkey clusters:

  • High-availability clusters with Sentinels, main nodes and replicas

    //creates a cluster with 3 sentinels, quorum size of 2 and 3 replication groups, each with one main node and one replica
    ValkeyHighAvailability valkey = ValkeyHighAvailability.builder()
            .withSentinelBuilder(ValkeySentinel.builder())
            .sentinelCount(3)
            .quorumSize(2)
            .replicationGroup("main1", 1)
            .replicationGroup("main2", 1)
            .replicationGroup("main3", 1)
            .build();
    valkey.start();
    // do some work
    valkey.stop();
  • Sharded clusters with node replication

    ValkeyShardedCluster valkey = ValkeyShardedCluster.builder()
            .shard("main1", 1)
            .shard("main2", 1)
            .shard("main3", 1)
            .build();
    valkey.start();
    // do some work
    valkey.stop();

Retrieving ports

After starting a Valkey server or cluster, you can retrieve the ports the servers are bound to. The way in which you do this depends on whether you started a single node or a cluster:

ValkeyStandalone standalone = ValkeyStandalone.builder().build();
standalone.start();
int port = standalone.getPort(); // works the same way for sentinels

ValkeyHighAvailability highAvail = ValkeyHighAvailability.builder().build();
highAvail.start();
List<Integer> sentinelPorts = highAvail.getSentinels().stream().map(ValkeyNode::getPort).toList();

ValkeyShardedCluster cluster = ValkeyShardedCluster.builder()
        .shard("main1", 1)
        .shard("main2", 1)
        .shard("main3", 1)
        .build();
cluster.start();
List<Integer> mainNodePorts = cluster.getMainNodes().stream().map(ValkeyNode::getPort).toList();
List<Integer> replicaPorts = cluster.getReplicas().stream().map(ValkeyNode::getPort).toList();

Valkey packages

The library does not ship with any Valkey binaries. Instead, it downloads (and extracts) Valkey packages on demand from various sources. The following distributions are supported out of the box:

Operating system Distribution type Currently used version Packages downloaded from Link to package

Linux x86_64

Valkey

8.1.3

https://valkey.io

valkey-8.1.3-jammy-x86_64.tar.gz

Linux arm64

Valkey

8.1.3

https://valkey.io

valkey-8.1.3-jammy-arm64.tar.gz

macOS arm64

Valkey

8.1.3

https://macports.org

valkey-8.1.3_0.darwin_25.arm64.tbz2

macOS x86_64

Valkey

8.1.3

https://macports.org

valkey-8.1.3_0.darwin_24.x86_64.tbz2

Windows x86_64

Memurai Developer

4.1.6

https://www.nuget.org/packages

memuraideveloper.4.1.6.nupkg

Troubleshooting

SSL/TLS

You might get an error when you try to start the default binary without having openssl installed. The default binaries have TLS support but require a library on the host OS. On macOS you will probably get an error that looks like this:

'/opt/homebrew/opt/openssl@3/lib/libssl.3.dylib' (no such file),
'/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/opt/openssl@3/lib/libssl.3.dylib' (no such file),
'/opt/homebrew/opt/openssl@3/lib/libssl.3.dylib' (no such file),
'/usr/lib/libssl.3.dylib' (no such file, not in dyld cache)

One option for resolving the issue is to install openssl using brew install openssl@3. Alternatively, you can use a binary that doesn’t have TLS support. Either by compiling your own from source, or by using HankCP’s binary at ExecutableProvider.REDIS_7_2_MACOSX_14_SONOMA_HANKCP, or downloading one from some other place.

On linux the error will look like this:

/app/redis-server-6.2.6-v5-linux-amd64: error while loading shared libraries: libssl.so.3: cannot open
shared object file: No such file or directory

The problem is the same as on macOS. You need a binary that doesn’t require the libssl library or you need to provide that library. If you are running the app on your host you can install the needed package using your package manager. Such as with apt-get (sudo apt-get install openssl). If you are running this inside a docker image you’ll need to make sure the library is available inside the image.

Acknowledgements

Project history

This project is a refactored Kotlin port forked from the embedded-redis project by codemonstur, which was forked from ozimov, which was originally forked from kstyrc

Contributors