[maintenance events] Support maintenance PUSH notification #4473
Merged
ggivo merged 143 commits intoJul 9, 2026
Merged
Conversation
- 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
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.
atakavci
reviewed
Jun 29, 2026
- add todo for missing `none` support for moving event
…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>
…notifications-rebased-wo-generic-push-listeners
…xception when applying socket.setSoTimeout
There was a problem hiding this comment.
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).
❌ 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.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

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:
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:
CLIENT TRACKING ONis enabled in a context other than CSC, or if other RESP3 push events are received.Changes Introduced
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 whenMaintenanceNotificationsConfigis 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 onMIGRATED/FAILED_OVERor 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 (
RedisClientviaStandaloneClientBuilder, default AUTO) and per-database inMultiDbConfig(default disabled).ConnectionPool,ConnectionFactory,DefaultJedisSocketFactory,TrackingConnectionPool, andPooledConnectionProviderthread the maintenance controller through factory construction.Connectiontimeout handling is refactored aroundTimeoutSourceoverrides (with redundantsetSoTimeoutavoided 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
MaintenanceEventsExampledocument 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.