Repository: redis/lettuce
Pull request #2889 (2024-06-10 09:50:03)

Bump reactor.version from 3.6.4 to 3.6.6
Bumps `reactor.version` from 3.6.4 to 3.6.6.
Updates `io.projectreactor:reactor-core` from 3.6.4 to 3.6.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/reactor/reactor-core/releases">io.projectreactor:reactor-core's releases</a>.</em></p>
<blockquote>
<h2>v3.6.6</h2>
<!-- raw HTML omitted -->
<p><code>Reactor Core</code> <code>3.6.6</code> is part of <strong><code>2023.0.6</code> Release Train</strong>.</p>
<h2>What's Changed</h2>
<h3>:sparkles: New features and improvements</h3>
<ul>
<li>Bump byteBuddyVersion from 1.14.12 to 1.14.13 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3778">reactor/reactor-core#3778</a></li>
<li>Bump io.projectreactor.tools:blockhound from 1.0.8.RELEASE to 1.0.9.RELEASE by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3788">reactor/reactor-core#3788</a></li>
<li>Bump byteBuddyVersion from 1.14.13 to 1.14.14 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3795">reactor/reactor-core#3795</a></li>
</ul>
<h3>:lady_beetle: Bug fixes</h3>
<ul>
<li>Fix pendingTasks accounting if TimedRunnable disposed before scheduling by <a href="https://github.com/kkondratov"><code>@​kkondratov</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3780">reactor/reactor-core#3780</a></li>
<li>Remove Flux#thenMany optimization breaking concat by <a href="https://github.com/chemicL"><code>@​chemicL</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3783">reactor/reactor-core#3783</a></li>
<li>Improve auto context propagation in lifting and ConnectableFlux intersection by <a href="https://github.com/chemicL"><code>@​chemicL</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3787">reactor/reactor-core#3787</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/kkondratov"><code>@​kkondratov</code></a> made their first contribution in <a href="https://redirect.github.com/reactor/reactor-core/pull/3780">reactor/reactor-core#3780</a></li>
<li><a href="https://github.com/chickenchickenlove"><code>@​chickenchickenlove</code></a> made their first contribution in <a href="https://redirect.github.com/reactor/reactor-core/pull/3779">reactor/reactor-core#3779</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/reactor/reactor-core/compare/v3.6.5...v3.6.6">https://github.com/reactor/reactor-core/compare/v3.6.5...v3.6.6</a></p>
<h2>v3.6.5</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>:sparkles: New features and improvements</h3>
<ul>
<li>Bump byteBuddyVersion from 1.14.11 to 1.14.12 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3729">reactor/reactor-core#3729</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/reactor/reactor-core/compare/v3.6.4...v3.6.5">https://github.com/reactor/reactor-core/compare/v3.6.4...v3.6.5</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/reactor/reactor-core/commit/69871e9aabae11f5bea8c4cd63753088dc231d62"><code>69871e9</code></a> [release] Prepare and release 3.6.6</li>
<li><a href="https://github.com/reactor/reactor-core/commit/3bd2d1c665265029880fb85a9aea63c70c412f8d"><code>3bd2d1c</code></a> Merge-ignore release 3.4.38 into 3.6.6</li>
<li><a href="https://github.com/reactor/reactor-core/commit/8ab9a81e3cc416424e31bd72c5f5be365e899dd2"><code>8ab9a81</code></a> Merge-ignore release 3.4.38 into 3.5.17</li>
<li><a href="https://github.com/reactor/reactor-core/commit/ac572b742b6022f63bb22a2a9e157d0a71e60f5e"><code>ac572b7</code></a> [release] Next development version 3.4.39-SNAPSHOT</li>
<li><a href="https://github.com/reactor/reactor-core/commit/734e7791a8574dd932816a6cb66e247df6ebf97a"><code>734e779</code></a> [release] Prepare and release 3.4.38</li>
<li><a href="https://github.com/reactor/reactor-core/commit/bf22c93aa5555e0004a3990776dcf2dabe501439"><code>bf22c93</code></a> Merge 47436c0 into 3.6.6</li>
<li><a href="https://github.com/reactor/reactor-core/commit/3435586d7ca675879aed20df54a1b49f596f3680"><code>3435586</code></a> Merge 47436c0 into 3.5.17</li>
<li><a href="https://github.com/reactor/reactor-core/commit/47436c09ef47979d78620d498a13f5a8a5e61f30"><code>47436c0</code></a> Migrate gradle/wrapper-validation-action 2.1.2 to gradle/actions/wrapper-vali...</li>
<li><a href="https://github.com/reactor/reactor-core/commit/6832bad2b672c304b0851562b530c8a74cb0ef3a"><code>6832bad</code></a> Merge <a href="https://redirect.github.com/reactor/reactor-core/issues/3800">#3800</a> into 3.6.6</li>
<li><a href="https://github.com/reactor/reactor-core/commit/88c26b0f3debc551a9bb0b92876a2ad5592f385f"><code>88c26b0</code></a> Merge <a href="https://redirect.github.com/reactor/reactor-core/issues/3800">#3800</a> into 3.5.17</li>
<li>Additional commits viewable in <a href="https://github.com/reactor/reactor-core/compare/v3.6.4...v3.6.6">compare view</a></li>
</ul>
</details>
<br />

Updates `io.projectreactor:reactor-test` from 3.6.4 to 3.6.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/reactor/reactor-core/releases">io.projectreactor:reactor-test's releases</a>.</em></p>
<blockquote>
<h2>v3.6.6</h2>
<!-- raw HTML omitted -->
<p><code>Reactor Core</code> <code>3.6.6</code> is part of <strong><code>2023.0.6</code> Release Train</strong>.</p>
<h2>What's Changed</h2>
<h3>:sparkles: New features and improvements</h3>
<ul>
<li>Bump byteBuddyVersion from 1.14.12 to 1.14.13 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3778">reactor/reactor-core#3778</a></li>
<li>Bump io.projectreactor.tools:blockhound from 1.0.8.RELEASE to 1.0.9.RELEASE by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3788">reactor/reactor-core#3788</a></li>
<li>Bump byteBuddyVersion from 1.14.13 to 1.14.14 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3795">reactor/reactor-core#3795</a></li>
</ul>
<h3>:lady_beetle: Bug fixes</h3>
<ul>
<li>Fix pendingTasks accounting if TimedRunnable disposed before scheduling by <a href="https://github.com/kkondratov"><code>@​kkondratov</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3780">reactor/reactor-core#3780</a></li>
<li>Remove Flux#thenMany optimization breaking concat by <a href="https://github.com/chemicL"><code>@​chemicL</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3783">reactor/reactor-core#3783</a></li>
<li>Improve auto context propagation in lifting and ConnectableFlux intersection by <a href="https://github.com/chemicL"><code>@​chemicL</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3787">reactor/reactor-core#3787</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/kkondratov"><code>@​kkondratov</code></a> made their first contribution in <a href="https://redirect.github.com/reactor/reactor-core/pull/3780">reactor/reactor-core#3780</a></li>
<li><a href="https://github.com/chickenchickenlove"><code>@​chickenchickenlove</code></a> made their first contribution in <a href="https://redirect.github.com/reactor/reactor-core/pull/3779">reactor/reactor-core#3779</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/reactor/reactor-core/compare/v3.6.5...v3.6.6">https://github.com/reactor/reactor-core/compare/v3.6.5...v3.6.6</a></p>
<h2>v3.6.5</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>:sparkles: New features and improvements</h3>
<ul>
<li>Bump byteBuddyVersion from 1.14.11 to 1.14.12 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/reactor/reactor-core/pull/3729">reactor/reactor-core#3729</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/reactor/reactor-core/compare/v3.6.4...v3.6.5">https://github.com/reactor/reactor-core/compare/v3.6.4...v3.6.5</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/reactor/reactor-core/commit/69871e9aabae11f5bea8c4cd63753088dc231d62"><code>69871e9</code></a> [release] Prepare and release 3.6.6</li>
<li><a href="https://github.com/reactor/reactor-core/commit/3bd2d1c665265029880fb85a9aea63c70c412f8d"><code>3bd2d1c</code></a> Merge-ignore release 3.4.38 into 3.6.6</li>
<li><a href="https://github.com/reactor/reactor-core/commit/8ab9a81e3cc416424e31bd72c5f5be365e899dd2"><code>8ab9a81</code></a> Merge-ignore release 3.4.38 into 3.5.17</li>
<li><a href="https://github.com/reactor/reactor-core/commit/ac572b742b6022f63bb22a2a9e157d0a71e60f5e"><code>ac572b7</code></a> [release] Next development version 3.4.39-SNAPSHOT</li>
<li><a href="https://github.com/reactor/reactor-core/commit/734e7791a8574dd932816a6cb66e247df6ebf97a"><code>734e779</code></a> [release] Prepare and release 3.4.38</li>
<li><a href="https://github.com/reactor/reactor-core/commit/bf22c93aa5555e0004a3990776dcf2dabe501439"><code>bf22c93</code></a> Merge 47436c0 into 3.6.6</li>
<li><a href="https://github.com/reactor/reactor-core/commit/3435586d7ca675879aed20df54a1b49f596f3680"><code>3435586</code></a> Merge 47436c0 into 3.5.17</li>
<li><a href="https://github.com/reactor/reactor-core/commit/47436c09ef47979d78620d498a13f5a8a5e61f30"><code>47436c0</code></a> Migrate gradle/wrapper-validation-action 2.1.2 to gradle/actions/wrapper-vali...</li>
<li><a href="https://github.com/reactor/reactor-core/commit/6832bad2b672c304b0851562b530c8a74cb0ef3a"><code>6832bad</code></a> Merge <a href="https://redirect.github.com/reactor/reactor-core/issues/3800">#3800</a> into 3.6.6</li>
<li><a href="https://github.com/reactor/reactor-core/commit/88c26b0f3debc551a9bb0b92876a2ad5592f385f"><code>88c26b0</code></a> Merge <a href="https://redirect.github.com/reactor/reactor-core/issues/3800">#3800</a> into 3.5.17</li>
<li>Additional commits viewable in <a href="https://github.com/reactor/reactor-core/compare/v3.6.4...v3.6.6">compare view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index e33dcd630..6509c5c87 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -5,7 +5,7 @@ Make sure that:
 
 - [ ] You have read the [contribution guidelines](https://github.com/lettuce-io/lettuce-core/blob/main/.github/CONTRIBUTING.md).
 - [ ] You have created a feature request first to discuss your contribution intent. Please reference the feature request ticket number in the pull request.
-- [ ] You use the code formatters provided [here](https://github.com/lettuce-io/lettuce-core/blob/main/formatting.xml) and have them applied to your changes. Don’t submit any formatting related changes.
+- [ ] You applied code formatting rules using the `mvn formatter:format` target. Don’t submit any formatting related changes.
 - [ ] You submit test cases (unit or integration tests) that back your changes.
 
  <!--
diff --git a/.github/wordlist.txt b/.github/wordlist.txt
index 7c8f1adf4..2714717cb 100644
--- a/.github/wordlist.txt
+++ b/.github/wordlist.txt
@@ -73,3 +73,10 @@ github
 runtime
 transactional
 Github
+formatter
+RedisConnectionCommands
+BaseRedisCommands
+DnsResolver
+dnsResolver
+evalReadOnly
+gg
diff --git a/.github/workflows/version-and-release.yaml b/.github/workflows/version-and-release.yaml
index 93ee19348..cc6b766e7 100644
--- a/.github/workflows/version-and-release.yaml
+++ b/.github/workflows/version-and-release.yaml
@@ -10,17 +10,11 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - name: 1. Checkout sources
+      - name: Checkout sources
         uses: actions/checkout@v4
 
-      - name: 2. Extract version from tag
-        id: get_version
-        run: |
-          realversion="${GITHUB_REF/refs\/tags\//}"
-          realversion="${realversion//v/}"
-          echo "VERSION=$realversion" >> $GITHUB_OUTPUT
+      - name: Set up Java with Maven cache
 
-      - name: 3. Set up Java with Maven cache
         uses: actions/setup-java@v4
         with:
           java-version: '8'
@@ -30,20 +24,27 @@ jobs:
           server-username: MAVEN_USERNAME
           server-password: MAVEN_PASSWORD
 
-      - name: 4. Update version in Maven configuration
-        run: mvn versions:set -DnewVersion=${{ steps.get_version.outputs.VERSION }}
+      - name: Update version in Maven configuration
+        run: |
+          mvn --no-transfer-progress \
+            --batch-mode \
+            versions:set -DnewVersion=${{ github.event.release.tag_name  }}
+        if: ${{ github.event.release.tag_name  }}
+
+      - name: Install GPG key
 
-      - name: 5. Install GPG key
         run: |
           cat <(echo -e "${{ secrets.OSSH_GPG_SECRET_KEY }}") | gpg --batch --import
           gpg --list-secret-keys --keyid-format LONG
 
-      - name: 6. Publish to Maven
+      - name: Publish to Maven
+
         run: |
           mvn --no-transfer-progress \
             --batch-mode \
             -Dgpg.passphrase='${{ secrets.OSSH_GPG_SECRET_KEY_PASSWORD }}' \
-            release:perform
+            deploy -P sonatype-oss-release
+
         env:
           MAVEN_USERNAME: ${{secrets.OSSH_USERNAME}}
           MAVEN_PASSWORD: ${{secrets.OSSH_TOKEN}}
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 227a07354..481c2b4cf 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,124 +1,60 @@
-Lettuce 6.3.0 RELEASE NOTES
+Lettuce 6.4.0 RELEASE NOTES
 ==============================
 
-The Lettuce team is delighted to announce general availability of Lettuce 6.3.
+The Redis team is delighted to announce general availability of Lettuce 6.4.
 
-This release ships with support for Observability through Micrometer Tracing that has been
-ported from Spring Data into Lettuce directly. Another infrastructure change is to report
-the driver version and driver name to Redis through `CLIENT SETINFO` if the Redis version
-is 7.2 or later using RESP3.
-
-This release ships also with refinements around Cluster Topology refresh to suspend
-periodic refresh. This is useful for JVM Checkpoint-Restore arrangements (Project CRaC)
-to ensure that no background activity opens connections.
-
-This release also upgrades to Kotlin 1.7 as baseline.
+This Lettuce driver is now going to be shipped under the MIT licensing scheme. The `CLIENT SETINFO`
+is now working in a fire-and-forget mode to allow better compatibility with Redis servers that do
+not support this command.
 
 Lettuce 6 supports Redis 2.6+ up to Redis 7.x. In terms of Java runtime, Lettuce requires
 at least Java 8 and works with Java 21.
 
-Thanks to all contributors who made Lettuce 6.3.0 possible.
+Thanks to all contributors who made Lettuce 6.4.0 possible.
 
 If you need any support, meet Lettuce at
 
 * GitHub Discussions: https://github.com/lettuce-io/lettuce-core/discussions
 * Stack Overflow (Questions): https://stackoverflow.com/questions/tagged/lettuce
-* Join the chat at https://gitter.im/lettuce-io/Lobby for general discussion
+* Join the chat at https://discord.gg/redis for general discussion
 * GitHub Issues (Bug reports, feature
   requests): https://github.com/lettuce-io/lettuce-core/issues
-* Documentation: https://lettuce.io/core/6.3.0.RELEASE/reference/
-* Javadoc: https://lettuce.io/core/6.3.0.RELEASE/api/
+* Documentation: https://lettuce.io/core/6.4.0.RELEASE/reference/
+* Javadoc: https://lettuce.io/core/6.4.0.RELEASE/api/
 
 Commands
 --------
 
-* Add `WITHSCORE` option to `ZRANK` and `ZREVRANK` commands #2410
-* Add support for `CLIENT SETINFO`, `CLIENT INFO`, and enhanced `CLIENT LIST` #2439
-* ZMPOP and BZMPOP commands #2435
-* Support 'FCALL' commands to Call Lua-scripts that are loaded as Function in redis #2185
+* Add `PUBSUB` shard channel commands `SHARDCHANNELS` #2756, `SHARDNUMSUB` #2776
+* Add `PUBSUB` shard channel commands `SPUBLISH` #2757, `SSUBSCRIBE` #2758 and `SUNSUBSCRIBE` #2758
+* Add support for `CLIENT KILL [MAXAGE]` #2782
+* Hash field expiration commands `HEXPIRE`, `HEXPIREAT`, `HEXPIRETIME` and `HPERSIST` #2836
+* Hash field expiration commands `HPEXPIRE`, `HPEXPIREAT`, `HPEXPIRETIME`, `HTTL` and `HPTTL` #2857
 
 Enhancements
 ------------
 
-* Add support for disconnect on timeout to recover early from no `RST` packet failures
-  #2082
-* Avoid buffer copies in `RedisStateMachine` #2173
-* Make SlotHash utility methods public #2199
-* Improve `AdaptiveRefreshTriggeredEvent` to provide the cause and contextual details
-  #2338
-* Refine `RedisException` instantiation to avoid exception instances if they are not used
-  #2353
-* Add capability of FailOver with takeOver option #2358
-* Add option to disable tracing for individual commands #2373
-* ReplicaTopologyProvider can't parse replicas from INFO #2375
-* Add support for Micrometer Tracing #2391
-* Add Command filter to `MicrometerCommandLatencyRecorder` #2406
-* Expose methods to suspend periodic topology refresh and to check whether a topology
-  refresh is running #2428
-* Accept Double and Boolean in `MapOutput` #2429
-* Array lists with set capacities in SimpleBatcher #2445
-* Add fallback to RESP2 upon `NOPROTO` response #2455
-* Introduce generic Object output #2467
-* Propagate initialization failures from `ChannelInitializer` #2475
-* Register library name and library version on Redis 7.2 or greater #2483
-* Add support for cluster-announced hostname #2487
+* Add support for `HSCAN NOVALUES` #2763
+* Send the `CLIENT SETINFO` command in a fire-and-forget way #2082
+* Change the license to more permissive MIT #2173
+* Add a evalReadOnly overload that accepts the script as a String #2868
+* `XREAD` support for reading last message from stream #2863
+* Mark dnsResolver(DnsResolver) as deprecated  #2855
+* Remove connection-related methods from commands API #2027
+* Move connection-related commands from BaseRedisCommands to RedisConnectionCommands #2031
 
 Fixes
 -----
 
-* Proper creation of `AttributeKey` #2111
-* INFO response parsing throws on encountering '\' on NodeTopologyView #2161
-* `PartitionSelectorException` during refresh of `Partitions` #2178
-* RedisURI.Builder#withSsl(RedisURI) not working with SslVerifyMode#CA #2182
-* SMISMEMBER is not marked a readonly command #2197
-* Eval lua script expects return integer but null #2200
-* `ZRANGESTORE` does not support by Rank comparison #2202
-* zrevrangestorebylex/zrevrangestorebyscore range arguments flipped #2203
-* Own `RedisCredentialsProvider` causes issue with protocol handshake on Redis 5 #2234
-* NullPointerException if INFO command on redis cluster fails #2243
-* XTrimArgs Should Allow Limit = 0 #2250
-* The hostname and password cannot parse even if escaping with RedisURI redis-sentinel the
-  password include '@' and '#' #2254
-* Fix password parsing error when redis-sentinel URI contains @ #2255
-* Handle unknown endpoints in MOVED response #2290
-* Fallback to RESP2 hides potential authentication configuration problems #2313
-* Accept slots as String using `CLUSTER SHARDS` #2325
-* `RedisURI.applySsl(…)` does not retain `SslVerifyMode` #2328
-* Apply `SslVerifyMode` in `RedisURI.applySsl(…)` #2329
-* Fix long overflow in `RedisSubscription#potentiallyReadMore` #2383
-* Consistently implement CompositeArgument in arg types #2387
-* Reactive Cluster `MGET` is not running in parallel #2395
-* Polish RedisObservation name & javadoc #2404
-* `memory usage` command passes key as `String` instead of using the codec #2424
-* Fix NPE when manually flushing a batch #2444
-* `flushCommands` leads to random inbound command order when using large argument values
-  with SSL #2456
-* `CommandListener` notified twice on error #2457
-* `RoleParser` does not define `none`, `handshake`, and `unknown` replica states #2482
-* StatefulRedisClusterPubSubConnectionImpl's activated() method will report exception
-  after resubscribe() was call. #2534
+* None
 
 Other
 -----
 
-* Improve Document on pingBeforeActivateConnection #2138
-* Improve Document on dynamicRefreshSources #2139
-* Fixes typo in ReadFrom #2213
-* Fix duplicate word occurrences #2307
-* Update netty.version to 4.1.89.Final #2311
-* Avoid using port 7443 in Lettuce tests #2326
-* Upgrade to Reactor 3.4.27 #2330
-* Fix Set unit test sscanMultiple fail in redis7 #2349
-* README.md demo has a error #2377
-* Upgrade to Kotlin 1.7 #2392
-* Upgrade to Netty 4.1.94.Final #2431
-* Update SetArgs.java builder method param comment #2441
-* Use enum for no-op `PauseDetectorWrapper` #2474
-* Upgrade build to Redis 7.2 #2481
-* Refine command outputs to capture whether a segment has been received instead of relying
-  on the deserialized value state #2498
-* Upgrade to Reactor 3.6.0 #2517
-* Docs on metrics (wiki) are misleading #2538
-* Upgrade to Micrometer 1.12.0 #2549
-* Upgrade to netty 4.1.101.Final #2550
-
+* Bump `org.apache.commons:commons-pool2` from 2.11.1 to 2.12.0 #2877 
+* Bump `org.openjdk.jmh:jmh-generator-annprocess` from 1.21 to 1.37 #2876
+* Bump `org.apache.maven.plugins:maven-jar-plugin` from 3.3.0 to 3.4.1 #2875 
+* Bump `org.codehaus.mojo:flatten-maven-plugin from` 1.5.0 to 1.6.0 #2874
+* Bump `org.apache.maven.plugins:maven-javadoc-plugin` from 3.6.3 to 3.7.0 #2873
+* Applying code formatter each time we run a Maven build #2841 
+* Bump `setup-java` to v4 #2807 
diff --git a/pom.xml b/pom.xml
index 9be9d982f..c98c2fc35 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,7 +64,7 @@
         <mockito.version>4.9.0</mockito.version>
         <netty.version>4.1.107.Final</netty.version>
         <openwebbeans.version>2.0.27</openwebbeans.version>
-        <reactor.version>3.6.4</reactor.version>
+        <reactor.version>3.6.6</reactor.version>
         <rxjava.version>1.3.8</rxjava.version>
         <rxjava-reactive-streams.version>1.2.1</rxjava-reactive-streams.version>
         <rxjava2.version>2.2.21</rxjava2.version>
@@ -900,6 +900,18 @@
                 </configuration>
             </plugin>
 
+            <plugin>
+                <groupId>org.sonatype.plugins</groupId>
+                <artifactId>nexus-staging-maven-plugin</artifactId>
+                <version>1.6.13</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <serverId>ossrh</serverId>
+                    <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+                    <autoReleaseAfterClose>true</autoReleaseAfterClose>
+                </configuration>
+            </plugin>
+
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-source-plugin</artifactId>
@@ -1019,6 +1031,12 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-gpg-plugin</artifactId>
+                        <configuration>
+                            <gpgArguments>
+                                <arg>--pinentry-mode</arg>
+                                <arg>loopback</arg>
+                            </gpgArguments>
+                        </configuration>
                         <executions>
                             <execution>
                                 <id>sign-artifacts</id>
diff --git a/src/main/java/io/lettuce/core/codec/ByteArrayCodec.java b/src/main/java/io/lettuce/core/codec/ByteArrayCodec.java
index c8a9cffd0..bcecd3d28 100644
--- a/src/main/java/io/lettuce/core/codec/ByteArrayCodec.java
+++ b/src/main/java/io/lettuce/core/codec/ByteArrayCodec.java
@@ -90,9 +90,11 @@ public class ByteArrayCodec implements RedisCodec<byte[], byte[]>, ToByteBufEnco
     }
 
     private static byte[] getBytes(ByteBuffer buffer) {
+        if (buffer == null) {
+            return EMPTY;
+        }
 
         int remaining = buffer.remaining();
-
         if (remaining == 0) {
             return EMPTY;
         }
diff --git a/src/main/java/io/lettuce/core/masterreplica/ReadOnlyCommands.java b/src/main/java/io/lettuce/core/masterreplica/ReadOnlyCommands.java
deleted file mode 100644
index 67616a7ef..000000000
--- a/src/main/java/io/lettuce/core/masterreplica/ReadOnlyCommands.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2020-Present, Redis Ltd. and Contributors
- * All rights reserved.
- *
- * Licensed under the MIT License.
- *
- * This file contains contributions from third-party contributors
- * licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.lettuce.core.masterreplica;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import io.lettuce.core.protocol.CommandType;
-import io.lettuce.core.protocol.ProtocolKeyword;
-
-/**
- * Contains all command names that are read-only commands.
- *
- * @author Mark Paluch
- */
-class ReadOnlyCommands {
-
-    private static final Set<CommandType> READ_ONLY_COMMANDS = EnumSet.noneOf(CommandType.class);
-
-    static {
-        for (CommandName commandNames : CommandName.values()) {
-            READ_ONLY_COMMANDS.add(CommandType.valueOf(commandNames.name()));
-        }
-    }
-
-    /**
-     * @param protocolKeyword must not be {@code null}.
-     * @return {@code true} if {@link ProtocolKeyword} is a read-only command.
-     */
-    public static boolean isReadOnlyCommand(ProtocolKeyword protocolKeyword) {
-        return READ_ONLY_COMMANDS.contains(protocolKeyword);
-    }
-
-    /**
-     * @return an unmodifiable {@link Set} of {@link CommandType read-only} commands.
-     */
-    public static Set<CommandType> getReadOnlyCommands() {
-        return Collections.unmodifiableSet(READ_ONLY_COMMANDS);
-    }
-
-    enum CommandName {
-        ASKING, BITCOUNT, BITPOS, CLIENT, COMMAND, DUMP, ECHO, EVAL_RO, EVALSHA_RO, EXISTS, FCALL_RO, //
-        GEODIST, GEOPOS, GEORADIUS, GEORADIUS_RO, GEORADIUSBYMEMBER, GEORADIUSBYMEMBER_RO, GEOSEARCH, GEOHASH, GET, GETBIT, //
-        GETRANGE, HEXISTS, HGET, HGETALL, HKEYS, HLEN, HMGET, HRANDFIELD, HSCAN, HSTRLEN, //
-        HVALS, INFO, KEYS, LINDEX, LLEN, LPOS, LRANGE, SORT_RO, MGET, PFCOUNT, PTTL, //
-        RANDOMKEY, READWRITE, SCAN, SCARD, SCRIPT, //
-        SDIFF, SINTER, SISMEMBER, SMISMEMBER, SMEMBERS, SRANDMEMBER, SSCAN, STRLEN, //
-        SUNION, TIME, TTL, TYPE, //
-        XINFO, XLEN, XPENDING, XRANGE, XREVRANGE, XREAD, //
-        ZCARD, ZCOUNT, ZLEXCOUNT, ZRANGE, //
-        ZRANDMEMBER, ZRANGEBYLEX, ZRANGEBYSCORE, ZRANK, ZREVRANGE, ZREVRANGEBYLEX, ZREVRANGEBYSCORE, ZREVRANK, ZSCAN, ZSCORE,
-    }
-
-}
diff --git a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java
index a6bfc6333..81671bf76 100644
--- a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java
+++ b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java
@@ -71,7 +71,7 @@ public class ReadOnlyCommands {
 
     enum CommandName {
         ASKING, BITCOUNT, BITPOS, CLIENT, COMMAND, DUMP, ECHO, EVAL_RO, EVALSHA_RO, EXISTS, FCALL_RO, //
-        GEODIST, GEOPOS, GEORADIUS, GEORADIUS_RO, GEORADIUSBYMEMBER, GEORADIUSBYMEMBER_RO, GEOSEARCH, GEOHASH, GET, GETBIT, //
+        GEODIST, GEOPOS, GEORADIUS_RO, GEORADIUSBYMEMBER_RO, GEOSEARCH, GEOHASH, GET, GETBIT, //
         GETRANGE, HEXISTS, HGET, HGETALL, HKEYS, HLEN, HMGET, HRANDFIELD, HSCAN, HSTRLEN, //
         HVALS, INFO, KEYS, LINDEX, LLEN, LPOS, LRANGE, SORT_RO, MGET, PFCOUNT, PTTL, //
         RANDOMKEY, READWRITE, SCAN, SCARD, SCRIPT, //


Test patch:
diff --git a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java
index b1f6cad9f..502198b7a 100644
--- a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java
+++ b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java
@@ -16,7 +16,7 @@ class ClusterReadOnlyCommandsUnitTests {
 
     @Test
     void testCount() {
-        assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(86);
+        assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(84);
     }
 
     @Test
diff --git a/src/test/java/io/lettuce/core/codec/ByteArrayCodecUnitTests.java b/src/test/java/io/lettuce/core/codec/ByteArrayCodecUnitTests.java
new file mode 100644
index 000000000..c8f2394da
--- /dev/null
+++ b/src/test/java/io/lettuce/core/codec/ByteArrayCodecUnitTests.java
@@ -0,0 +1,35 @@
+package io.lettuce.core.codec;
+
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ByteArrayCodecUnitTests {
+
+    @Test
+    void testDecodeValue_withNonEmptyByteBuffer() {
+        final ByteArrayCodec byteArrayCodec = ByteArrayCodec.INSTANCE;
+        final byte[] expectedBytes = { 1, 2, 3, 4, 5 };
+        final ByteBuffer buffer = ByteBuffer.wrap(expectedBytes);
+        final byte[] result = byteArrayCodec.decodeValue(buffer);
+        assertThat(result).isEqualTo(expectedBytes);
+    }
+
+    @Test
+    void testDecodeValue_withEmptyByteBuffer() {
+        final ByteArrayCodec byteArrayCodec = ByteArrayCodec.INSTANCE;
+        final ByteBuffer buffer = ByteBuffer.allocate(0);
+        final byte[] result = byteArrayCodec.decodeValue(buffer);
+        assertThat(result).isEmpty();
+    }
+
+    @Test
+    void testDecodeValue_withNullByteBuffer() {
+        final ByteArrayCodec byteArrayCodec = ByteArrayCodec.INSTANCE;
+        final byte[] result = byteArrayCodec.decodeValue(null);
+        assertThat(result).isEmpty();
+    }
+
+}
diff --git a/src/test/java/io/lettuce/core/commands/HashCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/HashCommandIntegrationTests.java
index f6148209c..2cd1608e4 100644
--- a/src/test/java/io/lettuce/core/commands/HashCommandIntegrationTests.java
+++ b/src/test/java/io/lettuce/core/commands/HashCommandIntegrationTests.java
@@ -560,8 +560,7 @@ public class HashCommandIntegrationTests extends TestSupport {
         assertThat(redis.hexpire(MY_KEY, 0, MY_FIELD)).containsExactly(2L);
         assertThat(redis.hset(MY_KEY, MY_FIELD, MY_VALUE)).isTrue();
         assertThat(redis.hexpire(MY_KEY, 1, MY_FIELD, MY_SECOND_FIELD)).containsExactly(1L, -2L);
-        assertThat(redis.hexpire("invalidKey", 1, MY_FIELD)).isEmpty();
-
+        assertThat(redis.hexpire("invalidKey", 1, MY_FIELD)).containsExactly(-2L);
         await().until(() -> redis.hget(MY_KEY, MY_FIELD) == null);
     }
 
@@ -573,7 +572,6 @@ public class HashCommandIntegrationTests extends TestSupport {
         assertThat(redis.hexpire(MY_KEY, Duration.ofSeconds(1), ExpireArgs.Builder.xx(), MY_FIELD)).containsExactly(1L);
         assertThat(redis.hexpire(MY_KEY, Duration.ofSeconds(10), ExpireArgs.Builder.gt(), MY_FIELD)).containsExactly(1L);
         assertThat(redis.hexpire(MY_KEY, Duration.ofSeconds(1), ExpireArgs.Builder.lt(), MY_FIELD)).containsExactly(1L);
-
         await().until(() -> redis.hget(MY_KEY, MY_FIELD) == null);
     }
 
@@ -584,8 +582,7 @@ public class HashCommandIntegrationTests extends TestSupport {
         assertThat(redis.hexpireat(MY_KEY, Instant.now().minusSeconds(1), MY_FIELD)).containsExactly(2L);
         assertThat(redis.hset(MY_KEY, MY_FIELD, MY_VALUE)).isTrue();
         assertThat(redis.hexpireat(MY_KEY, Instant.now().plusSeconds(1), MY_FIELD)).containsExactly(1L);
-        assertThat(redis.hexpireat("invalidKey", Instant.now().plusSeconds(1), MY_FIELD)).isEmpty();
-
+        assertThat(redis.hexpireat("invalidKey", Instant.now().plusSeconds(1), MY_FIELD)).containsExactly(-2L);
         await().until(() -> redis.hget(MY_KEY, MY_FIELD) == null);
     }
 
@@ -609,8 +606,7 @@ public class HashCommandIntegrationTests extends TestSupport {
     @Test
     @EnabledOnCommand("HPERSIST")
     void hpersist() {
-        assertThat(redis.hpersist(MY_KEY, MY_FIELD)).isEmpty();
-
+        assertThat(redis.hpersist(MY_KEY, MY_FIELD)).containsExactly(-2L);
         assertThat(redis.hset(MY_KEY, MY_FIELD, MY_VALUE)).isTrue();
         assertThat(redis.hpersist(MY_KEY, MY_FIELD)).containsExactly(-1L);
 
diff --git a/src/test/java/io/lettuce/core/commands/ServerCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/ServerCommandIntegrationTests.java
index b9be862e7..a64414edf 100644
--- a/src/test/java/io/lettuce/core/commands/ServerCommandIntegrationTests.java
+++ b/src/test/java/io/lettuce/core/commands/ServerCommandIntegrationTests.java
@@ -33,6 +33,8 @@ import java.util.regex.Pattern;
 
 import javax.inject.Inject;
 
+import io.lettuce.core.cluster.ClusterReadOnlyCommands;
+import io.lettuce.core.protocol.ProtocolKeyword;
 import io.lettuce.test.condition.RedisConditions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
@@ -592,6 +594,13 @@ public class ServerCommandIntegrationTests extends TestSupport {
         assertThat(redis.clientInfo().contains("lib-name=lettuce")).isTrue();
     }
 
+    @Test
+    void testReadOnlyCommands() {
+        for (ProtocolKeyword readOnlyCommand : ClusterReadOnlyCommands.getReadOnlyCommands()) {
+            assertThat(isCommandReadOnly(readOnlyCommand.name())).isTrue();
+        }
+    }
+
     private boolean noSaveInProgress() {
 
         String info = redis.info();
@@ -599,4 +608,19 @@ public class ServerCommandIntegrationTests extends TestSupport {
         return !info.contains("aof_rewrite_in_progress:1") && !info.contains("rdb_bgsave_in_progress:1");
     }
 
+    private boolean isCommandReadOnly(String commandName) {
+        List<Object> commandInfo = redis.commandInfo(commandName);
+        if (commandInfo == null || commandInfo.isEmpty()) {
+            throw new IllegalArgumentException("Command not found: " + commandName);
+        }
+
+        List<CommandDetail> details = CommandDetailParser.parse(commandInfo);
+        if (details.isEmpty()) {
+            throw new IllegalArgumentException("Command details could not be parsed: " + commandName);
+        }
+
+        CommandDetail detail = details.get(0);
+        return !detail.getFlags().contains(CommandDetail.Flag.WRITE);
+    }
+
 }