mattermost-community-enterp.../vendor/github.com/redis/rueidis/helper.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

329 lines
9.6 KiB
Go

package rueidis
import (
"context"
"errors"
"iter"
"time"
intl "github.com/redis/rueidis/internal/cmds"
)
// MGetCache is a helper that consults the client-side caches with multiple keys by grouping keys within the same slot into multiple GETs
func MGetCache(client Client, ctx context.Context, ttl time.Duration, keys []string) (ret map[string]RedisMessage, err error) {
if len(keys) == 0 {
return make(map[string]RedisMessage), nil
}
if isCacheDisabled(client) {
return MGet(client, ctx, keys)
}
cmds := mgetcachecmdsp.Get(len(keys), len(keys))
defer mgetcachecmdsp.Put(cmds)
for i := range cmds.s {
cmds.s[i] = CT(client.B().Get().Key(keys[i]).Cache(), ttl)
}
return doMultiCache(client, ctx, cmds.s, keys)
}
func isCacheDisabled(client Client) bool {
switch c := client.(type) {
case *singleClient:
return c.DisableCache
case *standalone:
return c.primary.Load().DisableCache
case *sentinelClient:
return c.mOpt != nil && c.mOpt.DisableCache
case *clusterClient:
return c.opt != nil && c.opt.DisableCache
}
return false
}
// MGet is a helper that consults the redis directly with multiple keys by grouping keys within the same slot into MGET or multiple GETs
func MGet(client Client, ctx context.Context, keys []string) (ret map[string]RedisMessage, err error) {
if len(keys) == 0 {
return make(map[string]RedisMessage), nil
}
switch client.(type) {
case *singleClient, *standalone, *sentinelClient:
return clientMGet(client, ctx, client.B().Mget().Key(keys...).Build(), keys)
}
cmds := mgetcmdsp.Get(len(keys), len(keys))
defer mgetcmdsp.Put(cmds)
for i := range cmds.s {
cmds.s[i] = client.B().Get().Key(keys[i]).Build()
}
return doMultiGet(client, ctx, cmds.s, keys)
}
// MSet is a helper that consults the redis directly with multiple keys by grouping keys within the same slot into MSETs or multiple SETs
func MSet(client Client, ctx context.Context, kvs map[string]string) map[string]error {
if len(kvs) == 0 {
return make(map[string]error)
}
switch client.(type) {
case *singleClient, *standalone, *sentinelClient:
return clientMSet(client, ctx, "MSET", kvs, make(map[string]error, len(kvs)))
}
cmds := mgetcmdsp.Get(0, len(kvs))
defer mgetcmdsp.Put(cmds)
for k, v := range kvs {
cmds.s = append(cmds.s, client.B().Set().Key(k).Value(v).Build().Pin())
}
return doMultiSet(client, ctx, cmds.s)
}
// MDel is a helper that consults the redis directly with multiple keys by grouping keys within the same slot into DELs
func MDel(client Client, ctx context.Context, keys []string) map[string]error {
if len(keys) == 0 {
return make(map[string]error)
}
switch client.(type) {
case *singleClient, *standalone, *sentinelClient:
return clientMDel(client, ctx, keys)
}
cmds := mgetcmdsp.Get(len(keys), len(keys))
defer mgetcmdsp.Put(cmds)
for i, k := range keys {
cmds.s[i] = client.B().Del().Key(k).Build().Pin()
}
return doMultiSet(client, ctx, cmds.s)
}
// MSetNX is a helper that consults the redis directly with multiple keys by grouping keys within the same slot into MSETNXs or multiple SETNXs
func MSetNX(client Client, ctx context.Context, kvs map[string]string) map[string]error {
if len(kvs) == 0 {
return make(map[string]error)
}
switch client.(type) {
case *singleClient, *standalone, *sentinelClient:
return clientMSet(client, ctx, "MSETNX", kvs, make(map[string]error, len(kvs)))
}
cmds := mgetcmdsp.Get(0, len(kvs))
defer mgetcmdsp.Put(cmds)
for k, v := range kvs {
cmds.s = append(cmds.s, client.B().Set().Key(k).Value(v).Nx().Build().Pin())
}
return doMultiSet(client, ctx, cmds.s)
}
// JsonMGetCache is a helper that consults the client-side caches with multiple keys by grouping keys within the same slot into multiple JSON.GETs
func JsonMGetCache(client Client, ctx context.Context, ttl time.Duration, keys []string, path string) (ret map[string]RedisMessage, err error) {
if len(keys) == 0 {
return make(map[string]RedisMessage), nil
}
cmds := mgetcachecmdsp.Get(len(keys), len(keys))
defer mgetcachecmdsp.Put(cmds)
for i := range cmds.s {
cmds.s[i] = CT(client.B().JsonGet().Key(keys[i]).Path(path).Cache(), ttl)
}
return doMultiCache(client, ctx, cmds.s, keys)
}
// JsonMGet is a helper that consults redis directly with multiple keys by grouping keys within the same slot into JSON.MGETs or multiple JSON.GETs
func JsonMGet(client Client, ctx context.Context, keys []string, path string) (ret map[string]RedisMessage, err error) {
if len(keys) == 0 {
return make(map[string]RedisMessage), nil
}
switch client.(type) {
case *singleClient, *standalone, *sentinelClient:
return clientMGet(client, ctx, client.B().JsonMget().Key(keys...).Path(path).Build(), keys)
}
cmds := mgetcmdsp.Get(len(keys), len(keys))
defer mgetcmdsp.Put(cmds)
for i := range cmds.s {
cmds.s[i] = client.B().JsonGet().Key(keys[i]).Path(path).Build()
}
return doMultiGet(client, ctx, cmds.s, keys)
}
// JsonMSet is a helper that consults redis directly with multiple keys by grouping keys within the same slot into JSON.MSETs or multiple JSON.SETs
func JsonMSet(client Client, ctx context.Context, kvs map[string]string, path string) map[string]error {
if len(kvs) == 0 {
return make(map[string]error)
}
switch client.(type) {
case *singleClient, *standalone, *sentinelClient:
return clientJSONMSet(client, ctx, kvs, path, make(map[string]error, len(kvs)))
}
cmds := mgetcmdsp.Get(0, len(kvs))
defer mgetcmdsp.Put(cmds)
for k, v := range kvs {
cmds.s = append(cmds.s, client.B().JsonSet().Key(k).Path(path).Value(v).Build().Pin())
}
return doMultiSet(client, ctx, cmds.s)
}
// DecodeSliceOfJSON is a helper that struct-scans each RedisMessage into dest, which must be a slice of the pointer.
func DecodeSliceOfJSON[T any](result RedisResult, dest *[]T) error {
values, err := result.ToArray()
if err != nil {
return err
}
ts := make([]T, len(values))
for i, v := range values {
var t T
if err = v.DecodeJSON(&t); err != nil {
if IsRedisNil(err) {
continue
}
return err
}
ts[i] = t
}
*dest = ts
return nil
}
func clientMGet(client Client, ctx context.Context, cmd Completed, keys []string) (ret map[string]RedisMessage, err error) {
arr, err := client.Do(ctx, cmd).ToArray()
if err != nil {
return nil, err
}
return arrayToKV(make(map[string]RedisMessage, len(keys)), arr, keys), nil
}
func clientMSet(client Client, ctx context.Context, mset string, kvs map[string]string, ret map[string]error) map[string]error {
cmd := client.B().Arbitrary(mset)
for k, v := range kvs {
cmd = cmd.Args(k, v)
}
ok, err := client.Do(ctx, cmd.Build()).AsBool()
if err == nil && !ok {
err = ErrMSetNXNotSet
}
for k := range kvs {
ret[k] = err
}
return ret
}
func clientJSONMSet(client Client, ctx context.Context, kvs map[string]string, path string, ret map[string]error) map[string]error {
cmd := intl.JsonMsetTripletValue(client.B().JsonMset())
for k, v := range kvs {
cmd = cmd.Key(k).Path(path).Value(v)
}
err := client.Do(ctx, cmd.Build()).Error()
for k := range kvs {
ret[k] = err
}
return ret
}
func clientMDel(client Client, ctx context.Context, keys []string) map[string]error {
err := client.Do(ctx, client.B().Del().Key(keys...).Build()).Error()
ret := make(map[string]error, len(keys))
for _, k := range keys {
ret[k] = err
}
return ret
}
func doMultiCache(cc Client, ctx context.Context, cmds []CacheableTTL, keys []string) (ret map[string]RedisMessage, err error) {
ret = make(map[string]RedisMessage, len(keys))
resps := cc.DoMultiCache(ctx, cmds...)
defer resultsp.Put(&redisresults{s: resps})
for i, resp := range resps {
if err := resp.NonRedisError(); err != nil {
return nil, err
}
ret[keys[i]] = resp.val
}
return ret, nil
}
func doMultiGet(cc Client, ctx context.Context, cmds []Completed, keys []string) (ret map[string]RedisMessage, err error) {
ret = make(map[string]RedisMessage, len(keys))
resps := cc.DoMulti(ctx, cmds...)
defer resultsp.Put(&redisresults{s: resps})
for i, resp := range resps {
if err := resp.NonRedisError(); err != nil {
return nil, err
}
ret[keys[i]] = resp.val
}
return ret, nil
}
func doMultiSet(cc Client, ctx context.Context, cmds []Completed) (ret map[string]error) {
ret = make(map[string]error, len(cmds))
resps := cc.DoMulti(ctx, cmds...)
for i, resp := range resps {
if ret[cmds[i].Commands()[1]] = resp.Error(); resp.NonRedisError() == nil {
intl.PutCompletedForce(cmds[i])
}
}
resultsp.Put(&redisresults{s: resps})
return ret
}
func arrayToKV(m map[string]RedisMessage, arr []RedisMessage, keys []string) map[string]RedisMessage {
for i, resp := range arr {
m[keys[i]] = resp
}
return m
}
// ErrMSetNXNotSet is used in the MSetNX helper when the underlying MSETNX response is 0.
// Ref: https://redis.io/commands/msetnx/
var ErrMSetNXNotSet = errors.New("MSETNX: no key was set")
type Scanner struct {
next func(cursor uint64) (ScanEntry, error)
err error
}
func NewScanner(next func(cursor uint64) (ScanEntry, error)) *Scanner {
return &Scanner{next: next}
}
func (s *Scanner) scan() iter.Seq[[]string] {
return func(yield func([]string) bool) {
var e ScanEntry
for e, s.err = s.next(0); s.err == nil && yield(e.Elements) && e.Cursor != 0; {
e, s.err = s.next(e.Cursor)
}
}
}
func (s *Scanner) Iter() iter.Seq[string] {
return func(yield func(string) bool) {
for vs := range s.scan() {
for _, v := range vs {
if !yield(v) {
return
}
}
}
}
}
func (s *Scanner) Iter2() iter.Seq2[string, string] {
return func(yield func(string, string) bool) {
for vs := range s.scan() {
for i := 0; i+1 < len(vs); i += 2 {
if !yield(vs[i], vs[i+1]) {
return
}
}
}
}
}
func (s *Scanner) Err() error {
return s.err
}