Skip to content

feat(jedis-compatibility): add pub sub commands jedis layer#5286

Open
prashanna-frsh wants to merge 26 commits intovalkey-io:mainfrom
prashanna-frsh:task/add-pub-sub-commands-jedis-layer
Open

feat(jedis-compatibility): add pub sub commands jedis layer#5286
prashanna-frsh wants to merge 26 commits intovalkey-io:mainfrom
prashanna-frsh:task/add-pub-sub-commands-jedis-layer

Conversation

@prashanna-frsh
Copy link
Contributor

@prashanna-frsh prashanna-frsh commented Feb 2, 2026

Summary

This PR adds comprehensive PubSub (Publish/Subscribe) command support to the Jedis compatibility layer, enabling applications to migrate from Jedis to Valkey GLIDE with full PubSub API compatibility. The implementation includes publishing commands, subscription/unsubscription commands, and channel introspection commands with both String and binary variants.

Issue link

This Pull Request is linked to issue: #5285

Features / Behaviour Changes

Publishing & Introspection Commands:

  • publish() - Publish messages to channels (returns 0 as GLIDE doesn't expose subscriber count)
  • pubsubChannels() - List active channels with optional pattern filtering
  • pubsubNumPat() - Get count of unique pattern subscriptions
  • pubsubNumSub() - Get subscriber counts for specific channels

Subscription Commands:

  • subscribe() / unsubscribe() - Subscribe/unsubscribe to exact channels
  • psubscribe() / punsubscribe() - Subscribe/unsubscribe to channel patterns (glob-style)
  • ssubscribe() / sunsubscribe() - Subscribe/unsubscribe to sharded channels (cluster mode, Valkey 7.0+)

All commands include both String and binary (byte[]) variants for full Jedis API compatibility. Total of 16 new public methods added to the Jedis class.

Implementation

Publishing commands (lines 6996-7136 in Jedis.java):

  • Use GLIDE's native PubSub APIs (glideClient.publish(), glideClient.pubsubChannels(), etc.)
  • Follow existing executeCommandWithGlide() pattern for consistent error handling
  • publish() returns 0L as GLIDE doesn't provide subscriber count in response

Subscription commands (lines 7022-7315 in Jedis.java):

  • Use glideClient.customCommand() to send subscription commands to server
  • Each command constructs command array (command name + channels/patterns)
  • Return void as per Jedis API contract
  • Support varargs for multiple channels/patterns

Key code highlights:

  • Binary variants convert byte[] to GlideString for GLIDE compatibility
  • Subscription commands send commands but don't handle incoming messages (documented limitation)
  • All methods include comprehensive JavaDoc with Valkey version info and migration notes

Documentation updates:

  • Updated compatibility-layer-migration-guide.md with complete PubSub command list
  • Added usage examples for both basic commands and full GLIDE subscription configuration
  • Clarified limitations and recommended migration path

Limitations

Subscription message handling:
The subscribe(), psubscribe(), and ssubscribe() methods send subscription commands to the server but do not handle incoming PubSub messages. This is a deliberate design choice aligned with GLIDE's architecture.

For full PubSub functionality with message callbacks, applications should use GLIDE's native subscription configuration:

StandaloneSubscriptionConfiguration subConfig = 
    StandaloneSubscriptionConfiguration.builder()
        .subscription(PubSubChannelMode.EXACT, gs("channel"))
        .subscription(PubSubChannelMode.PATTERN, gs("news.*"))
        .callback((message, context) -> {
            // Handle incoming messages
        })
        .build();

Other limitations:

  • publish() returns 0 instead of actual subscriber count (GLIDE API limitation)
  • ssubscribe() / sunsubscribe() only work in cluster mode (will fail on standalone)
  • Jedis-style JedisPubSub callback listeners are not supported

Testing

Unit tests (JedisPubSubCommandsTest.java):

  • 22 tests validating method signatures and return types
  • Tests cover all command variants (String/binary, subscribe/unsubscribe)
  • All tests passing ✓

Integration tests (JedisPubSubIntegrationTest.java):

  • 14 integration tests covering actual command execution
  • Tests disabled by default (require running Valkey/Redis server)
  • Tests cover:
    • Single and multiple channel subscriptions
    • Pattern and sharded channel subscriptions
    • Binary variants of all commands
    • Unsubscribe-all functionality
    • Mixed subscription types
    • Complete publish-subscribe workflow

Manual testing:

  • Verified commands execute successfully against Valkey server
  • Confirmed no regressions in existing PubSub functionality

Test execution:

./gradlew :jedis-compatibility:test --tests 'JedisPubSubCommandsTest' --rerun
# Result: BUILD SUCCESSFUL - 22/22 tests passed

Checklist

Before submitting the PR make sure the following are checked:

  • This Pull Request is related to one issue.
  • Commit message has a detailed description of what changed and why.
  • Tests are added or updated.
  • CHANGELOG.md and documentation files are updated.
  • Linters have been run (make *-lint targets) and Prettier has been run (make prettier-fix).
  • Destination branch is correct - main or release
  • Create merge commit if merging release branch into main, squash otherwise.

@prashanna-frsh prashanna-frsh requested a review from a team as a code owner February 2, 2026 07:33
@prashanna-frsh prashanna-frsh changed the title Task/add pub sub commands jedis layer feat(jedis-compatibility): add pub sub commands jedis layer Feb 2, 2026
@prashanna-frsh
Copy link
Contributor Author

prashanna-frsh commented Feb 4, 2026

since #5267 is resolved, subscribe commands can be updated.

Updated subscribe/unsubscribe methods in Jedis compatibility layer to use
GLIDE's dynamic subscription APIs from PR #5269 instead of customCommand().

@xShinnRyuu
Copy link
Collaborator

Hi @prashanna-frsh, it looks like some of your commits are not verified. We have a policy that all commits needs to have signing and signoff. We can help review your PR after these are being taken care of.

@xShinnRyuu xShinnRyuu linked an issue Feb 5, 2026 that may be closed by this pull request
2 tasks
…bChannels, pubsubNumPat, pubsubNumSub)

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
… of customCommand

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
…mands

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
…cription APIs

Update subscribe/unsubscribe methods in Jedis compatibility layer to use
GLIDE's dynamic subscription APIs from PR valkey-io#5269 instead of customCommand().

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
@prashanna-frsh prashanna-frsh force-pushed the task/add-pub-sub-commands-jedis-layer branch from f025365 to 6ef11fb Compare February 5, 2026 03:33
@prashanna-frsh
Copy link
Contributor Author

Hi @prashanna-frsh, it looks like some of your commits are not verified. We have a policy that all commits needs to have signing and signoff. We can help review your PR after these are being taken care of.

Signed the commits

Copy link
Collaborator

@currantw currantw left a comment

Choose a reason for hiding this comment

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

Thanks a lot for your work on this @prashanna-frsh !

I have reviewed Jedis.java so far and added some comments. Will try to finish reviewing the remaining files shortly.

Comment on lines +1646 to +1647
List<String> channelsWithPattern = jedis.pubsubChannels(channel);
assertNotNull(channelsWithPattern, "PUBSUB CHANNELS pattern should return a list");
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should be able to assert here that is just returns a list with the channel right (not just any list)? Similar with some of the other tests below, I think we can go a bit more specific?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed it

List<byte[]> channelsBinary = jedis.pubsubChannels(channelBytes);
assertNotNull(channelsBinary, "PUBSUB CHANNELS binary should return a list");

Map<byte[], Long> numSubBinary = jedis.pubsubNumSub(channelBytes);
Copy link
Collaborator

Choose a reason for hiding this comment

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

We need to examine the contents of the map.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@prashanna-frsh please address this comment. We need to assert that the values in the map are what we expect.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed it

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
@prashanna-frsh
Copy link
Contributor Author

@jduo and @currantw addressed all the feedbacks

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
*
* @return the number of unique patterns
*/
public long pubsubNumPat() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should return a java.lang.Long rather than primitive long. Please correct the signature and check over the signatures for other methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed it

* @param channels channel names
* @return map of channel name to subscriber count
*/
public Map<byte[], Long> pubsubNumSub(final byte[]... channels) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is the binary overload needed? I don't see it in the 5.1.5 Jedis API

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed it

* @param pattern glob-style pattern (pass null or empty array for all channels)
* @return list of channel names
*/
public List<byte[]> pubsubChannels(final byte[] pattern) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please verify if the binary overload is required.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed it

* @return a List of byte arrays
*/
private static List<byte[]> convertGlideStringArrayToByteArrayList(GlideString[] glideStrings) {
List<byte[]> result = new ArrayList<>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: Let's specify the capacity of the list since we know the length of the input.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed it

- ✅ Key operations (DEL, EXISTS, EXPIRE, TTL)
- ✅ Connection commands (PING, SELECT)
- ✅ ACL commands (ACL LIST, ACL GETUSER, ACL SETUSER, ACL DELUSER, ACL CAT, ACL GENPASS, ACL LOG, ACL LOG RESET, ACL WHOAMI, ACL USERS, ACL SAVE, ACL LOAD, ACL DRYRUN)
- ✅ Pub/Sub (PUBLISH, PUBSUB CHANNELS, PUBSUB NUMSUB, PUBSUB NUMPAT). `publish()` returns `0` instead of the actual subscriber count due to GLIDE API limitations (see [issue #5354](https://github.com/valkey-io/valkey-glide/issues/5354)). For subscription management and message handling, use GLIDE's native `StandaloneSubscriptionConfiguration` or `ClusterSubscriptionConfiguration` at client creation time.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment should be changed because Java supports dynamic pubsub now. They don't have to use subscription configuration at client creation time. Let's just tell the user to use Glide's native pubsub features.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed it

- **Pipelining**: Jedis pipelining functionality unavailable
- **Pub/Sub**: Redis publish/subscribe not implemented
- **Lua scripting**: Full support for EVAL/EVALSHA, SCRIPT management, and Valkey Functions (FCALL/FUNCTION *)
- **Pub/Sub with JedisPubSub callbacks**: Jedis-style `JedisPubSub` callback listeners are not supported. For PubSub functionality with message handling, configure GLIDE's native `StandaloneSubscriptionConfiguration` or `ClusterSubscriptionConfiguration` at client creation time to receive messages via callbacks or message queues. The compatibility layer only provides `publish()` and `pubsub*()` inspection commands.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar comment to above. Users should use Glide's native pubsub functionality rather than subscription configuration specifically.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed it

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
@prashanna-frsh prashanna-frsh requested a review from jduo February 25, 2026 04:22
@xShinnRyuu
Copy link
Collaborator

Hi @prashanna-frsh,

It looks like there are still a few comments that haven’t been replied to or addressed yet.

Would you be able to revisit those and either resolve them or leave a note indicating whether they’ve been addressed, and if so, in which commit? That would be very helpful for tracking and review purposes.

Thank you!

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
@prashanna-frsh
Copy link
Contributor Author

Feedback Status Update

I've reviewed all outstanding feedback and addressed the remaining items:

✅ Addressed

  1. JedisPubSub Callbacks Issue (@jduo's request)

  2. Return Type Verification (@jduo's question on pubsubNumPat)

    • Verified against Jedis 5.1.5 source: return type is Long (not long)
    • Current implementation is correct (line 9000 in Jedis.java)
    • Integration test correctly uses Long (line 1820 in JedisTest.java)

📝 Notes on Other Feedback

  • Binary variant removal: Correctly removed per @jduo's feedback (only String variants remain for pubsubChannels and pubsubNumSub)
  • Test assertions: Current tests are comprehensive given the constraint that we cannot test with actual subscriptions (subscribe/unsubscribe methods were removed per main feedback)
  • Null condition in pubsubChannels: The null check is defensive programming; GLIDE APIs shouldn't return null, but the check ensures safety

All feedback items from the review have been addressed or clarified.

prashanna-frsh added a commit to prashanna-frsh/valkey-glide that referenced this pull request Mar 2, 2026
…ub callback support

Add reference to issue valkey-io#5469 in migration guide to track planned
support for Jedis-style JedisPubSub callback listeners.

Addresses feedback from PR valkey-io#5286 review.

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
Made-with: Cursor
…ub callback support

Add reference to issue valkey-io#5469 in migration guide to track planned
support for Jedis-style JedisPubSub callback listeners.

Addresses feedback from PR valkey-io#5286 review.

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
Made-with: Cursor
Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
Made-with: Cursor
@prashanna-frsh prashanna-frsh force-pushed the task/add-pub-sub-commands-jedis-layer branch from faced02 to 66bbbeb Compare March 2, 2026 04:47
@currantw
Copy link
Collaborator

currantw commented Mar 4, 2026

Thanks for your work on this @prashanna-frsh ! I have been quite busy. Will do my best to take another look in the next 2 days.

Copy link
Collaborator

@currantw currantw left a comment

Choose a reason for hiding this comment

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

Thanks for the update and sorry for the review delay!

Some thoughts:

  • Does it also make sense to add these methods for JedisCluster in this pull request as well?
  • I think we are still missing the test coverage we would need to be able to merge this.

Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
Signed-off-by: prashanna-frsh <prashanna.rajendran@freshworks.com>
@prashanna-frsh
Copy link
Contributor Author

#5286 (comment)

Added pubsub_introspection_with_active_subscription() for testing it

@prashanna-frsh prashanna-frsh requested a review from currantw March 12, 2026 04:11
@xShinnRyuu xShinnRyuu removed their request for review March 19, 2026 18:34
Copy link
Collaborator

@currantw currantw left a comment

Choose a reason for hiding this comment

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

Looks good! Just a few minor comments to address.

* @see <a href="https://valkey.io/commands/publish/">valkey.io</a> for details.
* @since Valkey 1.0.0
*/
public long publish(String channel, String message) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this need to return Long for Jedis compatibility? Same for the binary publish method.

* @param pattern glob-style pattern
* @return list of channel names
*/
public List<String> pubsubChannels(String pattern) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit. Would it be possible to group all the pub/sub methods together. Will be tough to read for future developers if they're sprinkled through this file. 🤷

Comment on lines +9072 to +9079
public List<String> pubsubChannels() {
return executeCommandWithGlide(
"PUBSUB CHANNELS",
() -> {
String[] arr = glideClient.pubsubChannels().get();
return arr != null ? Arrays.asList(arr) : Collections.emptyList();
});
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I do not believe that pubsubChannels will ever return null, so this can be simplified. Same for the other methods with similar implementations.

Suggested change
public List<String> pubsubChannels() {
return executeCommandWithGlide(
"PUBSUB CHANNELS",
() -> {
String[] arr = glideClient.pubsubChannels().get();
return arr != null ? Arrays.asList(arr) : Collections.emptyList();
});
}
public List<String> pubsubChannels() {
return executeCommandWithGlide(
"PUBSUB CHANNELS",
() -> Arrays.asList(glideClient.pubsubChannels().get()));
}

- ✅ Wait operations (WAIT, WAITAOF)
- ✅ Object introspection (OBJECT ENCODING, OBJECT FREQ, OBJECT IDLETIME, OBJECT REFCOUNT)
- ✅ Geospatial operations (GEOADD, GEOPOS, GEODIST, GEOHASH, GEOSEARCH, GEOSEARCHSTORE)
- ✅ Pub/Sub (PUBLISH, PUBSUB CHANNELS, PUBSUB NUMSUB, PUBSUB NUMPAT). `publish()` returns `0` instead of the actual subscriber count due to GLIDE API limitations (see [issue #5354](https://github.com/valkey-io/valkey-glide/issues/5354)). For subscription management and message handling, use GLIDE's native dynamic pubsub APIs for subscribing to channels and handling messages at runtime.
Copy link
Collaborator

Choose a reason for hiding this comment

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

GLIDE's native dynamic pubsub APIs

Not sure exactly what this refers to? Are those APIs implemented yet?

* channel appears and subscriber count is correct.
*/
@Test
void pubsub_introspection_with_active_subscription() throws Exception {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks much better! Can we please split it out into separate tests for each method and each test case?

As an example, here are the tests that I implemented for the Valkey GLIDE C# client's pub/sub introspection commands:

  1. PubSubChannelsAsync_ReturnsEmpty
  2. PubSubChannelsAsync_WithActiveSubscription_ReturnsChannel
  3. PubSubChannelsAsync_WithPattern_ReturnsMatchingChannels
  4. PubSubNumSubAsync_WithNoSubscribers_ReturnsZeroCounts
  5. PubSubNumSubAsync_WithSubscribers_ReturnsChannelCounts
  6. PubSubNumPatAsync_WithNoPatternSubscriptions_ReturnsZero
  7. PubSubNumPatAsync_WithPatternSubscriptions_ReturnsPatternCount

We can certainly re-use the same GlideClient between test methods if convenient, but I think it is best to have separate test methods? Similarly, having separate tests for publish would be useful.

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.

(jedis-compatibility): Add PubSub commands support

5 participants