mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
fix: ensure Lua scripts are loaded before pipeline EVALSHA calls
Pipeline mode only sends EVALSHA (no automatic fallback to EVAL). If Redis restarts and script cache is lost, all pipeline Lua calls silently fail with NOSCRIPT. Add ensureScriptLoaded() to pre-load scripts before pipeline execution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -370,19 +369,7 @@ func (h *AccountHandler) List(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
if len(accountGroups) > 0 {
|
||||
var affinityErr error
|
||||
affinityClients, affinityErr = h.gatewayCache.GetAccountAffinityClientsBatch(c.Request.Context(), accountGroups, service.ClientAffinityTTL)
|
||||
if affinityErr != nil {
|
||||
slog.Error("GetAccountAffinityClientsBatch failed",
|
||||
"error", affinityErr,
|
||||
"account_count", len(accountGroups),
|
||||
)
|
||||
} else {
|
||||
slog.Info("GetAccountAffinityClientsBatch success",
|
||||
"account_count", len(accountGroups),
|
||||
"result_count", len(affinityClients),
|
||||
)
|
||||
}
|
||||
affinityClients, _ = h.gatewayCache.GetAccountAffinityClientsBatch(c.Request.Context(), accountGroups, service.ClientAffinityTTL)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,16 @@ func NewGatewayCache(rdb *redis.Client) service.GatewayCache {
|
||||
return &gatewayCache{rdb: rdb}
|
||||
}
|
||||
|
||||
// ensureScriptLoaded 确保 Lua 脚本已加载到 Redis 服务器的脚本缓存中。
|
||||
// Pipeline 中的 Script.Run 只发送 EVALSHA,如果 Redis 重启过导致脚本缓存丢失,
|
||||
// EVALSHA 会返回 NOSCRIPT 错误。此方法提前加载脚本以避免该问题。
|
||||
func ensureScriptLoaded(ctx context.Context, rdb *redis.Client, script *redis.Script) {
|
||||
exists, err := script.Exists(ctx, rdb).Result()
|
||||
if err != nil || len(exists) == 0 || !exists[0] {
|
||||
_ = script.Load(ctx, rdb).Err()
|
||||
}
|
||||
}
|
||||
|
||||
// buildSessionKey 构建 session key,包含 groupID 实现分组隔离
|
||||
// 格式: sticky_session:{groupID}:{sessionHash}
|
||||
func buildSessionKey(groupID int64, sessionHash string) string {
|
||||
@@ -125,6 +135,8 @@ func (c *gatewayCache) GetAccountAffinityCountBatch(ctx context.Context, groupID
|
||||
now := time.Now().Unix()
|
||||
expireThreshold := now - int64(ttl.Seconds())
|
||||
|
||||
ensureScriptLoaded(ctx, c.rdb, getAffinityCountScript)
|
||||
|
||||
pipe := c.rdb.Pipeline()
|
||||
cmds := make([]*redis.Cmd, len(accountIDs))
|
||||
for i, accID := range accountIDs {
|
||||
@@ -166,6 +178,8 @@ func (c *gatewayCache) GetAccountAffinityClientsBatch(ctx context.Context, accou
|
||||
}
|
||||
}
|
||||
|
||||
ensureScriptLoaded(ctx, c.rdb, getAffinityClientsScript)
|
||||
|
||||
pipe := c.rdb.Pipeline()
|
||||
cmds := make([]*redis.Cmd, len(queries))
|
||||
for i, q := range queries {
|
||||
|
||||
Reference in New Issue
Block a user