Skip to content

Redis cluster support Phase 5: complete RedisOps/RedisStreamOps adapter + cluster-aware SCAN #14

Description

@zhchxiao123

What to build

This is the largest of the Java-specific issues: finish the Redis operations adapter layer so that business classes (WorkerRegistry, WorkerRunner, AgentContext, GatewayClient, GatewayWorker, WorkerManager, WakeupController, AvailabilityRouter) depend only on business-level interfaces, never on Jedis/JedisCluster/Pipeline directly. A raw Pipeline-exposing interface doesn't work here — JedisCluster's pipeline capability and standalone Pipeline aren't the same abstraction (different cross-slot limits, different return types), and Phase 3 already established that cross-entity operations get split into independent business-level calls rather than generic pipelines.

Add a RedisOps interface exposing:

  • single-key passthroughs (get, set with ttl, hgetAll, sadd, expire)
  • business-level batch operations named for what they do, not how (saveSessionExecution, updateWorkerAdminState, writeTraceSpan, batchGetWorkerAdminStates, scanKeys)
  • isClusterMode()

Implement StandaloneRedisOps (batch ops use Pipeline+sync internally where all keys are guaranteed same-node) and ClusterRedisOps (batch ops split into independent calls per Phase 3's patterns, or use JedisCluster pipelining only where Phase 2a's hash tags guarantee same-slot).

Extend RedisStreamOps (from the Phase 4 prefactor, #12) with the remaining stream operations not yet covered: xadd taking an explicit XAddOptions type (noTrim() vs trimTo(maxLen), where trimming is always approximate — never expose a way to request exact O(N) trimming), xpendingRange (PEL range read), and xclaim returning the actual claimed StreamEntry list (not just a count — the caller needs the message contents to redispatch them, not just confirmation that claiming succeeded).

Add the cluster-aware scanKeys(pattern, limit): JedisCluster has no built-in cross-node SCAN, so this must iterate getClusterNodes() and merge results from each node's Jedis.scan(cursor, params).

Acceptance criteria

  • RedisOps interface finalized with the methods above; StandaloneRedisOps/ClusterRedisOps implemented
  • RedisStreamOps extended with xadd(streamName, fields, XAddOptions), xpendingRange(streamName, groupName, count), xclaim(...) returning claimed entries
  • XAddOptions.trimTo(maxLen) always uses approximate trimming; noTrim() ignores the approximate flag entirely (no ~ emitted)
  • scanKeys implemented via cluster-node iteration + merge for ClusterRedisOps; single-node passthrough for StandaloneRedisOps
  • All listed business classes updated to depend only on RedisOps/RedisStreamOps, with no remaining direct Jedis/JedisCluster/Pipeline imports
  • Contract test suite run against both StandaloneRedisOps/ClusterRedisOps (and StandaloneRedisStreamOps/ClusterRedisStreamOps) asserting identical business semantics

Blocked by

#11, #12

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions