Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ simple.metrics.dumpRate=PT5S
management.otlp.metrics.export.enabled=false
management.otlp.metrics.export.url=http://localhost:4318/v1/metrics
management.otlp.metrics.export.step=PT1S

management.tracing.enabled=false
34 changes: 23 additions & 11 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jedis.version>7.0.0-SNAPSHOT</jedis.version>
<jedis.version>7.0.0</jedis.version>
</properties>

<repositories>
Expand Down Expand Up @@ -69,10 +69,6 @@
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-influx</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-otlp</artifactId>
Expand Down Expand Up @@ -111,6 +107,11 @@
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-all</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
Expand All @@ -126,8 +127,24 @@
<build>
<finalName>jedis-test-app</finalName>

<plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>jedis-version.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>jedis-version.properties</exclude>
</excludes>
</resource>
</resources>

<plugins>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
Expand All @@ -145,7 +162,6 @@
<goal>validate</goal>
</goals>
</execution>

</executions>
</plugin>
<plugin>
Expand All @@ -155,10 +171,6 @@
<mainClass>redis.clients.jedis.test.JedisTestApplication</mainClass>
</configuration>
</plugin>



</plugins>
</build>

</project>
75 changes: 75 additions & 0 deletions runner-config-multidb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
runner:
redis:
clientName: "jedis-test-app-multidb"
#username: "default"
#password: "foobared"
verifyPeer: false

test:
mode: multidb # Multi-database mode with failover support
clients: 1 # Number of client instances
connectionsPerClient: 100 # Number of connections per client (used as default pool size)
threadsPerConnection: 1 # Number of threads sharing same connection
workload:
type: redis_commands
maxDuration: PT10S
options:
valueSize: 100 # 100 characters
iterationCount: 10000 # How many iterations to run of the workload before finishing
keyRangeMin: 0 # 0 Minimum key range
keyRangeMax: 1000 # 10000 Maximum key range

clientOptions: # Jedis connection-level options
timeoutOptions:
fixedTimeout: PT2S # Socket/read timeout
socketOptions:
connectTimeout: PT2S # TCP connect timeout
pool:
maxIdle: 100
minIdle: 1
maxWait: PT3S
blockWhenExhausted: true
testWhileIdle: true
timeBetweenEvictionRuns: PT10S

# Multi-database configuration for failover support
# NOTE: For testing with a single Redis instance, we use 127.0.0.1 and localhost
# as different endpoints (they resolve to the same server but are treated as different by Jedis)
# In production, you would use different Redis servers (e.g., different hosts or ports)
# Connection pool settings are taken from clientOptions.pool section (shared across all databases)
multiDbConfig:
databases:
# Primary database (higher weight = higher priority)
- endpoint: redis://localhost:6379/0
weight: 1.0
healthCheckEnabled: true
healthCheckType: ping # Options: ping, lagaware

# Secondary database (lower weight = lower priority, used for failover)
# Using 127.0.0.1 instead of localhost to create a different endpoint identifier
- endpoint: redis://127.0.0.1:6379/0
weight: 0.5
healthCheckEnabled: true
healthCheckType: ping

# Circuit breaker configuration for failure detection
failureDetector:
slidingWindowSize: 1000 # Sliding window size in number of calls
thresholdMinNumberOfFailures: 500 # Minimum number of failures before circuit breaker is tripped
failureRateThreshold: 50.0 # Percentage of failures to trigger circuit breaker

# Retry configuration
commandRetry:
maxAttempts: 3 # Maximum number of retry attempts (including the initial call)
waitDuration: 500 # Number of milliseconds to wait between retry attempts
exponentialBackoffMultiplier: 2 # Exponential backoff factor

# Failback configuration
failbackSupported: true # Enable automatic failback
failbackCheckInterval: 1000 # Check every second for unhealthy database recovery
gracePeriod: 2000 # Keep database disabled for 2 seconds after it becomes unhealthy

# Failover behavior
fastFailover: true # Force closing connections to unhealthy database on failover
retryOnFailover: false # Do not retry failed commands during failover

62 changes: 60 additions & 2 deletions src/main/java/redis/clients/jedis/test/JedisWorkloadRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.File;

@Component
public class JedisWorkloadRunner {

Expand All @@ -21,6 +23,58 @@ public class JedisWorkloadRunner {
public JedisWorkloadRunner(MetricsReporter metricsReporter, WorkloadRunnerConfig config) {
this.metricsReporter = metricsReporter;
this.config = config;

// Initialize SSL configuration at startup
initializeSslConfiguration();
}

/**
* Initialize SSL/TLS configuration for the application.
*
* This method configures JVM-wide SSL truststore properties based on the runner configuration. The truststore is used for
* all SSL/TLS connections made by the application, including: - Redis connections over SSL/TLS - LagAware health check REST
* API calls - Any other HTTPS connections
*/
private void initializeSslConfiguration() {
WorkloadRunnerConfig.SslConfig sslConfig = config.getSsl();

if (sslConfig == null || sslConfig.getTruststorePath() == null) {
log.debug("No SSL truststore configuration provided, using JVM defaults");
return;
}

String truststorePath = sslConfig.getTruststorePath();
String truststorePassword = sslConfig.getTruststorePassword();
String truststoreType = sslConfig.getTruststoreType() != null ? sslConfig.getTruststoreType() : "JKS";

File truststoreFile = new File(truststorePath);
if (!truststoreFile.exists()) {
log.warn("SSL truststore file not found at: {}", truststoreFile.getAbsolutePath());
log.warn("SSL connections may fail if server certificates cannot be verified");
return;
}

if (!truststoreFile.canRead()) {
log.warn("SSL truststore file is not readable at: {}", truststoreFile.getAbsolutePath());
log.warn("SSL connections may fail if server certificates cannot be verified");
return;
}

log.info("Configuring JVM-wide SSL truststore");
log.info(" Truststore path: {}", truststoreFile.getAbsolutePath());
log.info(" Truststore type: {}", truststoreType);

// Set JVM-wide SSL truststore properties
// These properties are used by:
// - HttpsURLConnection (used by LagAware REST API calls)
// - SSLContext.getDefault() (used by various SSL/TLS clients)
System.setProperty("javax.net.ssl.trustStore", truststoreFile.getAbsolutePath());
if (truststorePassword != null) {
System.setProperty("javax.net.ssl.trustStorePassword", truststorePassword);
}
System.setProperty("javax.net.ssl.trustStoreType", truststoreType);

log.info("JVM-wide SSL truststore configured successfully");
}

public void run() {
Expand All @@ -29,8 +83,12 @@ public void run() {
runner = new StandaloneJedisWorkloadRunner(config, metricsReporter);
log.info("Running standalone workload (Jedis/UnifiedJedis)");
}
default -> throw new IllegalArgumentException("Unsupported mode for Jedis app: " + config.getTest().getMode()
+ ". Only STANDALONE is supported for Jedis right now.");
case "MULTIDB" -> {
runner = new MultiDbJedisWorkloadRunner(config, metricsReporter);
log.info("Running multi-database workload with failover support (MultiDbClient)");
}
default -> throw new IllegalArgumentException(
"Unsupported mode for Jedis app: " + config.getTest().getMode() + ". Supported modes: STANDALONE, MULTIDB");
}
runner.run();
}
Expand Down
Loading