diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index b3538290ee..e1bb6dcefa 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -33,6 +33,8 @@ Set up test environments with ```make start```, tear down those environments wit
* You can import code style file (located to hbase-formatter.xml) to Eclipse, IntelliJ
* line break by column count seems not working with IntelliJ
* You can run ```make format``` anytime to reformat without IDEs
+* DO NOT format the source codes within `io.redis.examples` test package.
+* A test class name MUST NOT end with `Example`.
## Adding commands
diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE
index 25318670b1..ef15b4a903 100644
--- a/.github/ISSUE_TEMPLATE
+++ b/.github/ISSUE_TEMPLATE
@@ -1,3 +1,12 @@
+
+
### Expected behavior
Write here what you're expecting ...
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..069e8c5a55
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,7 @@
+version: 2
+
+updates:
+ - package-ecosystem: "maven"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/release-drafter-config.yml b/.github/release-drafter-config.yml
index c0e1399db6..edc2911f43 100644
--- a/.github/release-drafter-config.yml
+++ b/.github/release-drafter-config.yml
@@ -1,23 +1,26 @@
-name-template: '$NEXT_MAJOR_VERSION'
-tag-template: 'v$NEXT_MAJOR_VERSION'
+name-template: '$NEXT_MINOR_VERSION'
+tag-template: 'v$NEXT_MINOR_VERSION'
autolabeler:
- - label: 'chore'
+ - label: 'maintenance'
files:
- '*.md'
- '.github/*'
- label: 'bug'
branch:
- '/bug-.+'
- - label: 'chore'
+ - label: 'maintenance'
branch:
- - '/chore-.+'
+ - '/maintenance-.+'
- label: 'feature'
branch:
- '/feature-.+'
categories:
- - title: 'Breaking Changes'
+ - title: '๐ฅ Breaking Changes'
labels:
- 'breakingchange'
+ - title: '๐งช Experimental Features'
+ labels:
+ - 'experimental'
- title: '๐ New Features'
labels:
- 'feature'
@@ -29,12 +32,15 @@ categories:
- 'bug'
- 'BUG'
- title: '๐งฐ Maintenance'
- label: 'chore'
+ labels:
+ - 'maintenance'
+ - 'dependencies'
+ - 'testing'
change-template: '- $TITLE (#$NUMBER)'
exclude-labels:
- 'skip-changelog'
template: |
- ## Changes
+ # Changes
$CHANGES
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index fd7c46dc80..3f472ee7cc 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -24,24 +24,20 @@ jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'java' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
- # Learn more:
- # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
+ permissions:
+ security-events: write
+ actions: read
+ contents: read
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@v2
with:
- languages: ${{ matrix.language }}
+ languages: java
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
@@ -50,7 +46,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v1
+ uses: github/codeql-action/autobuild@v2
# โน๏ธ Command-line programs to run using the OS shell.
# ๐ https://git.io/JvXDl
@@ -64,4 +60,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/doctests.yml b/.github/workflows/doctests.yml
new file mode 100644
index 0000000000..daa8858b89
--- /dev/null
+++ b/.github/workflows/doctests.yml
@@ -0,0 +1,37 @@
+name: Documentation Tests
+
+on:
+ push:
+ tags-ignore:
+ - '*'
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ doctests:
+ runs-on: ubuntu-latest
+ services:
+ redis-stack:
+ image: redis/redis-stack-server:latest
+ options: >-
+ --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
+ ports:
+ - 6379:6379
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Cache dependencies
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.m2/repository
+ /var/cache/apt
+ key: jedis-${{hashFiles('**/pom.xml')}}
+ - name: Set up Java
+ uses: actions/setup-java@v2
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ - name: Run doctests
+ run: |
+ mvn -Pdoctests test
diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
index c192d0c122..84da5938f6 100644
--- a/.github/workflows/integration.yml
+++ b/.github/workflows/integration.yml
@@ -10,11 +10,16 @@ on:
- '**/*.rst'
branches:
- master
- - '[0-9].[0-9]'
+ - '[0-9].x'
+ - '[0-9].[0-9].x'
pull_request:
branches:
- master
- - '[0-9].[0-9]'
+ - '[0-9].x'
+ - '[0-9].[0-9].x'
+ schedule:
+ - cron: '0 1 * * *' # nightly build
+ workflow_dispatch:
jobs:
@@ -28,10 +33,10 @@ jobs:
with:
java-version: '8'
distribution: 'temurin'
- - name: stunnel
+ - name: System setup
run: |
- sudo apt-get update
- sudo apt-get install -y stunnel make
+ sudo apt update
+ sudo apt install -y stunnel make
make system-setup
- name: Cache dependencies
uses: actions/cache@v2
@@ -40,19 +45,36 @@ jobs:
~/.m2/repository
/var/cache/apt
key: jedis-${{hashFiles('**/pom.xml')}}
- - name: mvn offline
+ - name: Maven offline
run: |
mvn -q dependency:go-offline
- - name: run tests
+ - name: Build docs
+ run: |
+ mvn javadoc:jar
+ - name: Run tests
run: |
TEST="" make test
env:
JVM_OPTS: -Xmx3200m
TERM: dumb
- - name: redismod docker
- run: docker run --name mod -p 52567:6379 -d redislabs/redismod:edge
- - name: run tests
- run: mvn -q -DmodulesDocker="mod:52567" -Dtest="redis.clients.jedis.modules.**" test
- - name: codecov
+ - name: sleep 10s
+ run: sleep 10s
+ - name: Make - start
+ run: |
+ make start
+ sleep 2s
+ - name: Docker - mod or stack
+ run: docker run -p 52567:6379 -d redis/redis-stack-server:edge
+ - name: Test commands - default protocol
+ run: mvn -Dtest="redis.clients.jedis.commands.**" test
+ - name: Test commands - RESP3 protocol
+ run: mvn -DjedisProtocol=3 -Dtest="redis.clients.jedis.commands.**" test
+ - name: Test module commands - default protocol
+ run: mvn -DmodulesDocker="localhost:52567" -Dtest="redis.clients.jedis.modules.**" test
+ - name: Test module commands - RESP3 protocol
+ run: mvn -DjedisProtocol=3 -DmodulesDocker="localhost:52567" -Dtest="redis.clients.jedis.modules.**" test
+ - name: Make - stop
+ run: make stop
+ - name: Codecov
run: |
bash <(curl -s https://codecov.io/bash)
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
index 7ffb64166c..8a409b8331 100644
--- a/.github/workflows/snapshot.yml
+++ b/.github/workflows/snapshot.yml
@@ -3,12 +3,11 @@
name: Publish Snapshot
on:
- workflow_run:
- workflows: [Integration]
- types: [completed]
+ push:
branches:
- master
- - '[0-9].[0-9]'
+ - '[0-9].x'
+ workflow_dispatch:
jobs:
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000..15c4dd523a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021-2023, Redis, inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/LICENSE.txt b/LICENSE.txt
deleted file mode 100644
index 7b8b1cee63..0000000000
--- a/LICENSE.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (c) 2010 Jonathan Leibiusky
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 481a421ed0..2987ad80dd 100644
--- a/Makefile
+++ b/Makefile
@@ -464,10 +464,7 @@ release:
make stop
system-setup:
- sudo apt-get install -y gcc-8 g++-8
- cd /usr/bin ;\
- sudo ln -sf gcc-8 gcc ;\
- sudo ln -sf g++-8 g++
+ sudo apt install -y gcc g++
[ ! -e redis-git ] && git clone https://github.com/redis/redis.git --branch unstable --single-branch redis-git || true
$(MAKE) -C redis-git clean
$(MAKE) -C redis-git
diff --git a/README.md b/README.md
index 4cb96c704c..2bea11911a 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,6 @@
[![Javadocs](https://www.javadoc.io/badge/redis.clients/jedis.svg)](https://www.javadoc.io/doc/redis.clients/jedis)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE.txt)
[![Integration](https://github.com/redis/jedis/actions/workflows/integration.yml/badge.svg?branch=master)](https://github.com/redis/jedis/actions/workflows/integration.yml)
-[![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/redis/jedis.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/redis/jedis/context:java)
[![codecov](https://codecov.io/gh/redis/jedis/branch/master/graph/badge.svg?token=pAstxAAjYo)](https://codecov.io/gh/redis/jedis)
[![Discord](https://img.shields.io/discord/697882427875393627?style=flat-square)](https://discord.gg/qRhBuY8Z)
@@ -13,14 +12,39 @@
Jedis is a Java client for [Redis](https://github.com/redis/redis "Redis") designed for performance and ease of use.
-## Contributing
+Are you looking for a high-level library to handle object mapping? See [redis-om-spring](https://github.com/redis/redis-om-spring)!
-We'd love your contributions!
+## How do I Redis?
-**Bug reports** are always welcome! [You can open a bug report on GitHub](https://github.com/redis/jedis/issues/new).
+[Learn for free at Redis University](https://university.redis.com/)
-You can also **contribute documentation** -- or anything to improve Jedis. Please see
-[contribution guideline](https://github.com/redis/jedis/blob/master/.github/CONTRIBUTING.md) for more details.
+[Build faster with the Redis Launchpad](https://launchpad.redis.com/)
+
+[Try the Redis Cloud](https://redis.com/try-free/)
+
+[Dive in developer tutorials](https://developer.redis.com/)
+
+[Join the Redis community](https://redis.com/community/)
+
+[Work at Redis](https://redis.com/company/careers/jobs/)
+
+## Supported Redis versions
+
+The most recent version of this library supports redis version
+[5.0](https://github.com/redis/redis/blob/5.0/00-RELEASENOTES),
+[6.0](https://github.com/redis/redis/blob/6.0/00-RELEASENOTES),
+[6.2](https://github.com/redis/redis/blob/6.2/00-RELEASENOTES),
+[7.0](https://github.com/redis/redis/blob/7.0/00-RELEASENOTES) and
+[7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES).
+
+The table below highlights version compatibility of the most-recent library versions and Redis versions. Compatibility means communication features, and Redis command capabilities.
+
+
+| Jedis version | Supported Redis versions | JDK Compatibility |
+|---------------|--------------------------------|-------------------|
+| 3.9+ | 5.0 and 6.2 Family of releases | 8, 11 |
+| >= 4.0 | Version 5.0 to current | 8, 11, 17 |
+| >= 5.0 | Version 6.0 to current | 8, 11, 17 |
## Getting started
@@ -30,11 +54,19 @@ To get started with Jedis, first add it as a dependency in your Java project. If
redis.clientsjedis
- 4.2.0
+ 5.0.0
```
-Next, you'll need to connect to Redis. For many applications, it's best to use a connection pool. You can instantiate a Jedis connection pool like so:
+To use the cutting-edge Jedis, check [here](/docs/jedis-maven.md).
+
+Next, you'll need to connect to Redis. Consider installing a redis-stack docker:
+
+```bash
+docker run -p 6379:6379 -it redis/redis-stack:latest
+```
+
+For many applications, it's best to use a connection pool. You can instantiate a Jedis connection pool like so:
```java
JedisPool pool = new JedisPool("localhost", 6379);
@@ -58,7 +90,7 @@ for the complete list of supported commands.
### Easier way of using connection pool
-Using a *try-with-resources* block for each command may be cumbursome, so you may consider using JedisPooled.
+Using a *try-with-resources* block for each command may be cumbersome, so you may consider using JedisPooled.
```java
JedisPooled jedis = new JedisPooled("localhost", 6379);
@@ -90,29 +122,49 @@ jedis.sadd("planets", "Mars");
## Using Redis modules
-Jedis provides support for some of the [Redis modules](https://redis.io/docs/modules/), most notably
+Jedis includes support for [Redis modules](https://redis.io/docs/modules/) such as
[RedisJSON](https://oss.redis.com/redisjson/) and [RediSearch](https://oss.redis.com/redisearch/).
See the [RedisJSON Jedis](docs/redisjson.md) or [RediSearch Jedis](docs/redisearch.md) for details.
+## Failover
+
+Jedis supports retry and failover for your Redis deployments. This is useful when:
+
+1. You have more than one Redis deployment. This might include two independent Redis servers or two or more Redis databases replicated across multiple [active-active Redis Enterprise](https://docs.redis.com/latest/rs/databases/active-active/) clusters.
+2. You want your application to connect to one deployment at a time and to fail over to the next available deployment if the first deployment becomes unavailable.
+
+For the complete failover configuration options and examples, see the [Jedis failover docs](docs/failover.md).
+
## Documentation
The [Jedis wiki](http://github.com/redis/jedis/wiki) contains several useful articles for using Jedis.
You can also check the [latest Jedis Javadocs](https://www.javadoc.io/doc/redis.clients/jedis/latest/index.html).
+Some specific use-case examples can be found in [`redis.clients.jedis.examples`
+package](src/test/java/redis/clients/jedis/examples/) of the test source codes.
+
## Troubleshooting
If you run into trouble or have any questions, we're here to help!
-Hit us up on the [Redis Discord Server](http://discord.gg/redis) or [open an issue on GitHub](https://github.com/redis/jedis).
+Hit us up on the [Redis Discord Server](http://discord.gg/redis) or
+[Jedis GitHub Discussions](https://github.com/redis/jedis/discussions) or
+[Jedis mailing list](http://groups.google.com/group/jedis_redis).
-You can also find help on the [Jedis mailing list](http://groups.google.com/group/jedis_redis) or the
-[GitHub Discussions](https://github.com/redis/jedis/discussions).
+## Contributing
+
+We'd love your contributions!
+
+Bug reports are always welcome! [You can open a bug report on GitHub](https://github.com/redis/jedis/issues/new).
+
+You can also contribute documentation -- or anything to improve Jedis. Please see
+[contribution guideline](https://github.com/redis/jedis/blob/master/.github/CONTRIBUTING.md) for more details.
## License
-Jedis is licensed under the [MIT license](https://github.com/redis/jedis/blob/master/LICENSE.txt).
+Jedis is licensed under the [MIT license](https://github.com/redis/jedis/blob/master/LICENSE).
## Sponsorship
diff --git a/docs/3to4-primitives.md b/docs/3to4-primitives.md
index ebda28ee25..76049f34ea 100644
--- a/docs/3to4-primitives.md
+++ b/docs/3to4-primitives.md
@@ -1,5 +1,5 @@
-## The following methods now return primitive values (`long`/`boolean`/`double`
-instead of `Long`/`Boolean`/`Double`):
+## The following methods now return primitive values:
+\>> `long`/`boolean`/`double` instead of `Long`/`Boolean`/`Double`:
- dbSize()
- lastsave()
diff --git a/docs/3to4.md b/docs/3to4.md
index 58a8247531..af1df0158e 100644
--- a/docs/3to4.md
+++ b/docs/3to4.md
@@ -1,4 +1,4 @@
-# Jedis 3 to Jedis 4 Breaking Changes
+# Jedis 4 Breaking Changes
- The `BinaryJedis` and `BinaryJedisCluster` classes have been removed.
diff --git a/docs/breaking-5.md b/docs/breaking-5.md
new file mode 100644
index 0000000000..4a013c3800
--- /dev/null
+++ b/docs/breaking-5.md
@@ -0,0 +1,161 @@
+# Jedis 5 Breaking Changes
+
+- All variants of `blmpop` and `bzmpop` methods now take `double timeout` parameter instead of `long timeout` parameter.
+ This is breaking ONLY IF you are using `Long` for timeout.
+
+- `Reducer` abstract class is refactored:
+ - **`Reducer(String field)` constructor is removed; `Reducer(String name, String field)` constructor is added.**
+ - **`Reducer(String name)` constructor is added; it will cause runtime error with older `Reducer(String field)` constructor.**
+ - `getName` method is removed.
+ - `getAlias` method is removed.
+ - `setAlias` method is removed; use `as` method.
+ - `setAliasAsField` method is removed.
+ - `getOwnArgs` method is now abstract.
+ - `getArgs` method is removed.
+
+- `quit()` method has been removed from `Connection` and `ServerCommands` interface and implementations.
+
+- `updatePassword(String password)` method has been removed from `JedisClientConfig` and implementations.
+
+- `setPassword(String password)` method has been removed from both `JedisFactory` and `ConnectionFactory` classes.
+
+- Both `bzpopmax(double timeout, String... keys)` and `bzpopmin(double timeout, String... keys)` now return `KeyValue` (instead of `KeyedZSetElement`).
+
+- Both `bzpopmax(double timeout, byte[]... keys)` and `bzpopmin(double timeout, byte[]... keys)` now return `KeyValue` (instead of `List`).
+
+- Following methods now return `KeyValue` instead of `KeyedListElement`:
+ - `blpop(double timeout, String key)`
+ - `blpop(double timeout, String... keys)`
+ - `brpop(double timeout, String key)`
+ - `brpop(double timeout, String... keys)`
+
+- Following methods now return `KeyValue` instead of `List`:
+ - `blpop(double timeout, byte[]... keys)`
+ - `brpop(double timeout, byte[]... keys)`
+
+- `zdiff(String... keys)` method now returns `List` (instead of `Set`).
+- `zdiff(byte[]... keys)` method now returns `List` (instead of `Set`).
+- Both `zdiffWithScores(String... keys)` and `zdiffWithScores(byte[]... keys)` methods now return `List` (instead of `Set`).
+
+- `zinter(ZParams params, String... keys)` method now returns `List` (instead of `Set`).
+- `zinter(ZParams params, byte[]... keys)` method now returns `List` (instead of `Set`).
+- Both `zinterWithScores(ZParams params, String... keys)` and `zinterWithScores(ZParams params, byte[]... keys)` methods now return `List` (instead of `Set`).
+
+- `zunion(ZParams params, String... keys)` method now returns `List` (instead of `Set`).
+- `zunion(ZParams params, byte[]... keys)` method now returns `List` (instead of `Set`).
+- Both `zunionWithScores(ZParams params, String... keys)` and `zunionWithScores(ZParams params, byte[]... keys)` methods now return `List` (instead of `Set`).
+
+- Both `configGet(String pattern)` and `configGet(String... patterns)` methods now return `Map` instead of `List`.
+- Both `configGet(byte[] pattern)` and `configGet(byte[]... patterns)` methods now return `Map` instead of `List`.
+
+- New `aclDelUser(String... names)` method replaces `aclDelUser(String name)` and `aclDelUser(String name, String... names)` methods.
+- New `aclDelUser(byte[]... names)` method replaces `aclDelUser(byte[] name)` and `aclDelUser(byte[] name, byte[]... names)` methods.
+
+- `tsMGet(TSMGetParams multiGetParams, String... filters)` method now returns `Map` instead of `List>`.
+
+- Following methods now return `Map` instead of `List`:
+ - `tsMRange(long fromTimestamp, long toTimestamp, String... filters)`
+ - `tsMRange(TSMRangeParams multiRangeParams)`
+ - `tsMRevRange(long fromTimestamp, long toTimestamp, String... filters)`
+ - `tsMRevRange(TSMRangeParams multiRangeParams)`
+
+- `jsonNumIncrBy(String key, Path2 path, double value)` method now returns `Object` instead of `JSONArray`.
+ - The returning object would still be JSONArray for all previous cases. So simple type casting is enough to handle this change.
+ - The returning object will be `List` when running under RESP3 protocol.
+
+- `getAgeSeconds()` in `AccessControlLogEntry` now returns `Double` instead of `String`.
+
+- Both `ftConfigGet(String option)` and `ftConfigGet(String indexName, String option)` methods now return `Map` instead of `Map`.
+
+- `ftList()` method now returns `Set` instead of `List`.
+
+- `graphSlowlog(String graphName)` now returns `List>` (instead of `List>`).
+
+- `CommandListFilterByParams` now throws `IllegalArgumentException` (instead of `JedisDataException`) in case of unfulfilling filter.
+
+- `FailoverParams` now throws `IllegalArgumentException` (instead of `IllegalStateException`) in case of unfulfilling optional arguments.
+
+- `XPendingParams` now throws `IllegalArgumentException` (instead of `IllegalStateException`) in case of unfulfilling optional arguments.
+
+- `get()` option has been removed from `SetParams`. Following methods have been added in Jedis/UnifiedJedis for convenience:
+ - `setGet(String key, String value)` method has been added in `StringCommands` interface.
+ - `setGet(byte[] key, byte[] value)` method has been added in `StringBinaryCommands` interface.
+
+- `xpending(String key, String groupName, StreamEntryID start, StreamEntryID end, int count, String consumerName)` method has been removed from everywhere.
+ - Use `xpending(java.lang.String, java.lang.String, redis.clients.jedis.params.XPendingParams)` instead.
+
+- `xpending(byte[] key, byte[] groupName, byte[] start, byte[] end, int count, byte[] consumerName)` method has been removed from everywhere.
+ - Use `xpending(byte[], byte[], redis.clients.jedis.params.XPendingParams)` instead.
+
+- `retentionTime(long retentionTime)` method in `TSAlterParams` has been removed. Use `retention(long)` method instead.
+
+- Following classes have been removed:
+ - `KeyedZSetElement`
+ - `KeyedListElement`
+ - `TSKeyValue`
+ - `TSKeyedElements`
+ - `Limit`
+
+- Following BuilderFactory implementations have been removed:
+ - `BYTE_ARRAY` (use `BINARY`)
+ - `BYTE_ARRAY_LIST` (use `BINARY_LIST`)
+ - `BINARY_MAP_FROM_PAIRS`
+ - `STRING_ORDERED_SET`
+
+- All _payload_ related parameters are removed from _search_ related classes; namely `Document`, `IndexDefinition`, `Query`.
+
+- `topkCount(String key, String... items)` method has been removed from everywhere.
+
+- Following methods supporting JSON.RESP command have been removed:
+ - `jsonResp(String key)`
+ - `jsonResp(String key, Path path)`
+ - `jsonResp(String key, Path2 path)`
+
+- `RedisJsonCommands` and `RedisJsonPipelineCommands` interfaces have been moved into `redis.clients.jedis.json.commands` package.
+
+- `AbortedTransactionException` is removed.
+
+- `Queable` class is removed.
+
+- `Params` abstract class is removed.
+ - `toString()` support used by its sub-classes is now unavailable.
+
+- `getParams()` method is removed from `SortingParams` class.
+
+- Both `SEARCH_AGGREGATION_RESULT` and `SEARCH_AGGREGATION_RESULT_WITH_CURSOR` implementations from `SearchBuilderFactory` class have been moved to `AggregationResult` class.
+
+- All `AggregationResult` constructors have been made `private`.
+
+- `getArgs()`, `getArgsString()` and `serializeRedisArgs(List redisArgs)` methods have been removed from `AggregationBuilder`.
+
+- `totalResults` variable in `AggregationResult` has been made private. Use `getTotalResults()` method instead.
+
+- `getArgs()` and `limit(Limit limit)` methods have been removed from `Group` class.
+
+- `addCommandEncodedArguments` and `addCommandBinaryArguments` methods have been removed from `FieldName` class.
+
+- `addObjects(int[] ints)` method has been removed from `CommandArguments`.
+
+- Following methods have been removed:
+ - `strAlgoLCSStrings(String strA, String strB, StrAlgoLCSParams params)`
+ - `strAlgoLCSStrings(byte[] strA, byte[] strB, StrAlgoLCSParams params)`
+ - `strAlgoLCSKeys(String keyA, String keyB, StrAlgoLCSParams params)`
+ - `strAlgoLCSKeys(byte[] keyA, byte[] keyB, StrAlgoLCSParams params)`
+
+- `StrAlgoLCSParams` class has been removed.
+
+- Following methods have been removed from all Pipeline classes:
+ - `ftCursorRead(String indexName, long cursorId, int count)`
+ - `ftCursorDel(String indexName, long cursorId)`
+ - `ftDropIndex(String indexName)`
+ - `ftDropIndexDD(String indexName)`
+ - `ftAliasAdd(String aliasName, String indexName)`
+ - `ftAliasUpdate(String aliasName, String indexName)`
+ - `ftAliasDel(String aliasName)`
+
+- `JedisSentineled(String masterName, Set sentinels, JedisClientConfig masterClientConfig, JedisClientConfig sentinelClientConfig)` and
+`JedisSentineled(String masterName, Set sentinels, GenericObjectPoolConfig poolConfig, JedisClientConfig masterClientConfig, JedisClientConfig sentinelClientConfig)`
+constructors have been removed.
+
+- `JedisClusterInfoCache(JedisClientConfig clientConfig)` and `JedisClusterInfoCache(JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig)`
+constructors have been removed.
diff --git a/docs/failover.md b/docs/failover.md
new file mode 100644
index 0000000000..8414e41376
--- /dev/null
+++ b/docs/failover.md
@@ -0,0 +1,225 @@
+# Failover with Jedis
+
+Jedis supports failover for your Redis deployments. This is useful when:
+1. You have more than one Redis deployment. This might include two independent Redis servers or two or more Redis databases replicated across multiple [active-active Redis Enterprise](https://docs.redis.com/latest/rs/databases/active-active/) clusters.
+2. You want your application to connect to and use one deployment at a time.
+3. You want your application to fail over to the next available deployment if the current deployment becomes unavailable.
+
+Jedis will fail over to a subsequent Redis deployment after reaching a configurable failure threshold.
+This failure threshold is implemented using a [circuit breaker pattern](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern).
+
+You can also configure Jedis to retry failed calls to Redis.
+Once a maximum number of retries have been exhausted, the circuit breaker will record a failure.
+When the circuit breaker reaches its failure threshold, a failover will be triggered on the subsequent operation.
+
+The remainder of this guide describes:
+
+* A basic failover configuration
+* Supported retry and circuit breaker settings
+* Failback and the cluster selection API
+
+We recommend that you read this guide carefully and understand the configuration settings before enabling Jedis failover
+in production.
+
+## Basic usage
+
+To configure Jedis for failover, you specify an ordered list of Redis databases.
+By default, Jedis will connect to the first Redis database in the list. If the first database becomes unavailable,
+Jedis will attempt to connect to the next database in the list, and so on.
+
+Suppose you run two Redis deployments.
+We'll call them `redis-east` and `redis-west`.
+You want your application to first connect to `redis-east`.
+If `redis-east` becomes unavailable, you want your application to connect to `redis-west`.
+
+Let's look at one way of configuring Jedis for this scenario.
+
+First, create an array of `ClusterConfig` objects, one for each Redis database.
+
+```java
+JedisClientConfig config = DefaultJedisClientConfig.builder().user("cache").password("secret").build();
+
+ClusterConfig[] clientConfigs = new ClusterConfig[2];
+clientConfigs[0] = new ClusterConfig(new HostAndPort("redis-east.example.com", 14000), config);
+clientConfigs[1] = new ClusterConfig(new HostAndPort("redis-west.example.com", 14000), config);
+```
+
+The configuration above represents your two Redis deployments: `redis-east` and `redis-west`.
+You'll use this array of configuration objects to create a connection provider that supports failover.
+
+Use the `MultiClusterClientConfig` builder to set your preferred retry and failover configuration, passing in the client configs you just created.
+Then build a `MultiClusterPooledConnectionProvider`.
+
+```java
+MultiClusterClientConfig.Builder builder = new MultiClusterClientConfig.Builder(clientConfigs);
+builder.circuitBreakerSlidingWindowSize(10);
+builder.circuitBreakerSlidingWindowMinCalls(1);
+builder.circuitBreakerFailureRateThreshold(50.0f);
+
+MultiClusterPooledConnectionProvider provider = new MultiClusterPooledConnectionProvider(builder.build());
+```
+
+Internally, the connection provider uses a [highly configurable circuit breaker and retry implementation](https://resilience4j.readme.io/docs/circuitbreaker) to determine when to fail over.
+In the configuration here, we've set a sliding window size of 10 and a failure rate threshold of 50%.
+This means that a failover will be triggered if 5 out of any 10 calls to Redis fail.
+
+Once you've configured and created a `MultiClusterPooledConnectionProvider`, instantiate a `UnifiedJedis` instance for your application, passing in the provider you just created:
+
+```java
+UnifiedJedis jedis = new UnifiedJedis(provider);
+```
+
+You can now use this `UnifiedJedis` instance, and the connection management and failover will be handled transparently.
+
+## Configuration options
+
+Under the hood, Jedis' failover support relies on [resilience4j](https://resilience4j.readme.io/docs/getting-started),
+a fault-tolerance library that implements [retry](https://resilience4j.readme.io/docs/retry) and [circuit breakers](https://resilience4j.readme.io/docs/circuitbreaker).
+
+Once you configure Jedis for failover using the `MultiClusterPooledConnectionProvider`, each call to Redis is decorated with a resilience4j retry and circuit breaker.
+
+By default, any call that throws a `JedisConnectionException` will be retried up to 3 times.
+If the call continues to fail after the maximum number of retry attempts, then the circuit breaker will record a failure.
+
+The circuit breaker maintains a record of failures in a sliding window data structure.
+If the failure rate reaches a configured threshold (e.g., when 50% of the last 10 calls have failed),
+then the circuit breaker's state transitions from `CLOSED` to `OPEN`.
+When this occurs, Jedis will attempt to connect to the next Redis database in its client configuration list.
+
+The supported retry and circuit breaker settings, and their default values, are described below.
+You can configure any of these settings using the `MultiClusterClientConfig.Builder` builder.
+Refer the basic usage above for an example of this.
+
+### Retry configuration
+
+Jedis uses the following retry settings:
+
+| Setting | Default value | Description |
+|----------------------------------|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Max retry attempts | 3 | Maximum number of retry attempts (including the initial call) |
+| Retry wait duration | 500 ms | Number of milliseconds to wait between retry attempts |
+| Wait duration backoff multiplier | 2 | Exponential backoff factor multiplied against wait duration between retries. For example, with a wait duration of 1 second and a multiplier of 2, the retries would occur after 1s, 2s, 4s, 8s, 16s, and so on. |
+| Retry included exception list | `JedisConnectionException` | A list of `Throwable` classes that count as failures and should be retried. |
+| Retry ignored exception list | Empty list | A list of `Throwable` classes to explicitly ignore for the purposes of retry. |
+
+To disable retry, set `maxRetryAttempts` to 1.
+
+### Circuit breaker configuration
+
+Jedis uses the following circuit breaker settings:
+
+| Setting | Default value | Description |
+|-----------------------------------------|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Sliding window type | `COUNT_BASED` | The type of sliding window used to record the outcome of calls. Options are `COUNT_BASED` and `TIME_BASED`. |
+| Sliding window size | 100 | The size of the sliding window. Units depend on sliding window type. When `COUNT_BASED`, the size represents number of calls. When `TIME_BASED`, the size represents seconds. |
+| Sliding window min calls | 100 | Minimum number of calls required (per sliding window period) before the CircuitBreaker will start calculating the error rate or slow call rate. |
+| Failure rate threshold | `50.0f` | Percentage of calls within the sliding window that must fail before the circuit breaker transitions to the `OPEN` state. |
+| Slow call duration threshold | 60000 ms | Duration threshold above which calls are classified as slow and added to the sliding window. |
+| Slow call rate threshold | `100.0f` | Percentage of calls within the sliding window that exceed the slow call duration threshold before circuit breaker transitions to the `OPEN` state. |
+| Circuit breaker included exception list | `JedisConnectionException` | A list of `Throwable` classes that count as failures and add to the failure rate. |
+| Circuit breaker ignored exception list | Empty list | A list of `Throwable` classes to explicitly ignore for failure rate calculations. | |
+
+### Failover callbacks
+
+In the event that Jedis fails over, you may wish to take some action. This might include logging a warning, recording
+a metric, or externally persisting the cluster connection state, to name just a few examples. For this reason,
+`MultiPooledConnectionProvider` lets you register a custom callback that will be called whenever Jedis
+fails over to a new cluster.
+
+To use this feature, you'll need to design a class that implements `java.util.function.Consumer`.
+This class must implement the `accept` method, as you can see below.
+
+```java
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.function.Consumer;
+
+public class FailoverReporter implements Consumer {
+
+ @Override
+ public void accept(String clusterName) {
+ Logger logger = LoggerFactory.getLogger(FailoverReporter.class);
+ logger.warn("Jedis failover to cluster: " + clusterName);
+ }
+}
+```
+
+You can then pass an instance of this class to your `MultiPooledConnectionProvider`.
+
+```
+FailoverReporter reporter = new FailoverReporter();
+provider.setClusterFailoverPostProcessor(reporter);
+```
+
+The provider will call your `accept` whenever a faoliver occurs.
+
+## Failing back
+
+We believe that failback should not be automatic.
+If Jedis fails over to a new cluster, Jedis will _not_ automatically fail back to the cluster that it was previously connected to.
+This design prevents a scenario in which Jedis fails back to a cluster that may not be entirely healthy yet.
+
+That said, we do provide an API that you can use to implement automated failback when this is appropriate for your application.
+
+## Failback scenario
+
+When a failover is triggered, Jedis will attempt to connect to the next Redis server in the list of server configurations
+you provide at setup.
+
+For example, recall the `redis-east` and `redis-west` deployments from the basic usage example above.
+Jedis will attempt to connect to `redis-east` first.
+If `redis-east` becomes unavailable (and the circuit breaker transitions), then Jedis will attempt to use `redis-west`.
+
+Now suppose that `redis-east` eventually comes back online.
+You will likely want to fail your application back to `redis-east`.
+However, Jedis will not fail back to `redis-east` automatically.
+
+In this case, we recommend that you first ensure that your `redis-east` deployment is healthy before you fail back your application.
+
+## Failback behavior and cluster selection API
+
+Once you've determined that it's safe to fail back to a previously-unavailable cluster,
+you need to decide how to trigger the failback. There are two ways to accomplish this:
+
+1. Use the cluster selection API
+2. Restart your application
+
+### Fail back using the cluster selection API
+
+`MultiClusterPooledConnectionProvider` exposes a method that you can use to manually select which cluster Jedis should use.
+To select a different cluster to use, pass the cluster's numeric index to `setActiveMultiClusterIndex()`.
+
+The cluster's index is a 1-based index derived from its position in the client configuration.
+For example, suppose you configure Jedis with the following client configs:
+
+```
+ClusterConfig[] clientConfigs = new ClusterConfig[2];
+clientConfigs[0] = new ClusterConfig(new HostAndPort("redis-east.example.com", 14000), config);
+clientConfigs[1] = new ClusterConfig(new HostAndPort("redis-west.example.com", 14000), config);
+```
+
+In this case, `redis-east` will have an index of `1`, and `redis-west` will have an index of `2`.
+To select and fail back to `redis-east`, you would call the function like so:
+
+```
+provider.setActiveMultiClusterIndex(1);
+```
+
+This method is thread-safe.
+
+If you decide to implement manual failback, you will need a way for external systems to trigger this method in your
+application. For example, if your application exposes a REST API, you might consider creating a REST endpoint
+to call `setActiveMultiClusterIndex` and fail back the application.
+
+### Fail back by restarting the application
+
+When your application starts, Jedis will attempt to connect to each cluster in the order that the clusters appear
+in your client configuration. It's important to understand this, especially in the case where Jedis has failed over.
+If Jedis has failed over to a new cluster, then restarting the application may result in an inadvertent failback.
+This can happen only if a failed cluster comes back online and the application subsequently restarts.
+
+If you need to avoid this scenario, consider using a failover callback, as described above, to externally record
+the name of the cluster that your application was most recently connected to. You can then check this state on startup
+to ensure that you application only connects to the most recently used cluster. For assistance with this technique,
+[start a discussion](https://github.com/redis/jedis/discussions/new?category=q-a).
diff --git a/docs/jedis-maven.md b/docs/jedis-maven.md
index 1ce4120367..6466b1ef3a 100644
--- a/docs/jedis-maven.md
+++ b/docs/jedis-maven.md
@@ -6,7 +6,7 @@
redis.clientsjedis
- 4.0.0
+ 5.0.0
```
@@ -28,7 +28,7 @@ and
redis.clientsjedis
- 4.1.0-SNAPSHOT
+ 5.1.0-SNAPSHOT
```
diff --git a/docs/redisjson.md b/docs/redisjson.md
index 86a1be96c6..6409e726f9 100644
--- a/docs/redisjson.md
+++ b/docs/redisjson.md
@@ -53,14 +53,23 @@ private class Student {
}
```
-Now we can create some students and store them in Redis as JSON":
+Now we can create some students and store them in Redis as JSON:
```java
+final Gson gson = new Gson();
+
Student maya = new Student("Maya", "Jayavant");
-client.jsonSet("student:111", maya);
+client.jsonSet("student:111", gson.toJson(maya));
Student oliwia = new Student("Oliwia", "Jagoda");
-client.jsonSet("student:112", oliwia);
+client.jsonSet("student:112", gson.toJson(oliwia));
+```
+
+Some of other ways to store POJOs as JSON:
+
+```
+client.jsonSetLegacy("student:111", maya);
+client.jsonSetWithEscape("student:112", oliwia);
```
## Querying and indexing JSON
diff --git a/pom.xml b/pom.xml
index 062bd4a55a..fedcb8d1bc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
jarredis.clientsjedis
- 4.3.0-SNAPSHOT
+ 5.1.0-SNAPSHOTJedisJedis is a blazingly small and sane Redis java client.https://github.com/redis/jedis
@@ -19,7 +19,7 @@
Jedis Mailing Listjedis_redis@googlegroups.com
- http://groups.google.com/group/jedis_redis
+ https://groups.google.com/group/jedis_redis
@@ -27,14 +27,14 @@
MIT
- http://github.com/redis/jedis/raw/master/LICENSE.txt
+ https://github.com/redis/jedis/blob/master/LICENSErepogithub
- http://github.com/redis/jedis/issues
+ https://github.com/redis/jedis/issues
@@ -46,8 +46,9 @@
github
- 1.7.32
+ 1.7.36redis.clients.jedis
+ 1.7.1
@@ -59,25 +60,46 @@
org.apache.commonscommons-pool2
- 2.11.1
+ 2.12.0org.jsonjson
- 20211205
+ 20231013com.google.code.gsongson
- 2.8.9
+ 2.10.1
+
+
+ com.kohlschutter.junixsocket
+ junixsocket-core
+ 2.8.1
+ pom
+ test
+
+
+
+ org.locationtech.jts
+ jts-core
+ 1.19.0
+ test
+ junitjunit4.13.2test
+
+ org.hamcrest
+ hamcrest
+ 2.2
+ test
+ org.slf4jslf4j-simple
@@ -85,18 +107,41 @@
test
- com.kohlschutter.junixsocket
- junixsocket-core
- 2.4.0
- pom
+ org.mockito
+ mockito-inline
+ 4.11.0test
- org.mockito
- mockito-inline
- 3.12.4
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.14.2test
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ 2.14.2
+ test
+
+
+ io.github.resilience4j
+ resilience4j-all
+ ${resilience4j.version}
+ true
+
+
+ io.github.resilience4j
+ resilience4j-circuitbreaker
+ ${resilience4j.version}
+ true
+
+
+ io.github.resilience4j
+ resilience4j-retry
+ ${resilience4j.version}
+ true
+
@@ -111,6 +156,12 @@
+
+
+ src/main/resources
+ true
+
+ org.jacoco
@@ -133,7 +184,7 @@
maven-compiler-plugin
- 3.8.1
+ 3.11.01.8
@@ -141,17 +192,20 @@
maven-surefire-plugin
- 2.22.2
+ 3.1.2${redis-hosts}
+
+ **/examples/*Example.java
+ maven-source-plugin
- 3.2.1
+ 3.3.0true
@@ -166,10 +220,12 @@
maven-javadoc-plugin
- 2.10.4
+ 3.6.0
- true
- -Xdoclint:none
+
+ false
+
+
@@ -182,12 +238,12 @@
maven-release-plugin
- 2.5.3
+ 3.0.1org.sonatype.pluginsnexus-staging-maven-plugin
- 1.6.8
+ 1.6.13trueossrh
@@ -205,7 +261,7 @@
maven-jar-plugin
- 3.0.2
+ 3.3.0${project.build.outputDirectory}/META-INF/MANIFEST.MF
@@ -218,7 +274,7 @@
org.apache.felixmaven-bundle-plugin
- 4.2.1
+ 5.1.9bundle-manifest
@@ -239,7 +295,7 @@
maven-gpg-plugin
- 3.0.1
+ 3.1.0--pinentry-mode
@@ -259,5 +315,19 @@
+
+ doctests
+
+
+
+ maven-surefire-plugin
+ 3.1.2
+
+ **/examples/*Example.java
+
+
+
+
+
diff --git a/src/main/java/redis/clients/jedis/BinaryJedisPubSub.java b/src/main/java/redis/clients/jedis/BinaryJedisPubSub.java
index e7c8f9f1b4..96123256c6 100644
--- a/src/main/java/redis/clients/jedis/BinaryJedisPubSub.java
+++ b/src/main/java/redis/clients/jedis/BinaryJedisPubSub.java
@@ -1,147 +1,9 @@
package redis.clients.jedis;
-import static redis.clients.jedis.Protocol.ResponseKeyword.*;
+public abstract class BinaryJedisPubSub extends JedisPubSubBase {
-import java.util.Arrays;
-import java.util.List;
-
-import redis.clients.jedis.Protocol.Command;
-import redis.clients.jedis.exceptions.JedisException;
-
-public abstract class BinaryJedisPubSub {
- private int subscribedChannels = 0;
- private Connection client;
-
- public void onMessage(byte[] channel, byte[] message) {
- }
-
- public void onPMessage(byte[] pattern, byte[] channel, byte[] message) {
- }
-
- public void onSubscribe(byte[] channel, int subscribedChannels) {
- }
-
- public void onUnsubscribe(byte[] channel, int subscribedChannels) {
- }
-
- public void onPUnsubscribe(byte[] pattern, int subscribedChannels) {
- }
-
- public void onPSubscribe(byte[] pattern, int subscribedChannels) {
- }
-
- public void onPong(byte[] pattern) {
- }
-
- public void unsubscribe() {
- client.sendCommand(Command.UNSUBSCRIBE);
- client.flush();
- }
-
- public void unsubscribe(byte[]... channels) {
- client.sendCommand(Command.UNSUBSCRIBE, channels);
- client.flush();
- }
-
- public void subscribe(byte[]... channels) {
- client.sendCommand(Command.SUBSCRIBE, channels);
- client.flush();
- }
-
- public void psubscribe(byte[]... patterns) {
- client.sendCommand(Command.PSUBSCRIBE, patterns);
- client.flush();
- }
-
- public void punsubscribe() {
- client.sendCommand(Command.PUNSUBSCRIBE);
- client.flush();
- }
-
- public void punsubscribe(byte[]... patterns) {
- client.sendCommand(Command.PUNSUBSCRIBE, patterns);
- client.flush();
- }
-
- public void ping() {
- client.sendCommand(Command.PING);
- client.flush();
- }
-
- public void ping(byte[] argument) {
- client.sendCommand(Command.PING, argument);
- client.flush();
- }
-
- public boolean isSubscribed() {
- return subscribedChannels > 0;
- }
-
- public void proceedWithPatterns(Connection client, byte[]... patterns) {
- this.client = client;
- this.client.setTimeoutInfinite();
- try {
- psubscribe(patterns);
- process();
- } finally {
- this.client.rollbackTimeout();
- }
- }
-
- public void proceed(Connection client, byte[]... channels) {
- this.client = client;
- this.client.setTimeoutInfinite();
- try {
- subscribe(channels);
- process();
- } finally {
- this.client.rollbackTimeout();
- }
- }
-
- private void process() {
- do {
- List