Skip to content

[maintenance events] Support maintenance PUSH notification #4473

Merged
ggivo merged 143 commits into
feature/sch-1from
feature/hu-notifications-rebased-wo-generic-push-listeners
Jul 9, 2026
Merged

[maintenance events] Support maintenance PUSH notification #4473
ggivo merged 143 commits into
feature/sch-1from
feature/hu-notifications-rebased-wo-generic-push-listeners

Conversation

@ggivo

@ggivo ggivo commented Mar 25, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR enhances Jedis and UnifiedJedis with improved support for RESP3 push messages and introduces a configurable option for relaxed timeout behavior.

These changes are part of a broader effort to support more robust and controlled upgrade and maintenance scenarios. Specifically, they lay the groundwork for:

  • Temporarily relaxing timeouts during planned maintenance windows.
  • Gracefully re-binding pooled connections to updated endpoints without disrupting active operations.

This PR aims to provide support for reading arbitrary PUSH notifications (such as maintenance events) and serve as an initial commit for introducing support for timeout relaxation on miantenace events

Backround

In the current state of the client:

  • Jedis supports only known Push notifications like invalidate. Push notifications are supported using dedicated client-side caching (CSC) connection (CacheConnection)
  • This leads to runtime errors if CLIENT TRACKING ON is enabled in a context other than CSC, or if other RESP3 push events are received.

Changes Introduced

  • Added support for processing and discarding all RESP3 push messages, preventing errors in non-CSC scenarios.
  • Pub/Sub push messages are still handled as before and propagated to the client for backward compatibility.
  • Added a configurable option to enable relaxed timeout behavior, intended for usage during maintenance events or when expecting slow responses from the server.

Note

High Risk
Changes core connection lifecycle, pooling return/eviction, socket connect targets, and read timeouts—any bug can misroute traffic or drop pooled connections during maintenance.

Overview
Adds Smart Client Handoff support for Redis maintenance RESP3 push events (MOVING, MIGRATING, FAILING_OVER, and their terminators), wired through pooled connections when MaintenanceNotificationsConfig is enabled or in AUTO mode.

Pooled clients negotiate CLIENT MAINT_NOTIFICATIONS ON (RESP3), register a maintenance push consumer, and run a chained timeout model so reads can temporarily use relaxed socket timeouts during migration/failover and restore them on MIGRATED/FAILED_OVER or a configurable max window. MOVING drives proactive endpoint rebind: post-DNS connect remapping, pool eviction on handoff, returning affected connections as broken, and expiring receivers that saw the event.

Configuration is exposed on standalone builders (RedisClient via StandaloneClientBuilder, default AUTO) and per-database in MultiDbConfig (default disabled). ConnectionPool, ConnectionFactory, DefaultJedisSocketFactory, TrackingConnectionPool, and PooledConnectionProvider thread the maintenance controller through factory construction.

Connection timeout handling is refactored around TimeoutSource overrides (with redundant setSoTimeout avoided via caching), blocking vs non-blocking reads, and optional connection expiry on pool return. Push consumer chains gain remove support for failed handshakes.

Broad sch unit/integration tests, shared abstract suites, and a manual MaintenanceEventsExample document handshake, relaxation, rebind, and multi-DB behavior.

Reviewed by Cursor Bugbot for commit 0b944a1. Bugbot is set up for automated code reviews on this repo. Configure here.

ggivo added 30 commits March 24, 2026 10:22
   - Preparation step for processing custom push notifications
   - Push notification can appear out-of band in-between executed commands
   - Current Connection implementation does not support out of band Push notifications
   - Meaning it will crash if "CLIENT TRACKING ON is enabled" on regular Jedis Connection and "invalidation" push event is triggered

 This commit provides a way to register push handler for the connection which process incoming push messages, before actual command is executed.  To preserve backward compatibility unprocessed push messages are forward to application logic as before.

   - By default Connection will start with NOOP push handler which marks any incoming push event as processed and skips it
   - On subcsribe/psubscribe a dedicated push handler is registered which propagates to the app only supported push  vents such as (message, subscribe, unsubscribe ...)
   - CacheConection is refactored to use a push handler handling "invalidate" push events only, and skipping any other

# Conflicts:
#	src/main/java/redis/clients/jedis/Connection.java
#	src/test/java/redis/clients/jedis/commands/jedis/PublishSubscribeCommandsTest.java
This commit adds a new PushHandlerChain class that implements the Chain of
Responsibility pattern for Redis RESP3 push message handling. Key features:

- Allows composing multiple PushHandlers in a processing chain
- Push events propagate through the complete chain in sequence
- Events marked as not processed are propagated to the client application
- Provides both constructor-based and fluent builder API for chain creation
- Includes predefined handlers for common use cases (CONSUME_ALL, PROPAGATE_ALL)
- Supports immutable chain transformations via methods like then(),

The chain approach provides a flexible way to handle different types of push
messages (invalidations, pub/sub, etc.) with specialized handlers while
maintaining a clean separation of concerns.

Example usage:
  PushHandlerChain chain = PushHandlerChain.of(loggingHandler)
      .then(invalidationHandler)
      .then(PushHandlerChain.PROPAGATE_PUB_SUB_PUSH_HANDLER);
  - code clean up
  - added relaxed timeout configuration
  - fix unit tests
Register PushInvalidateConsumer after cache is initialised
ConenctionFactory should be rebound before triggering the disposal of Idle connection, so that any newly creaetd are using the proper hostname
Issue : If Maintenace notifications are received during blocking command, relaxTimeout is enforced instead of infinit timeout.

Fix: Introduce dedicated relax timeout setting for blocking commands. It will fall back to infinit timeout if not set
   - Mark all pushes by default as processed
   - Remove CONSUME_ALL_HANDLER
 - Use existing ReflectionTestUtil instead of ReflectionTestUtils.java
 - address connection pool now uses builder - socketFactory not accessible
 - test should now use Endpoints
 - ListenerNotificationConsumer from Jedis
        moved to Connection
 - fix PushMessageNotificationTest
 - fix pom.xml missing includes tag
 - ConnectionTestHelper is obsolete after rebase
 - fix A MIGRATING event on connection A triggers AdaptiveTimeoutHandler for all connections
ggivo added 2 commits June 23, 2026 17:07
Relaxed socket/blocking timeouts were public knobs on JedisClientConfig
but only take effect during SCH maintenance windows. Move them to
MaintenanceNotificationsConfig (sourced by Connection/ConnectionFactory)
and make the connection-level relax API package-private, so the public
surface only exposes settings wired end-to-end.

Addresses @atakavci: don't add interface config that isn't supported
end-to-end via core components — it only creates user-side confusion.
…CF DISABLED

RedisClient defaults to AUTO; MultiDbClient databases default to DISABLED.
Each builder tracks the unset state via a dedicated sentinel instance, so an
explicit value is distinguishable from the default and the default can change
in a future release without overriding clients that set one.

Pending: "auto" endpoint-type discovery for MOVING (from IP format + TLS
config), to become the resolved default under AUTO mode.
Comment thread src/main/java/redis/clients/jedis/MaintenanceEventController.java Outdated
Comment thread src/main/java/redis/clients/jedis/MaintenanceEventConsumer.java Outdated
Comment thread src/main/java/redis/clients/jedis/MaintenancePushCodec.java Outdated
Comment thread src/main/java/redis/clients/jedis/MaintenancePushCodec.java
Comment thread src/main/java/redis/clients/jedis/MaintenanceEventController.java
@atakavci atakavci changed the base branch from master to feature/sch-1 July 8, 2026 12:14
…s via TimeoutSupplier (#4572)

* fix duble authx registration

* draft timeoutsupplier

* hot path perf optimization

* micro optimization by AI =) ridiculously micro

* - clean up connection from rebindstate
- preserver the old semantics with timout getters on conneciton
- use last reference for relaxingtimeouts instead of list
- drop simpletimeoutsupplier
- improve controller to hold rebinding connections.
- introduce returnHook in ConnectionPool

* clean up

* - feedback from @ggivo

* - clean duplicate fields
- fix compile issue

* - introduce TimeoutSupplierChain
- replace AdvancedTimeoutSupplier

* fix duration-timestamp issue with relaxation

* - drop weakhashmap
- remove applysotimeout at getStatusCodeReplyInner

* - fix possible refencing corruption with unified timeoutsupplierchain instance from controller

* - add expiration to conneciton in case future renewals needed
- improve connection with init visitors and disect the logic  out to relevant components for optional features.

* - rollback to using isBlocking at connection level
-clean up with configuration classes

* - feedback from @copilot

* - polish names

* - move applyCurrentTimeout into readProtocol

* - refactor type names

* - introduce upluggable source

* - move customtimeoutsupplier setup into maintenanceawarevisitor

* - fix test-compiler issues

* - inject maintenance initiation responsibililty into connection.builder

* - fix controller unit tests

* - fix connection build for maintenancevisitor
- fix AbstractRelaxedTimeoutBehaviorTest

* - probe and add controller as listener in maintenancevisitor

* fix java doc refs

* polish

* - address @cursor feedback

* clean up connection builder

* - fix tests

* feeback from @curosr

* - format

* - format

* Align TimeoutSource chain naming with its chain-of-responsibility role

TimeoutSourceNode leaked the linked-list implementation into the type
name and forced every source to be chainable through the
TimeoutSource/UnplugableSource split.

- TimeoutSourceNode -> ChainedTimeoutSource; overrideWith/unplug ->
  addOverride/removeOverride, typed to the chain class
- TimeoutSource slimmed to the read contract; the null-means-no-opinion
  protocol is now documented on get()
- UnplugableSource, its unused generic and the unchecked cast removed
- anonymous controller node named RebindTimeoutSource
- get() snapshots the volatile override to avoid an NPE against a
  concurrent removeOverride

---------

Co-authored-by: ggivo <ivo.gaydazhiev@redis.com>
Comment thread src/main/java/redis/clients/jedis/Connection.java Outdated
Comment thread src/main/java/redis/clients/jedis/MaintenanceEventController.java
Comment thread src/main/java/redis/clients/jedis/Connection.java Outdated
Comment thread src/main/java/redis/clients/jedis/Connection.java
Comment thread src/main/java/redis/clients/jedis/MaintenanceEventController.java

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

There are 6 total unresolved issues (including 4 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3f42dff. Configure here.

Comment thread src/main/java/redis/clients/jedis/MaintenanceEventController.java
Comment thread src/main/java/redis/clients/jedis/MaintenanceEventController.java
atakavci and others added 4 commits July 9, 2026 11:09
The shared handshake tests built a full RedisClient per test, coupling
connection-level behavior to client wiring. The base test now wires the
handshake onto a Connection.Builder directly, mirroring ConnectionFactory
production wiring. Client-level wiring coverage moves to a dedicated
RedisClient test.
@ggivo ggivo merged commit 5e8891f into feature/sch-1 Jul 9, 2026
26 of 46 checks passed
@ggivo ggivo deleted the feature/hu-notifications-rebased-wo-generic-push-listeners branch July 9, 2026 13:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants