rediscli

package
v1.12.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 4, 2026 License: GPL-3.0 Imports: 28 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (

	// ErrScriptNotFound is returned when a script is not found in the scripts map
	ErrScriptNotFound = errors.New("script not found")
)
View Source
var LuaScripts = map[string]string{

	"IncrementAndExpire": `
local count = redis.call("INCR", KEYS[1])
redis.call("EXPIRE", KEYS[1], ARGV[1])
return count
`,

	"AddToSetAndExpire": `
local result = redis.call("SADD", KEYS[1], ARGV[1])
redis.call("EXPIRE", KEYS[1], ARGV[2])
return result
`,

	"ZAddCountAndExpire": `
redis.call("ZADD", KEYS[1], ARGV[1], ARGV[2])
local count = redis.call("ZCOUNT", KEYS[1], "-inf", "+inf")
redis.call("EXPIRE", KEYS[1], ARGV[4])
redis.call("HSET", KEYS[2], ARGV[3], count)
redis.call("EXPIRE", KEYS[2], ARGV[4])
return count
`,

	"CalculateAdaptiveToleration": `
local positive = tonumber(redis.call("HGET", KEYS[1], "positive") or "0")
local negative = tonumber(redis.call("HGET", KEYS[1], "negative") or "0")
local min_percent = tonumber(ARGV[1])
local max_percent = tonumber(ARGV[2])
local scale_factor = tonumber(ARGV[3])
local static_percent = tonumber(ARGV[4])
local adaptive_enabled = tonumber(ARGV[5]) == 1

-- If adaptive toleration is disabled or there are no positive attempts, use static percentage
if not adaptive_enabled or positive == 0 then
    local max_negative = math.floor((static_percent * positive) / 100)
    return {static_percent, max_negative, positive, negative, 0}
end

-- Calculate adaptive percentage based on positive attempts and scale factor
local percent = min_percent
if positive > 0 then
    -- Calculate percentage between min and max based on positive attempts and scale factor
    local factor = math.min(1, math.log(positive + 1) / math.log(100) * scale_factor)
    percent = math.floor(min_percent + (max_percent - min_percent) * factor)

    -- Ensure percent is within bounds
    percent = math.max(min_percent, math.min(max_percent, percent))
end

-- Calculate maximum allowed negative attempts
local max_negative = math.floor((percent * positive) / 100)

-- Return the calculated percentage, max negative attempts, positive count, positive count, and factor
return {percent, max_negative, positive, negative, 1}
`,

	"AddToSetAndExpireLimit": `
local key = KEYS[1]
local hash = ARGV[1]
local ttl = tonumber(ARGV[2])
local max = tonumber(ARGV[3])

if redis.call('SCARD', key) >= max then
  if redis.call('SISMEMBER', key, hash) == 1 then
    redis.call('EXPIRE', key, ttl)
    return 1
  end
  return 0
end

redis.call('SADD', key, hash)
redis.call('EXPIRE', key, ttl)
return 1
`,

	"ColdStartGraceSeed": `
local c = redis.call('SET', KEYS[1], '1', 'NX', 'EX', ARGV[1])
redis.call('SET', KEYS[2], '1', 'NX', 'EX', ARGV[1])
if c then return 1 else return 0 end
`,

	"UnlockIfTokenMatches": `
if redis.call("GET", KEYS[1]) == ARGV[1] then
  return redis.call("DEL", KEYS[1])
else
  return 0
end
`,

	"RWPSlidingWindow": `
local key = KEYS[1]
local hash = ARGV[1]
local now = tonumber(ARGV[2])
local ttl = tonumber(ARGV[3])
local max = tonumber(ARGV[4])

-- Remove outdated entries
redis.call('ZREMRANGEBYSCORE', key, '-inf', '(' .. (now - ttl))

-- Check if hash already exists
local score = redis.call('ZSCORE', key, hash)
local card = redis.call('ZCARD', key)

-- Always add/update the current hash
redis.call('ZADD', key, now, hash)

-- If we exceed the max unique hashes, remove the oldest one
if redis.call('ZCARD', key) > max then
    redis.call('ZREMRANGEBYRANK', key, 0, 0)
end

redis.call('EXPIRE', key, ttl)

-- Return 1 if it was a repeat (existed before) or if we were below the limit
if score or card < max then
    return 1
end

return 0
`,

	"SlidingWindowCounter": `
local current_key = KEYS[1]
local prev_key = KEYS[2]

local increment = tonumber(ARGV[1])
local weight = tonumber(ARGV[2])
local ttl = tonumber(ARGV[3])
local base_limit = tonumber(ARGV[4] or "-1")

local adaptive_enabled = tonumber(ARGV[5] or "0") == 1
local min_percent = tonumber(ARGV[6] or "0")
local max_percent = tonumber(ARGV[7] or "0")
local scale_factor = tonumber(ARGV[8] or "1")
local static_percent = tonumber(ARGV[9] or "0")
local positive = tonumber(ARGV[10] or "0")

local limit = base_limit

if positive > 0 then
    local percent = static_percent
    if adaptive_enabled then
        local factor = math.min(1, math.log(positive + 1) / math.log(100) * scale_factor)
        percent = math.floor(min_percent + (max_percent - min_percent) * factor)
        percent = math.max(min_percent, math.min(max_percent, percent))
    end
    limit = math.floor(base_limit * (1 + percent / 100))
end

local current_cnt = tonumber(redis.call("GET", current_key) or 0)
if increment > 0 then
    current_cnt = redis.call("INCRBY", current_key, increment)
    if current_cnt == increment then
        redis.call("EXPIRE", current_key, ttl)
    end
end

local prev_cnt = tonumber(redis.call("GET", prev_key) or 0)
local total = current_cnt + (prev_cnt * weight)

local exceeded = 0
if limit >= 0 and total > limit then
    exceeded = 1
end

return {tostring(total), exceeded, tostring(limit)}
`,
}

LuaScripts contains all the Lua scripts used in the application

Functions

func ClearScriptCache added in v1.12.0

func ClearScriptCache()

ClearScriptCache clears the local script SHA1 cache. This is primarily used for testing purposes to ensure scripts are re-uploaded.

func EnsureKeysInSameSlot added in v1.7.7

func EnsureKeysInSameSlot(keys []string, hashTag string) []string

EnsureKeysInSameSlot ensures that all keys hash to the same slot in Redis Cluster by adding a common hash tag if needed (exported version) The hashTag parameter allows specifying a custom hash tag to use

func ExecuteReadPipeline added in v1.7.1

func ExecuteReadPipeline(ctx context.Context, redisClient Client, fn PipelineFunc) ([]redis.Cmder, error)

ExecuteReadPipeline executes multiple Redis read commands in a pipeline to reduce network round trips. It takes a context and a function that defines the commands to execute. The function should add commands to the pipeline but not execute them. Returns the command results and any error that occurred.

func ExecuteScript added in v1.7.7

func ExecuteScript(ctx context.Context, client Client, scriptName, scriptContent string, keys []string, args ...interface{}) (interface{}, error)

ExecuteScript executes a Lua script on Redis using its SHA1 hash. If the script is not found or Redis returns NOSCRIPT, it attempts to re-upload the script. This function is thread-safe and can be called concurrently.

If scriptContent is empty and the script is not found in the local cache, ErrScriptNotFound is returned. This allows callers to handle the case where a script needs to be uploaded first.

In Redis Cluster mode, this function ensures that all keys hash to the same slot by adding a common hash tag if needed.

func ExecuteWritePipeline added in v1.7.1

func ExecuteWritePipeline(ctx context.Context, redisClient Client, fn PipelineFunc) ([]redis.Cmder, error)

ExecuteWritePipeline executes multiple Redis write commands in a pipeline to reduce network round trips. It takes a context and a function that defines the commands to execute. The function should add commands to the pipeline but not execute them. Returns the command results and any error that occurred.

func GetBruteForceHashKey added in v1.12.0

func GetBruteForceHashKey(prefix, network string) string

GetBruteForceHashKey returns the sharded Redis key for brute-force tracking.

func GetShardID added in v1.12.0

func GetShardID(input string) string

GetShardID calculates a 2-digit hex shard ID (00-FF) for a given input string.

func GetUserHashKey added in v1.12.0

func GetUserHashKey(prefix, username string) string

GetUserHashKey returns the sharded Redis key for user account mapping.

func IsClusterClient added in v1.7.7

func IsClusterClient(client redis.UniversalClient) bool

IsClusterClient checks if the Redis client is a cluster client (exported version)

func RebuildClient added in v1.12.0

func RebuildClient()

RebuildClient closes the currently configured global client (if any) and replaces it with a freshly constructed client.

This is intended for in-process restart/reload operations where Redis configuration may have changed. Callers should treat this as best-effort and handle downstream readiness checks separately.

func RedisTLSOptions added in v1.3.2

func RedisTLSOptions(tlsCfg *config.TLS) *tls.Config

RedisTLSOptions checks if Redis TLS is enabled in the configuration. If TLS is enabled, it loads the X509 key pair and creates a tls.Config object. The loaded certificate is added to the tls.Config object. If an error occurs while loading the key pair, it logs the error and returns nil. If Redis TLS is disabled, it returns nil.

func UpdateRedisServerMetrics added in v1.7.3

func UpdateRedisServerMetrics(ctx context.Context, cfg config.File, logger *slog.Logger)

UpdateRedisServerMetrics periodically collects and updates Redis server metrics

func UploadAllScripts added in v1.7.7

func UploadAllScripts(ctx context.Context, logger *slog.Logger, client Client) error

UploadAllScripts uploads all Lua scripts defined in lua_scripts.go to Redis. This function should be called at program startup to ensure all scripts are available.

func UploadScript added in v1.7.7

func UploadScript(ctx context.Context, client Client, scriptName, scriptContent string) (string, error)

UploadScript uploads a Lua script to Redis and stores its SHA1 hash. If the script is already uploaded, it returns the existing SHA1 hash. This function is thread-safe and can be called concurrently.

Types

type BatchingHook added in v1.11.3

type BatchingHook struct {
	// contains filtered or unexported fields
}

BatchingHook implements redis.Hook and batches individual Process calls into short-lived pipelines to reduce network round-trips.

Design goals: - Preserve command ordering within a batch. - Respect context cancellations by unblocking waiters; actual execution may still occur. - Bypass batching when queue is saturated or for explicitly skipped commands. - Keep the public client API intact by operating at Hook level.

func NewBatchingHook added in v1.11.3

func NewBatchingHook(logger *slog.Logger, client redis.UniversalClient, cfg *config.RedisBatching) *BatchingHook

func (*BatchingHook) DialHook added in v1.11.3

func (h *BatchingHook) DialHook(next redis.DialHook) redis.DialHook

DialHook pass-through

func (*BatchingHook) ProcessHook added in v1.11.3

func (h *BatchingHook) ProcessHook(next redis.ProcessHook) redis.ProcessHook

ProcessHook implements single-command interception.

func (*BatchingHook) ProcessPipelineHook added in v1.11.3

func (h *BatchingHook) ProcessPipelineHook(next redis.ProcessPipelineHook) redis.ProcessPipelineHook

ProcessPipelineHook do not alter explicit caller pipelines; just pass through.

type Client added in v1.4.10

type Client interface {
	// GetWriteHandle retrieves the Redis client's write handle for operations requiring write access.
	GetWriteHandle() redis.UniversalClient

	// GetReadHandle retrieves a Redis client's read handle, supporting multiple read handles for load balancing.
	GetReadHandle() redis.UniversalClient

	// GetWritePipeline returns a Redis pipeline for batching write operations.
	GetWritePipeline() redis.Pipeliner

	// GetReadPipeline returns a Redis pipeline for batching read operations.
	GetReadPipeline() redis.Pipeliner

	// Close releases all resources associated with the client, including write and read handles, and closes any open connections.
	Close()
}

Client defines an interface for interacting with a Redis client with methods for initialization and handle retrieval.

func GetClient added in v1.4.10

func GetClient() Client

func NewClient added in v1.4.10

func NewClient() Client

NewClient creates and returns a new instance of a Redis client that implements the Client interface.

func NewClientWithDeps added in v1.12.0

func NewClientWithDeps(cfg config.File, logger *slog.Logger) Client

NewClientWithDeps creates and returns a new instance of a Redis client that implements the Client interface using injected dependencies.

This is the DI-owned construction path. It must not call `config.GetFile()` or use `log.Logger` internally.

func NewTestClient added in v1.4.10

func NewTestClient(db *redis.Client) Client

NewTestClient initializes and returns a new testClient instance, implementing the Client interface using the provided Redis client.

type PipelineFunc added in v1.7.1

type PipelineFunc func(pipe redis.Pipeliner) error

PipelineFunc is a function that executes Redis commands on a pipeline.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL