Summary
ScriptInvokeAsync defaults to Route.AllPrimaries when no route is provided via ClusterScriptOptions. This sends the script to every primary node, which is incorrect for key-targeted scripts.
Description
In cluster mode, all keys in a script must hash to the same slot (CROSSSLOT error otherwise). Glide-core's default routing for EVALSHA is ThirdArgAfterKeyCount — it routes by the first key's slot, or falls back to Random if no keys are provided.
Current behavior:
Route route = options.Route ?? Route.AllPrimaries;
When ClusterScriptOptions.Route is null, the script is sent to all primaries regardless of whether keys are provided.
Expected behavior:
The cluster client should follow the same pattern as Python, Node, Go, and Java:
- Key-based invocation (inherited from BaseClient): takes keys + args, glide-core routes by key slot, returns single value
- Route-based invocation (cluster-only): takes args + explicit Route parameter, returns
ClusterValue<T>
Proposed Solution
Remove:
ClusterScriptOptions class entirely (sources/Valkey.Glide/scripting/ClusterScriptOptions.cs)
IGlideClusterClient.ScriptInvokeAsync(Script, ClusterScriptOptions, CancellationToken) — interface declaration
GlideClusterClient.ScriptInvokeAsync(Script, ClusterScriptOptions, CancellationToken) — implementation
Keep (already exists, inherited by cluster client):
BaseClient.ScriptInvokeAsync(Script, CancellationToken) → Task<ValkeyResult> — no keys, no args
BaseClient.ScriptInvokeAsync(Script, ScriptOptions, CancellationToken) → Task<ValkeyResult> — with keys + args, glide-core routes by key slot
Add (new, cluster-only — for explicit route-based invocation without keys):
IGlideClusterClient.ScriptInvokeAsync(Script, Route, CancellationToken) → Task<ClusterValue<ValkeyResult>>
IGlideClusterClient.ScriptInvokeAsync(Script, string[], Route, CancellationToken) → Task<ClusterValue<ValkeyResult>> — with args
Effort & Severity Estimates
- Effort: medium
- Severity: medium
Related
Summary
ScriptInvokeAsyncdefaults toRoute.AllPrimarieswhen no route is provided viaClusterScriptOptions. This sends the script to every primary node, which is incorrect for key-targeted scripts.Description
In cluster mode, all keys in a script must hash to the same slot (CROSSSLOT error otherwise). Glide-core's default routing for
EVALSHAisThirdArgAfterKeyCount— it routes by the first key's slot, or falls back toRandomif no keys are provided.Current behavior:
When
ClusterScriptOptions.Routeis null, the script is sent to all primaries regardless of whether keys are provided.Expected behavior:
The cluster client should follow the same pattern as Python, Node, Go, and Java:
ClusterValue<T>Proposed Solution
Remove:
ClusterScriptOptionsclass entirely (sources/Valkey.Glide/scripting/ClusterScriptOptions.cs)IGlideClusterClient.ScriptInvokeAsync(Script, ClusterScriptOptions, CancellationToken)— interface declarationGlideClusterClient.ScriptInvokeAsync(Script, ClusterScriptOptions, CancellationToken)— implementationKeep (already exists, inherited by cluster client):
BaseClient.ScriptInvokeAsync(Script, CancellationToken)→Task<ValkeyResult>— no keys, no argsBaseClient.ScriptInvokeAsync(Script, ScriptOptions, CancellationToken)→Task<ValkeyResult>— with keys + args, glide-core routes by key slotAdd (new, cluster-only — for explicit route-based invocation without keys):
IGlideClusterClient.ScriptInvokeAsync(Script, Route, CancellationToken)→Task<ClusterValue<ValkeyResult>>IGlideClusterClient.ScriptInvokeAsync(Script, string[], Route, CancellationToken)→Task<ClusterValue<ValkeyResult>>— with argsEffort & Severity Estimates
Related