CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-redis-clients--jedis

Jedis is a blazingly small and sane Redis java client.

Overview
Eval results
Files

exceptions.mddocs/

Exception Handling

This document covers the comprehensive exception hierarchy in Jedis for robust error handling, including connection exceptions, cluster-specific exceptions, authentication failures, and best practices for error recovery.

Exception Hierarchy

Base Exception Classes

JedisException

Root exception class for all Jedis-specific exceptions.

public class JedisException extends RuntimeException {
    /**
     * Creates exception with message
     * @param message Error message
     */
    public JedisException(String message);
    
    /**
     * Creates exception with message and cause
     * @param message Error message
     * @param cause Underlying cause
     */
    public JedisException(String message, Throwable cause);
    
    /**
     * Creates exception with cause
     * @param cause Underlying cause
     */
    public JedisException(Throwable cause);
    
    /**
     * Gets error message
     * @return Exception message
     */
    @Override
    public String getMessage();
    
    /**
     * Gets underlying cause
     * @return Exception cause
     */
    @Override
    public Throwable getCause();
}

JedisDataException

Exception for Redis server errors and data-related issues.

public class JedisDataException extends JedisException {
    /**
     * Creates data exception with message
     * @param message Error message from Redis server
     */
    public JedisDataException(String message);
    
    /**
     * Creates data exception with message and cause
     * @param message Error message
     * @param cause Underlying cause
     */
    public JedisDataException(String message, Throwable cause);
}

Connection Exceptions

JedisConnectionException

Exception for connection-related failures.

public class JedisConnectionException extends JedisException {
    /**
     * Creates connection exception
     * @param message Connection error message
     */
    public JedisConnectionException(String message);
    
    /**
     * Creates connection exception with cause
     * @param message Error message
     * @param cause Network or I/O exception cause
     */
    public JedisConnectionException(String message, Throwable cause);
    
    /**
     * Creates connection exception from cause
     * @param cause Network exception
     */
    public JedisConnectionException(Throwable cause);
}

Usage Example

public void handleConnectionErrors() {
    Jedis jedis = null;
    try {
        jedis = new Jedis("redis.example.com", 6379);
        jedis.set("key", "value");
        
    } catch (JedisConnectionException e) {
        System.err.println("Connection failed: " + e.getMessage());
        
        // Check underlying cause
        Throwable cause = e.getCause();
        if (cause instanceof SocketTimeoutException) {
            System.err.println("Connection timed out");
        } else if (cause instanceof ConnectException) {
            System.err.println("Cannot reach Redis server");
        } else if (cause instanceof UnknownHostException) {
            System.err.println("Invalid Redis hostname");
        }
        
        // Implement retry logic
        retryConnection();
        
    } finally {
        if (jedis != null) {
            try {
                jedis.close();
            } catch (Exception e) {
                System.err.println("Error closing connection: " + e.getMessage());
            }
        }
    }
}

private void retryConnection() {
    int maxRetries = 3;
    int retryDelay = 1000; // 1 second
    
    for (int i = 0; i < maxRetries; i++) {
        try {
            Thread.sleep(retryDelay * (i + 1)); // Exponential backoff
            
            Jedis jedis = new Jedis("redis.example.com", 6379);
            jedis.ping(); // Test connection
            jedis.close();
            
            System.out.println("Connection restored");
            return;
            
        } catch (JedisConnectionException | InterruptedException e) {
            System.err.println("Retry " + (i + 1) + " failed: " + e.getMessage());
        }
    }
    
    System.err.println("All retry attempts failed");
}

Cluster Exceptions

JedisClusterException

General Redis Cluster operation exceptions.

public class JedisClusterException extends JedisException {
    /**
     * Creates cluster exception
     * @param message Error message
     */
    public JedisClusterException(String message);
    
    /**
     * Creates cluster exception with cause
     * @param message Error message
     * @param cause Exception cause
     */
    public JedisClusterException(String message, Throwable cause);
    
    /**
     * Creates cluster exception from cause
     * @param cause Exception cause
     */
    public JedisClusterException(Throwable cause);
}

JedisClusterOperationException

Exceptions specific to cluster operations.

public class JedisClusterOperationException extends JedisClusterException {
    /**
     * Creates cluster operation exception
     * @param message Operation error message
     */
    public JedisClusterOperationException(String message);
    
    /**
     * Creates cluster operation exception with cause
     * @param message Error message
     * @param cause Exception cause
     */
    public JedisClusterOperationException(String message, Throwable cause);
}

Cluster Redirection Exceptions

JedisRedirectionException

Base class for Redis Cluster redirection responses.

public abstract class JedisRedirectionException extends JedisException {
    /**
     * Creates redirection exception
     * @param message Redirection message
     * @param targetNode Target node for redirection
     * @param slot Cluster slot number
     */
    public JedisRedirectionException(String message, HostAndPort targetNode, int slot);
    
    /**
     * Gets target node for redirection
     * @return Target Redis cluster node
     */
    public HostAndPort getTargetNode();
    
    /**
     * Gets cluster slot number
     * @return Slot number (0-16383)
     */
    public int getSlot();
}

JedisMovedDataException

Exception for Redis Cluster MOVED responses.

public class JedisMovedDataException extends JedisRedirectionException {
    /**
     * Creates MOVED exception
     * @param message MOVED response message
     * @param targetNode Node that owns the slot
     * @param slot Cluster slot number
     */
    public JedisMovedDataException(String message, HostAndPort targetNode, int slot);
}

JedisAskDataException

Exception for Redis Cluster ASK responses.

public class JedisAskDataException extends JedisRedirectionException {
    /**
     * Creates ASK exception
     * @param message ASK response message
     * @param targetNode Node to ask for data
     * @param slot Cluster slot number
     */
    public JedisAskDataException(String message, HostAndPort targetNode, int slot);
}

Cluster Exception Handling Example

public class ClusterErrorHandler {
    private static final int MAX_REDIRECTIONS = 5;
    
    public String getWithRedirection(JedisCluster cluster, String key) {
        return executeWithRedirection(() -> cluster.get(key));
    }
    
    public <T> T executeWithRedirection(Supplier<T> operation) {
        int redirections = 0;
        
        while (redirections < MAX_REDIRECTIONS) {
            try {
                return operation.get();
                
            } catch (JedisMovedDataException e) {
                // Cluster topology changed, refresh slot mapping
                System.out.println("MOVED: Slot " + e.getSlot() + 
                                 " moved to " + e.getTargetNode());
                
                // JedisCluster handles this automatically, but we log it
                redirections++;
                
                if (redirections >= MAX_REDIRECTIONS) {
                    throw new JedisClusterOperationException(
                        "Too many MOVED redirections", e);
                }
                
            } catch (JedisAskDataException e) {
                // Temporary redirection during slot migration
                System.out.println("ASK: Slot " + e.getSlot() + 
                                 " temporarily at " + e.getTargetNode());
                
                // JedisCluster handles this automatically
                redirections++;
                
                if (redirections >= MAX_REDIRECTIONS) {
                    throw new JedisClusterOperationException(
                        "Too many ASK redirections", e);
                }
                
            } catch (JedisClusterException e) {
                // General cluster error
                System.err.println("Cluster operation failed: " + e.getMessage());
                
                if (e.getCause() instanceof JedisConnectionException) {
                    // Node unavailable, cluster may retry automatically
                    throw new JedisClusterOperationException(
                        "Cluster node unavailable", e);
                }
                
                throw e;
            }
        }
        
        throw new JedisClusterOperationException("Max redirections exceeded");
    }
}

Server State Exceptions

JedisBusyException

Exception when Redis server is busy.

public class JedisBusyException extends JedisDataException {
    /**
     * Creates busy exception
     * @param message Server busy message
     */
    public JedisBusyException(String message);
}

JedisNoScriptException

Exception when Lua script is not found in server cache.

public class JedisNoScriptException extends JedisDataException {
    /**
     * Creates no-script exception
     * @param message Script not found message
     */
    public JedisNoScriptException(String message);
    
    /**
     * Gets script SHA that wasn't found
     * @return Script SHA hash
     */
    public String getScriptSha();
}

Server State Exception Handling

public class ScriptManager {
    private final Map<String, String> scriptShas = new ConcurrentHashMap<>();
    
    public Object executeScript(Jedis jedis, String script, List<String> keys, 
                               List<String> args) {
        String sha = scriptShas.computeIfAbsent(script, 
            s -> jedis.scriptLoad(s));
        
        try {
            return jedis.evalsha(sha, keys, args);
            
        } catch (JedisNoScriptException e) {
            System.out.println("Script not in cache, loading: " + e.getScriptSha());
            
            // Reload script and retry
            String newSha = jedis.scriptLoad(script);
            scriptShas.put(script, newSha);
            
            return jedis.evalsha(newSha, keys, args);
            
        } catch (JedisBusyException e) {
            System.err.println("Redis server busy: " + e.getMessage());
            
            // Wait and retry once
            try {
                Thread.sleep(100);
                return jedis.evalsha(sha, keys, args);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                throw new JedisException("Script execution interrupted", ie);
            }
        }
    }
}

Security Exceptions

Authentication Exceptions

JedisAccessControlException

Exception for Redis ACL (Access Control List) violations.

public class JedisAccessControlException extends JedisDataException {
    /**
     * Creates ACL exception
     * @param message Access control error message
     */
    public JedisAccessControlException(String message);
}

JedisAuthenticationException

Exception for authentication failures.

public class JedisAuthenticationException extends JedisException {
    /**
     * Creates authentication exception
     * @param message Authentication error message
     */
    public JedisAuthenticationException(String message);
    
    /**
     * Creates authentication exception with cause
     * @param message Error message
     * @param cause Exception cause
     */
    public JedisAuthenticationException(String message, Throwable cause);
}

Security Exception Handling

public class SecureRedisClient {
    private final String username;
    private final String password;
    
    public SecureRedisClient(String username, String password) {
        this.username = username;
        this.password = password;
    }
    
    public Jedis createSecureConnection(String host, int port) {
        try {
            Jedis jedis = new Jedis(host, port);
            
            // Authenticate
            if (username != null && password != null) {
                jedis.auth(username, password);
            } else if (password != null) {
                jedis.auth(password);
            }
            
            // Test connection
            jedis.ping();
            return jedis;
            
        } catch (JedisAuthenticationException e) {
            System.err.println("Authentication failed: " + e.getMessage());
            throw new SecurityException("Cannot authenticate to Redis", e);
            
        } catch (JedisAccessControlException e) {
            System.err.println("Access denied: " + e.getMessage());
            
            if (e.getMessage().contains("NOPERM")) {
                System.err.println("User lacks required permissions");
            } else if (e.getMessage().contains("WRONGPASS")) {
                System.err.println("Invalid password");
            }
            
            throw new SecurityException("Access control violation", e);
            
        } catch (JedisConnectionException e) {
            System.err.println("Connection failed during authentication: " + e.getMessage());
            throw e;
        }
    }
    
    public <T> T executeSecureOperation(String host, int port, 
                                       Function<Jedis, T> operation) {
        Jedis jedis = null;
        try {
            jedis = createSecureConnection(host, port);
            return operation.apply(jedis);
            
        } catch (JedisAccessControlException e) {
            System.err.println("Operation denied by ACL: " + e.getMessage());
            
            // Could implement fallback to read-only operations
            if (e.getMessage().contains("WRITE")) {
                System.out.println("Attempting read-only fallback");
                // Implement read-only version
            }
            
            throw e;
            
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }
}

Validation Exceptions

JedisValidationException

Exception for input validation errors.

public class JedisValidationException extends JedisException {
    /**
     * Creates validation exception
     * @param message Validation error message
     */
    public JedisValidationException(String message);
}

InvalidURIException

Exception for malformed Redis connection URIs.

public class InvalidURIException extends JedisException {
    /**
     * Creates invalid URI exception
     * @param message URI error message
     */
    public InvalidURIException(String message);
    
    /**
     * Creates invalid URI exception with cause
     * @param message Error message
     * @param cause URI parsing exception
     */
    public InvalidURIException(String message, Throwable cause);
}

Cache and Broadcast Exceptions

JedisCacheException

Exception for client-side cache operations.

public class JedisCacheException extends JedisException {
    /**
     * Creates cache exception
     * @param message Cache error message
     */
    public JedisCacheException(String message);
    
    /**
     * Creates cache exception with cause
     * @param message Error message
     * @param cause Cache operation cause
     */
    public JedisCacheException(String message, Throwable cause);
}

JedisBroadcastException

Exception for broadcast operations across multiple Redis instances.

public class JedisBroadcastException extends JedisException {
    /**
     * Creates broadcast exception
     * @param message Broadcast error message
     */
    public JedisBroadcastException(String message);
    
    /**
     * Creates broadcast exception with partial results
     * @param message Error message
     * @param cause Exception cause
     */
    public JedisBroadcastException(String message, Throwable cause);
}

Exception Handling Best Practices

Comprehensive Error Handler

public class RobustJedisClient {
    private static final Logger logger = LoggerFactory.getLogger(RobustJedisClient.class);
    private final JedisPool pool;
    private final int maxRetries;
    
    public RobustJedisClient(JedisPool pool, int maxRetries) {
        this.pool = pool;
        this.maxRetries = maxRetries;
    }
    
    public <T> T executeWithRetry(Function<Jedis, T> operation) {
        int attempts = 0;
        JedisException lastException = null;
        
        while (attempts < maxRetries) {
            try (Jedis jedis = pool.getResource()) {
                return operation.apply(jedis);
                
            } catch (JedisConnectionException e) {
                lastException = e;
                attempts++;
                
                logger.warn("Connection attempt {} failed: {}", attempts, e.getMessage());
                
                if (attempts < maxRetries) {
                    waitBeforeRetry(attempts);
                }
                
            } catch (JedisBusyException e) {
                lastException = e;
                attempts++;
                
                logger.warn("Server busy on attempt {}: {}", attempts, e.getMessage());
                
                if (attempts < maxRetries) {
                    waitBeforeRetry(attempts);
                }
                
            } catch (JedisDataException e) {
                // Data exceptions usually don't benefit from retry
                logger.error("Data operation failed: {}", e.getMessage());
                throw e;
                
            } catch (JedisAuthenticationException e) {
                // Authentication errors shouldn't be retried
                logger.error("Authentication failed: {}", e.getMessage());
                throw e;
                
            } catch (JedisValidationException e) {
                // Validation errors shouldn't be retried
                logger.error("Validation failed: {}", e.getMessage());
                throw e;
                
            } catch (JedisException e) {
                // Generic Jedis exception
                lastException = e;
                attempts++;
                
                logger.warn("Operation attempt {} failed: {}", attempts, e.getMessage());
                
                if (attempts < maxRetries) {
                    waitBeforeRetry(attempts);
                }
            }
        }
        
        logger.error("All {} retry attempts failed", maxRetries);
        throw new JedisException("Operation failed after " + maxRetries + " attempts", 
                                lastException);
    }
    
    private void waitBeforeRetry(int attempt) {
        try {
            // Exponential backoff with jitter
            long delay = Math.min(1000 * (1L << attempt), 10000); // Max 10 seconds
            long jitter = (long) (Math.random() * 1000); // Up to 1 second jitter
            
            Thread.sleep(delay + jitter);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new JedisException("Retry interrupted", e);
        }
    }
}

Circuit Breaker Pattern

public class CircuitBreakerJedisClient {
    private enum State { CLOSED, OPEN, HALF_OPEN }
    
    private volatile State state = State.CLOSED;
    private volatile int failures = 0;
    private volatile long lastFailureTime = 0;
    private final int failureThreshold;
    private final long timeout;
    
    public CircuitBreakerJedisClient(int failureThreshold, long timeout) {
        this.failureThreshold = failureThreshold;
        this.timeout = timeout;
    }
    
    public <T> T execute(Function<Jedis, T> operation) {
        if (state == State.OPEN) {
            if (System.currentTimeMillis() - lastFailureTime > timeout) {
                state = State.HALF_OPEN;
            } else {
                throw new JedisException("Circuit breaker is OPEN");
            }
        }
        
        try (Jedis jedis = getJedis()) {
            T result = operation.apply(jedis);
            onSuccess();
            return result;
            
        } catch (JedisException e) {
            onFailure();
            throw e;
        }
    }
    
    private synchronized void onSuccess() {
        failures = 0;
        state = State.CLOSED;
    }
    
    private synchronized void onFailure() {
        failures++;
        lastFailureTime = System.currentTimeMillis();
        
        if (failures >= failureThreshold) {
            state = State.OPEN;
        }
    }
    
    private Jedis getJedis() {
        // Get Jedis instance from pool or create new one
        return new Jedis("localhost", 6379);
    }
}

Exception Logging and Monitoring

public class MonitoredJedisClient {
    private static final Logger logger = LoggerFactory.getLogger(MonitoredJedisClient.class);
    private final MeterRegistry meterRegistry;
    
    public MonitoredJedisClient(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    public <T> T executeWithMonitoring(String operationName, Function<Jedis, T> operation) {
        Timer.Sample sample = Timer.start(meterRegistry);
        
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            T result = operation.apply(jedis);
            
            meterRegistry.counter("jedis.operations.success", "operation", operationName)
                        .increment();
            
            return result;
            
        } catch (JedisConnectionException e) {
            logAndCount("connection_error", operationName, e);
            throw e;
            
        } catch (JedisClusterException e) {
            logAndCount("cluster_error", operationName, e);
            throw e;
            
        } catch (JedisAuthenticationException e) {
            logAndCount("auth_error", operationName, e);
            throw e;
            
        } catch (JedisDataException e) {
            logAndCount("data_error", operationName, e);
            throw e;
            
        } catch (JedisException e) {
            logAndCount("generic_error", operationName, e);
            throw e;
            
        } finally {
            sample.stop(Timer.builder("jedis.operations.duration")
                           .tag("operation", operationName)
                           .register(meterRegistry));
        }
    }
    
    private void logAndCount(String errorType, String operationName, JedisException e) {
        logger.error("Jedis {} error in operation {}: {}", 
                    errorType, operationName, e.getMessage(), e);
        
        meterRegistry.counter("jedis.operations.error", 
                            "operation", operationName,
                            "error_type", errorType)
                    .increment();
    }
}

The Jedis exception hierarchy provides comprehensive error handling capabilities for all Redis deployment scenarios. Proper exception handling is crucial for building resilient applications that can gracefully handle network issues, cluster topology changes, authentication problems, and server errors.

Install with Tessl CLI

npx tessl i tessl/maven-redis-clients--jedis

docs

authentication.md

client-side-caching.md

clustering.md

commands-operations.md

connection-management.md

core-clients.md

exceptions.md

index.md

modules.md

parameters.md

pubsub.md

transactions-pipelining.md

tile.json