Skip to content

Commit

Permalink
Add "Production usage" section to Jedis quick start (#2636)
Browse files Browse the repository at this point in the history
  • Loading branch information
uglide authored Jan 22, 2024
1 parent 59db4e3 commit 68992d7
Showing 1 changed file with 92 additions and 72 deletions.
164 changes: 92 additions & 72 deletions docs/connect/clients/java.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,104 +187,124 @@ public class Main {
}
```

### Example: Indexing and querying JSON documents
### Production usage

Make sure that you have Redis Stack and `Jedis` installed.
### Configuring Connection pool
As mentioned in the previous section, use `JedisPool` or `JedisPooled` to create a connection pool.
`JedisPooled`, added in Jedis version 4.0.0, provides capabilities similar to `JedisPool` but with a more straightforward API.
A connection pool holds a specified number of connections, creates more connections when necessary, and terminates them when they are no longer needed.

Import dependencies and add a sample `User` class:
Here is a simplified connection lifecycle in a pool:

```java
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.search.*;
import redis.clients.jedis.search.aggr.*;
import redis.clients.jedis.search.schemafields.*;

class User {
private String name;
private String email;
private int age;
private String city;

public User(String name, String email, int age, String city) {
this.name = name;
this.email = email;
this.age = age;
this.city = city;
}

//...
}
```
1. A connection is requested from the pool.
2. A connection is served:
- An idle connection is served when non-active connections are available, or
- A new connection is created when the number of connections is under `maxTotal`.
3. The connection becomes active.
4. The connection is released back to the pool.
5. The connection is marked as stale.
6. The connection is kept idle for `minEvictableIdleTime`.
7. The connection becomes evictable if the number of connections is greater than `minIdle`.
8. The connection is ready to be closed.

Connect to your Redis database with `JedisPooled`.
It's important to configure the connection pool correctly.
Use `GenericObjectPoolConfig` from [Apache Commons Pool2](https://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool2/impl/GenericObjectPoolConfig.html).

```java
JedisPooled jedis = new JedisPooled("localhost", 6379);
ConnectionPoolConfig poolConfig = new ConnectionPoolConfig();
// maximum active connections in the pool,
// tune this according to your needs and application type
// default is 8
poolConfig.setMaxTotal(8);

// maximum idle connections in the pool, default is 8
poolConfig.setMaxIdle(8);
// minimum idle connections in the pool, default 0
poolConfig.setMinIdle(0);

// Enables waiting for a connection to become available.
poolConfig.setBlockWhenExhausted(true);
// The maximum number of seconds to wait for a connection to become available
poolConfig.setMaxWait(Duration.ofSeconds(1));

// Enables sending a PING command periodically while the connection is idle.
poolConfig.setTestWhileIdle(true);
// controls the period between checks for idle connections in the pool
poolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1));

// JedisPooled does all hard work on fetching and releasing connection to the pool
// to prevent connection starvation
JedisPooled jedis = new JedisPooled(poolConfig, "localhost", 6379);
```

Let's create some test data to add to your database.

```java
User user1 = new User("Paul John", "[email protected]", 42, "London");
User user2 = new User("Eden Zamir", "[email protected]", 29, "Tel Aviv");
User user3 = new User("Paul Zamir", "[email protected]", 35, "Tel Aviv");
```
### Timeout

Create an index. In this example, all JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax](/docs/interact/search-and-query/query/).
To set a timeout for a connection, use the `JedisPooled` or `JedisPool` constructor with the `timeout` parameter, or use `JedisClientConfig` with the `socketTimeout` and `connectionTimeout` parameters:

```java
jedis.ftCreate("idx:users",
FTCreateParams.createParams()
.on(IndexDataType.JSON)
.addPrefix("user:"),
TextField.of("$.name").as("name"),
TagField.of("$.city").as("city"),
NumericField.of("$.age").as("age")
HostAndPort hostAndPort = new HostAndPort("localhost", 6379);

JedisPooled jedisWithTimeout = new JedisPooled(hostAndPort,
DefaultJedisClientConfig.builder()
.socketTimeoutMillis(5000) // set timeout to 5 seconds
.connectionTimeoutMillis(5000) // set connection timeout to 5 seconds
.build(),
poolConfig
);
```

Use `JSON.SET` to set each user value at the specified path.
### Exception handling
The Jedis Exception Hierarchy is rooted on `JedisException`, which implements `RuntimeException`, and are therefore all unchecked exceptions.

```java
jedis.jsonSetWithEscape("user:1", user1);
jedis.jsonSetWithEscape("user:2", user2);
jedis.jsonSetWithEscape("user:3", user3);
```
JedisException
├── JedisDataException
│ ├── JedisRedirectionException
│ │ ├── JedisMovedDataException
│ │ └── JedisAskDataException
│ ├── AbortedTransactionException
│ ├── JedisAccessControlException
│ └── JedisNoScriptException
├── JedisClusterException
│ ├── JedisClusterOperationException
│ ├── JedisConnectionException
│ └── JedisValidationException
└── InvalidURIException
```

Let's find user `Paul` and filter the results by age.
#### General Exceptions
In general, Jedis can throw the following exceptions while executing commands:

```java
var query = new Query("Paul @age:[30 40]");
var result = jedis.ftSearch("idx:users", query).getDocuments();
System.out.println(result);
// Prints: [id:user:3, score: 1.0, payload:null, properties:[$={"name":"Paul Zamir","email":"[email protected]","age":35,"city":"Tel Aviv"}]]
```
- `JedisConnectionException` - when the connection to Redis is lost or closed unexpectedly. Configure failover to handle this exception automatically with Resilience4J and the built-in Jedis failover mechanism.
- `JedisAccessControlException` - when the user does not have the permission to execute the command or the user ID and/or password are incorrect.
- `JedisDataException` - when there is a problem with the data being sent to or received from the Redis server. Usually, the error message will contain more information about the failed command.
- `JedisException` - this exception is a catch-all exception that can be thrown for any other unexpected errors.

Return only the `city` field.
Conditions when `JedisException` can be thrown:
- Bad return from a health check with the `PING` command
- Failure during SHUTDOWN
- Pub/Sub failure when issuing commands (disconnect)
- Any unknown server messages
- Sentinel: can connect to sentinel but master is not monitored or all Sentinels are down.
- MULTI or DISCARD command failed
- Shard commands key hash check failed or no Reachable Shards
- Retry deadline exceeded/number of attempts (Retry Command Executor)
- POOL - pool exhausted, error adding idle objects, returning broken resources to the pool

```java
var city_query = new Query("Paul @age:[30 40]");
var city_result = jedis.ftSearch("idx:users", city_query.returnFields("city")).getDocuments();
System.out.println(city_result);
// Prints: [id:user:3, score: 1.0, payload:null, properties:[city=Tel Aviv]]
```
All the Jedis exceptions are runtime exceptions and in most cases irrecoverable, so in general bubble up to the API capturing the error message.

Count all users in the same city.
## DNS cache and Redis

```java
AggregationBuilder ab = new AggregationBuilder("*")
.groupBy("@city", Reducers.count().as("count"));
AggregationResult ar = jedis.ftAggregate("idx:users", ab);
When you connect to a Redis with multiple endpoints, such as [Redis Enterprise Active-Active](https://redis.com/redis-enterprise/technology/active-active-geo-distribution/), it's recommended to disable the JVM's DNS cache to load-balance requests across multiple endpoints.

for (int idx=0; idx < ar.getTotalResults(); idx++) {
System.out.println(ar.getRow(idx).getString("city") + " - " + ar.getRow(idx).getString("count"));
}
// Prints:
// London - 1
// Tel Aviv - 2
You can do this in your application's code with the following snippet:
```java
java.security.Security.setProperty("networkaddress.cache.ttl","0");
java.security.Security.setProperty("networkaddress.cache.negative.ttl", "0");
```

### Learn more

* [Jedis API reference](https://www.javadoc.io/doc/redis.clients/jedis/latest/index.html)
* [Failover with Jedis](https://github.com/redis/jedis/blob/master/docs/failover.md)
* [GitHub](https://github.com/redis/jedis)

0 comments on commit 68992d7

Please sign in to comment.