mirror of
https://github.com/redis/go-redis.git
synced 2026-10-28 08:24:54 +08:00
* feat(search): add FT.ALIASLIST support (Redis 8.10)
- FTAliasList(ctx, index) returns the aliases of an index as an
unordered, server-deduplicated collection; an alias-free index
yields an empty result, not an error
- server errors (SEARCH_INDEX_NOT_FOUND, NOPERM) propagate unchanged;
the argument must be an index name, aliases are not resolved
- wire-format unit test plus a RESP2/RESP3 integration test that gates
on the live server supporting the command (the REDIS_VERSION float
cannot tell 8.10 from 8.1)
- runnable example under example/ft-aliaslist
* fix(search): route FT.ALIASLIST as keyless in cluster
The static FT policy table covers aliasadd/aliasupdate/aliasdel but
not aliaslist, so the default cluster resolver returned nil and a cold
command-info cache fell back to treating the index name as a key.
Register it keyless and read-only like the other alias commands.
* test(search): version-gate FT.ALIASLIST specs
Replace the standalone probe test (unknown-command sniffing plus the
REDIS_ALIASLIST_TEST_ADDR env var) with Ginkgo specs in the main suite,
gated on SkipBeforeRedisVersion("8.10"), which compares major.minor as
integers and so distinguishes 8.10 from 8.1. The specs cover RESP2 and
RESP3 and follow the suite conventions (FlushDB per spec, :6379).
* chore(deps): bump grpc to v1.82.1 in otel examples
govulncheck fails every PR on GO-2026-6061 in google.golang.org/grpc
v1.80.0 (indirect via the OTLP exporters); v1.82.1 carries the fix.
* fix(search): route policy-table keyless commands on cold cache
ClusterClient.process computes the slot before routeAndRun consults
the policy table, and with a cold command-info cache
cmdFirstKeyPosWithInfo fell back to treating argument 1 as a key — so
FT.ALIASLIST (and the other keyless FT commands) hashed the index name
into a slot for the first calls of a process. Consult the static
policy table there: entries with default request routing and a keyless
response policy resolve to keyless. Key-carrying (ft.suglen) and
specially-routed (ft.cursor) commands keep the old fallback.
---------
Co-authored-by: ofekshenawa <104765379+ofekshenawa@users.noreply.github.com>
45 lines
1.0 KiB
Go
45 lines
1.0 KiB
Go
package redis
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// TestFTAliasListArgs pins the wire format of FT.ALIASLIST: exactly one
|
|
// argument, the index name, with no optional arguments.
|
|
func TestFTAliasListArgs(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
tests := []struct {
|
|
name string
|
|
call func(c cmdable) Cmder
|
|
want []interface{}
|
|
}{
|
|
{
|
|
name: "index_only",
|
|
call: func(c cmdable) Cmder { return c.FTAliasList(ctx, "idx") },
|
|
want: []interface{}{"FT.ALIASLIST", "idx"},
|
|
},
|
|
{
|
|
name: "index_passed_verbatim",
|
|
call: func(c cmdable) Cmder { return c.FTAliasList(ctx, "My-Index:v2") },
|
|
want: []interface{}{"FT.ALIASLIST", "My-Index:v2"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var captured Cmder
|
|
c := captureCmdable(&captured)
|
|
cmd := tt.call(c)
|
|
if cmd == nil {
|
|
t.Fatalf("command builder returned nil")
|
|
}
|
|
if !reflect.DeepEqual(cmd.Args(), tt.want) {
|
|
t.Errorf("args mismatch\n got: %#v\nwant: %#v", cmd.Args(), tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|