mirror of
				https://github.com/redis/go-redis.git
				synced 2025-11-04 02:33:24 +03:00 
			
		
		
		
	- At present, the `scan` command is dispatched to a random slot. - As far as I can tell, the scanX family of commands are not cluster aware (e.g. don't redirect the client to the correct slot). - You can see [here](869dc0bb66/src/main/java/redis/clients/jedis/ShardedCommandObjects.java (L101)), the Jedis client calling `processKey` on the match argument, and this is what this PR also does. We've had this patch running in production, and it seems to work well for us. For further thought: - Continuing looking at other Redis clients (e.g. Jedis), they outright [reject as invalid](869dc0bb66/src/main/java/redis/clients/jedis/ShardedCommandObjects.java (L98)) any scan command that does not include a hash-tag. Presumably this has the advantage of users not being surprised when their scan produces no results when a random server is picked. - Perhaps it would be sensible for go-redis to do the same also? Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com>
		
			
				
	
	
		
			224 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			224 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package redis
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
 | 
						|
	"github.com/redis/go-redis/v9/internal/hashtag"
 | 
						|
)
 | 
						|
 | 
						|
type SetCmdable interface {
 | 
						|
	SAdd(ctx context.Context, key string, members ...interface{}) *IntCmd
 | 
						|
	SCard(ctx context.Context, key string) *IntCmd
 | 
						|
	SDiff(ctx context.Context, keys ...string) *StringSliceCmd
 | 
						|
	SDiffStore(ctx context.Context, destination string, keys ...string) *IntCmd
 | 
						|
	SInter(ctx context.Context, keys ...string) *StringSliceCmd
 | 
						|
	SInterCard(ctx context.Context, limit int64, keys ...string) *IntCmd
 | 
						|
	SInterStore(ctx context.Context, destination string, keys ...string) *IntCmd
 | 
						|
	SIsMember(ctx context.Context, key string, member interface{}) *BoolCmd
 | 
						|
	SMIsMember(ctx context.Context, key string, members ...interface{}) *BoolSliceCmd
 | 
						|
	SMembers(ctx context.Context, key string) *StringSliceCmd
 | 
						|
	SMembersMap(ctx context.Context, key string) *StringStructMapCmd
 | 
						|
	SMove(ctx context.Context, source, destination string, member interface{}) *BoolCmd
 | 
						|
	SPop(ctx context.Context, key string) *StringCmd
 | 
						|
	SPopN(ctx context.Context, key string, count int64) *StringSliceCmd
 | 
						|
	SRandMember(ctx context.Context, key string) *StringCmd
 | 
						|
	SRandMemberN(ctx context.Context, key string, count int64) *StringSliceCmd
 | 
						|
	SRem(ctx context.Context, key string, members ...interface{}) *IntCmd
 | 
						|
	SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd
 | 
						|
	SUnion(ctx context.Context, keys ...string) *StringSliceCmd
 | 
						|
	SUnionStore(ctx context.Context, destination string, keys ...string) *IntCmd
 | 
						|
}
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
func (c cmdable) SAdd(ctx context.Context, key string, members ...interface{}) *IntCmd {
 | 
						|
	args := make([]interface{}, 2, 2+len(members))
 | 
						|
	args[0] = "sadd"
 | 
						|
	args[1] = key
 | 
						|
	args = appendArgs(args, members)
 | 
						|
	cmd := NewIntCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SCard(ctx context.Context, key string) *IntCmd {
 | 
						|
	cmd := NewIntCmd(ctx, "scard", key)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SDiff(ctx context.Context, keys ...string) *StringSliceCmd {
 | 
						|
	args := make([]interface{}, 1+len(keys))
 | 
						|
	args[0] = "sdiff"
 | 
						|
	for i, key := range keys {
 | 
						|
		args[1+i] = key
 | 
						|
	}
 | 
						|
	cmd := NewStringSliceCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SDiffStore(ctx context.Context, destination string, keys ...string) *IntCmd {
 | 
						|
	args := make([]interface{}, 2+len(keys))
 | 
						|
	args[0] = "sdiffstore"
 | 
						|
	args[1] = destination
 | 
						|
	for i, key := range keys {
 | 
						|
		args[2+i] = key
 | 
						|
	}
 | 
						|
	cmd := NewIntCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SInter(ctx context.Context, keys ...string) *StringSliceCmd {
 | 
						|
	args := make([]interface{}, 1+len(keys))
 | 
						|
	args[0] = "sinter"
 | 
						|
	for i, key := range keys {
 | 
						|
		args[1+i] = key
 | 
						|
	}
 | 
						|
	cmd := NewStringSliceCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SInterCard(ctx context.Context, limit int64, keys ...string) *IntCmd {
 | 
						|
	numKeys := len(keys)
 | 
						|
	args := make([]interface{}, 4+numKeys)
 | 
						|
	args[0] = "sintercard"
 | 
						|
	args[1] = numKeys
 | 
						|
	for i, key := range keys {
 | 
						|
		args[2+i] = key
 | 
						|
	}
 | 
						|
	args[2+numKeys] = "limit"
 | 
						|
	args[3+numKeys] = limit
 | 
						|
	cmd := NewIntCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SInterStore(ctx context.Context, destination string, keys ...string) *IntCmd {
 | 
						|
	args := make([]interface{}, 2+len(keys))
 | 
						|
	args[0] = "sinterstore"
 | 
						|
	args[1] = destination
 | 
						|
	for i, key := range keys {
 | 
						|
		args[2+i] = key
 | 
						|
	}
 | 
						|
	cmd := NewIntCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SIsMember(ctx context.Context, key string, member interface{}) *BoolCmd {
 | 
						|
	cmd := NewBoolCmd(ctx, "sismember", key, member)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
// SMIsMember Redis `SMISMEMBER key member [member ...]` command.
 | 
						|
func (c cmdable) SMIsMember(ctx context.Context, key string, members ...interface{}) *BoolSliceCmd {
 | 
						|
	args := make([]interface{}, 2, 2+len(members))
 | 
						|
	args[0] = "smismember"
 | 
						|
	args[1] = key
 | 
						|
	args = appendArgs(args, members)
 | 
						|
	cmd := NewBoolSliceCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
// SMembers Redis `SMEMBERS key` command output as a slice.
 | 
						|
func (c cmdable) SMembers(ctx context.Context, key string) *StringSliceCmd {
 | 
						|
	cmd := NewStringSliceCmd(ctx, "smembers", key)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
// SMembersMap Redis `SMEMBERS key` command output as a map.
 | 
						|
func (c cmdable) SMembersMap(ctx context.Context, key string) *StringStructMapCmd {
 | 
						|
	cmd := NewStringStructMapCmd(ctx, "smembers", key)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SMove(ctx context.Context, source, destination string, member interface{}) *BoolCmd {
 | 
						|
	cmd := NewBoolCmd(ctx, "smove", source, destination, member)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
// SPop Redis `SPOP key` command.
 | 
						|
func (c cmdable) SPop(ctx context.Context, key string) *StringCmd {
 | 
						|
	cmd := NewStringCmd(ctx, "spop", key)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
// SPopN Redis `SPOP key count` command.
 | 
						|
func (c cmdable) SPopN(ctx context.Context, key string, count int64) *StringSliceCmd {
 | 
						|
	cmd := NewStringSliceCmd(ctx, "spop", key, count)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
// SRandMember Redis `SRANDMEMBER key` command.
 | 
						|
func (c cmdable) SRandMember(ctx context.Context, key string) *StringCmd {
 | 
						|
	cmd := NewStringCmd(ctx, "srandmember", key)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
// SRandMemberN Redis `SRANDMEMBER key count` command.
 | 
						|
func (c cmdable) SRandMemberN(ctx context.Context, key string, count int64) *StringSliceCmd {
 | 
						|
	cmd := NewStringSliceCmd(ctx, "srandmember", key, count)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SRem(ctx context.Context, key string, members ...interface{}) *IntCmd {
 | 
						|
	args := make([]interface{}, 2, 2+len(members))
 | 
						|
	args[0] = "srem"
 | 
						|
	args[1] = key
 | 
						|
	args = appendArgs(args, members)
 | 
						|
	cmd := NewIntCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SUnion(ctx context.Context, keys ...string) *StringSliceCmd {
 | 
						|
	args := make([]interface{}, 1+len(keys))
 | 
						|
	args[0] = "sunion"
 | 
						|
	for i, key := range keys {
 | 
						|
		args[1+i] = key
 | 
						|
	}
 | 
						|
	cmd := NewStringSliceCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SUnionStore(ctx context.Context, destination string, keys ...string) *IntCmd {
 | 
						|
	args := make([]interface{}, 2+len(keys))
 | 
						|
	args[0] = "sunionstore"
 | 
						|
	args[1] = destination
 | 
						|
	for i, key := range keys {
 | 
						|
		args[2+i] = key
 | 
						|
	}
 | 
						|
	cmd := NewIntCmd(ctx, args...)
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (c cmdable) SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd {
 | 
						|
	args := []interface{}{"sscan", key, cursor}
 | 
						|
	if match != "" {
 | 
						|
		args = append(args, "match", match)
 | 
						|
	}
 | 
						|
	if count > 0 {
 | 
						|
		args = append(args, "count", count)
 | 
						|
	}
 | 
						|
	cmd := NewScanCmd(ctx, c, args...)
 | 
						|
	if hashtag.Present(match) {
 | 
						|
		cmd.SetFirstKeyPos(4)
 | 
						|
	}
 | 
						|
	_ = c(ctx, cmd)
 | 
						|
	return cmd
 | 
						|
}
 |