This library provides an easy way to run an embedded Valkey server in your JVM projects, mainly for integration testing purposes.
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();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();
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();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 |
Valkey |
|
||
Linux |
Valkey |
|
||
macOS |
Valkey |
|
||
macOS |
Valkey |
|
||
Windows |
Memurai Developer |
|
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.
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
-
Krzysztof Styrc (@kstyrc)
-
Piotr Turek (@turu)
-
anthonyu (@anthonyu)
-
Artem Orobets (@enisher)
-
Sean Simonsen (@SeanSimonsen)
-
Rob Winch (@rwinch)
-
Cristian Badila (@cristi-badila)
-
Jurgen Voorneveld (@codemonstur)