Skip to content

feat(java): Implement missing eval & script commands - #5125

Merged
xShinnRyuu merged 11 commits into
valkey-io:mainfrom
Sasidharan3094:implement-script-eval-commands
Jan 9, 2026
Merged

xShinnRyuu merged 11 commits into
valkey-io:mainfrom
Sasidharan3094:implement-script-eval-commands

Conversation

@Sasidharan3094

@Sasidharan3094 Sasidharan3094 commented Jan 6, 2026

Copy link
Copy Markdown
Contributor

This PR fixes Issue
Implement EVAL_RO, EVALSHA_RO, and SCRIPT DEBUG Commands.

Summary

This PR adds support for 3 read-only Lua script execution and debugging commands to the Java client, enabling read-only script execution and script debugging capabilities for both standalone and cluster modes with full binary-safe support.

Commands Implemented

Base Commands (Standalone + Cluster)

EVAL_RO - Execute read-only Lua scripts (Valkey 7.0+)
EVALSHA_RO - Execute read-only scripts by SHA1 hash (Valkey 7.0+)
SCRIPT DEBUG - Set Lua script debugging mode

Motivation

These commands provide essential capabilities for Lua script execution and debugging:

  • Read-only script execution - Execute scripts that only read data without modifying it, allowing execution on replicas
  • Performance optimization - Read-only scripts can be routed to replica nodes to reduce load on primaries
  • Script debugging - Enable debugging modes for Lua script development and troubleshooting
  • Feature parity - Matches capabilities available in Lettuce and Jedis clients

Changes Made

1. Enum Definition

ScriptDebugMode.java - Created new enum with 3 modes:

public enum ScriptDebugMode {
    YES,    // Enable asynchronous Lua debugging
    SYNC,   // Enable synchronous Lua debugging  
    NO      // Disable Lua debugging
}

2. Interface Definitions

ScriptingAndFunctionsBaseCommands.java - Added 9 method signatures:

// EVAL_RO - 4 overloads
CompletableFuture<Object> evalReadOnly(String script);
CompletableFuture<Object> evalReadOnly(String script, String[] keys, String[] args);
CompletableFuture<Object> evalReadOnly(GlideString script);
CompletableFuture<Object> evalReadOnly(GlideString script, GlideString[] keys, GlideString[] args);

// EVALSHA_RO - 4 overloads
CompletableFuture<Object> evalshaReadOnly(String sha1);
CompletableFuture<Object> evalshaReadOnly(String sha1, String[] keys, String[] args);
CompletableFuture<Object> evalshaReadOnly(GlideString sha1);
CompletableFuture<Object> evalshaReadOnly(GlideString sha1, GlideString[] keys, GlideString[] args);

// SCRIPT DEBUG - 1 method
CompletableFuture<String> scriptDebug(ScriptDebugMode mode);

3. Client Implementations

GlideClient.java - Implemented 9 methods:

  • All methods return CompletableFuture<Object> for eval commands or CompletableFuture<String> for debug
  • Uses protobuf request types (EvalReadOnly, EvalShaReadOnly, ScriptDebug)
  • Supports both String[] and GlideString[] for binary safety
  • Properly formats arguments with key count for Lua script execution

GlideClusterClient.java - Implemented 9 methods:

  • Inherits all 9 method implementations from ScriptingAndFunctionsBaseCommands
  • Works seamlessly with cluster routing
  • Supports execution on both primary and replica nodes

4. Rust Core Implementation

glide-core/src/request_type.rs - Added protobuf mappings and command implementations:

// Protobuf-to-RequestType mappings
ProtobufRequestType::EvalReadOnly => RequestType::EvalReadOnly,
ProtobufRequestType::EvalShaReadOnly => RequestType::EvalShaReadOnly,
ProtobufRequestType::ScriptDebug => RequestType::ScriptDebug,

// Command implementations in get_command()
RequestType::EvalReadOnly => Some(cmd("EVAL_RO")),
RequestType::EvalShaReadOnly => Some(cmd("EVALSHA_RO")),
RequestType::ScriptDebug => Some(get_two_word_command("SCRIPT", "DEBUG")),

5. Integration Tests

SharedCommandTests.java - Added 3 comprehensive tests:

  1. evalReadOnly_test - Tests all 4 evalReadOnly overloads:

    • Simple script without keys/args
    • Script with keys and args
    • Binary-safe simple script
    • Binary-safe script with keys and args
    • Read-only script accessing existing keys
  2. evalshaReadOnly_test - Tests all 4 evalshaReadOnly overloads:

    • Simple execution by SHA1
    • Execution by SHA1 with keys and args
    • Binary-safe SHA1 execution
    • Binary-safe SHA1 with keys and args
    • Error handling for non-existent SHA1
  3. scriptDebug_test - Tests all 3 debug modes:

    • Enable async debugging mode (YES)
    • Enable sync debugging mode (SYNC)
    • Disable debugging mode (NO)
    • Verify scripts still execute after mode changes

All tests run against 4 configurations:

  • ✅ Standalone RESP3
  • ✅ Cluster RESP3
  • ✅ Standalone RESP2
  • ✅ Cluster RESP2

6. Documentation

  • Updated CHANGELOG.md with new commands
  • Added comprehensive JavaDoc comments for all methods
  • Included usage examples in method documentation
  • Added @since Valkey 7.0 annotations for version requirements

API Design

Async-First Pattern

All commands return CompletableFuture following GLIDE's async-first design:

// Execute read-only script
Object result = client.evalReadOnly("return 'Hello, World!'").get();

// Execute by SHA1
Object result = client.evalshaReadOnly(sha1).get();

// Set debug mode
String response = client.scriptDebug(ScriptDebugMode.YES).get();

Binary-Safe Support

Each eval command has both String[] and GlideString[] variants:

// String variant
Object result = client.evalReadOnly(
    "return {KEYS[1], ARGV[1]}", 
    new String[]{"key1"}, 
    new String[]{"arg1"}
).get();

// Binary-safe variant
GlideString script = gs("return redis.call('GET', KEYS[1])");
GlideString key = gs(new byte[]{(byte) 0xE2, 0x28, (byte) 0xA1});
Object result = client.evalReadOnly(
    script, 
    new GlideString[]{key}, 
    new GlideString[]{}
).get();

Script Debugging Modes

Three debugging modes available via ScriptDebugMode enum:

// Enable asynchronous debugging
client.scriptDebug(ScriptDebugMode.YES).get();

// Enable synchronous debugging (blocks server)
client.scriptDebug(ScriptDebugMode.SYNC).get();

// Disable debugging
client.scriptDebug(ScriptDebugMode.NO).get();

Testing

Test Coverage

✅ All 12 tests passing (3 test methods × 4 configurations each)
✅ Standalone mode tests (RESP2 + RESP3)
✅ Cluster mode tests (RESP2 + RESP3)
✅ Binary-safe operations with GlideString
✅ Error handling for invalid SHA1
✅ Script execution after debug mode changes
✅ Keys and arguments passing
✅ All debug modes (YES, SYNC, NO)

Test Results

SharedCommandTests > evalReadOnly_test
  [1] standalone RESP3 PASSED
  [2] cluster RESP3 PASSED
  [3] standalone RESP2 PASSED
  [4] cluster RESP2 PASSED

SharedCommandTests > evalshaReadOnly_test
  [1] standalone RESP3 PASSED
  [2] cluster RESP3 PASSED
  [3] standalone RESP2 PASSED
  [4] cluster RESP2 PASSED

SharedCommandTests > scriptDebug_test
  [1] standalone RESP3 PASSED
  [2] cluster RESP3 PASSED
  [3] standalone RESP2 PASSED
  [4] cluster RESP2 PASSED

Key Features

Read-Only Script Execution

  • Scripts can be executed on replica nodes for load distribution
  • Safer execution - prevents accidental data modifications
  • Compatible with read-from-replica configurations

SHA1-Based Execution

  • Reduced network overhead by using SHA1 hash instead of full script
  • Scripts must be pre-loaded using SCRIPT LOAD or Script object
  • Efficient for frequently executed scripts

Debugging Support

  • YES mode: Asynchronous debugging - doesn't block server
  • SYNC mode: Synchronous debugging - blocks server for step-through debugging
  • NO mode: Disables debugging for production use

Version Requirements

  • EVAL_RO: Valkey 7.0 and above
  • EVALSHA_RO: Valkey 7.0 and above
  • SCRIPT DEBUG: All supported Valkey versions

Files Modified

Java Client

  • java/client/src/main/java/glide/api/models/commands/ScriptDebugMode.java (created)
  • java/client/src/main/java/glide/api/commands/ScriptingAndFunctionsBaseCommands.java
  • java/client/src/main/java/glide/api/GlideClient.java
  • java/client/src/main/java/glide/api/GlideClusterClient.java
  • java/integTest/src/test/java/glide/SharedCommandTests.java

Rust Core

  • glide-core/src/request_type.rs

Documentation

  • CHANGELOG.md

Usage Examples

Basic Read-Only Script Execution

// Simple script
String script = "return 'Hello, World!'";
Object result = client.evalReadOnly(script).get();
assert result.equals("Hello, World!");

// Script with keys and arguments
String script = "return {KEYS[1], ARGV[1]}";
Object[] result = (Object[]) client.evalReadOnly(
    script, 
    new String[]{"mykey"}, 
    new String[]{"myarg"}
).get();
assert result[0].equals("mykey");
assert result[1].equals("myarg");

SHA1-Based Execution

// Load script and get SHA1
String script = "return redis.call('GET', KEYS[1])";
Script scriptObj = new Script(script, false);
client.invokeScript(scriptObj).get();
String sha1 = scriptObj.getHash();

// Execute by SHA1
client.set("mykey", "myvalue").get();
Object result = client.evalshaReadOnly(
    sha1, 
    new String[]{"mykey"}, 
    new String[]{}
).get();
assert result.equals("myvalue");

Script Debugging

// Enable debugging for development
client.scriptDebug(ScriptDebugMode.YES).get();

// Run your script
Object result = client.evalReadOnly("return 42").get();

// Disable debugging for production
client.scriptDebug(ScriptDebugMode.NO).get();

Binary-Safe Operations

// Binary-safe script execution
byte[] binaryData = {(byte) 0x01, (byte) 0x00, (byte) 0xFF};
GlideString key = gs(binaryData);
GlideString script = gs("return KEYS[1]");

Object result = client.evalReadOnly(
    script,
    new GlideString[]{key},
    new GlideString[]{}
).get();

assert ((GlideString) result).getBytes().equals(binaryData);

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>
Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>
@Sasidharan3094
Sasidharan3094 requested a review from a team as a code owner January 6, 2026 15:29
Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>
Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>
@Sasidharan3094

Copy link
Copy Markdown
Contributor Author

@yipin-chen @alexr-bq Please review this.

@yipin-chen
yipin-chen requested a review from affonsov January 7, 2026 17:58
Comment thread java/client/src/main/java/glide/api/GlideClient.java
Comment thread glide-core/src/request_type.rs Outdated
Comment thread java/client/src/main/java/glide/api/GlideClient.java Outdated

@affonsov affonsov left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Update the CHANGELOG.md file

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>
Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>
Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>
@Sasidharan3094

Sasidharan3094 commented Jan 8, 2026

Copy link
Copy Markdown
Contributor Author

@affonsov @jduo @xShinnRyuu Addressed all the comments. Please review again and Rust Tests/Lint failed which are not related to these changes. Please check that. Thanks.

Signed-off-by: Sasidharan3094 <sasidharan.gopal94@gmail.com>

@xShinnRyuu xShinnRyuu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for making the changes LGTM.

There is currently some issues with the Rust tests / lint (pull_request in the CICD workflow. I will rerun the CICD, once these issues have been resolved.

@xShinnRyuu
xShinnRyuu merged commit 59744fd into valkey-io:main Jan 9, 2026
70 of 71 checks passed
tdschwarz pushed a commit to tdschwarz/valkey-glide that referenced this pull request Jan 9, 2026
* Implement missing evalsha & script command for java client

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Applying splotlessapply changes

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Update test for script debug

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Fixing tests

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Addressing review comments

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Updating CHANGELOG

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Fixing spotlessApply changes

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

---------

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>
Signed-off-by: Sasidharan3094 <sasidharan.gopal94@gmail.com>
Co-authored-by: Thomas Zhou <54688146+xShinnRyuu@users.noreply.github.com>
affonsov pushed a commit that referenced this pull request Aug 21, 2026
* Implement missing evalsha & script command for java client

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Applying splotlessapply changes

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Update test for script debug

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Fixing tests

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Addressing review comments

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Updating CHANGELOG

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

* Fixing spotlessApply changes

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>

---------

Signed-off-by: Sasidharan Gopal <sasidharan.gopal94@gmail.com>
Signed-off-by: Sasidharan3094 <sasidharan.gopal94@gmail.com>
Co-authored-by: Thomas Zhou <54688146+xShinnRyuu@users.noreply.github.com>
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.

feat(Java): Implement missing eval & script commands

4 participants