Some checks failed
CI / test (push) Failing after 6s
CI / frontend (push) Failing after 4s
CI / golangci-lint (push) Failing after 4s
CI / windsurf-platform (macos-latest) (push) Has been cancelled
CI / windsurf-platform (windows-latest) (push) Has been cancelled
Security Scan / backend-security (push) Failing after 1m31s
Security Scan / frontend-security (push) Failing after 7s
83 lines
2.9 KiB
Go
83 lines
2.9 KiB
Go
package repository
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||
"github.com/redis/go-redis/v9"
|
||
)
|
||
|
||
const (
|
||
stickySessionPrefix = "sticky_session:"
|
||
cascadeIDPrefix = "windsurf_cascade:"
|
||
)
|
||
|
||
type gatewayCache struct {
|
||
rdb *redis.Client
|
||
}
|
||
|
||
func NewGatewayCache(rdb *redis.Client) service.GatewayCache {
|
||
return &gatewayCache{rdb: rdb}
|
||
}
|
||
|
||
// buildSessionKey 构建 session key,包含 groupID 实现分组隔离
|
||
// 格式: sticky_session:{groupID}:{sessionHash}
|
||
func buildSessionKey(groupID int64, sessionHash string) string {
|
||
return fmt.Sprintf("%s%d:%s", stickySessionPrefix, groupID, sessionHash)
|
||
}
|
||
|
||
func (c *gatewayCache) GetSessionAccountID(ctx context.Context, groupID int64, sessionHash string) (int64, error) {
|
||
key := buildSessionKey(groupID, sessionHash)
|
||
return c.rdb.Get(ctx, key).Int64()
|
||
}
|
||
|
||
func (c *gatewayCache) SetSessionAccountID(ctx context.Context, groupID int64, sessionHash string, accountID int64, ttl time.Duration) error {
|
||
key := buildSessionKey(groupID, sessionHash)
|
||
return c.rdb.Set(ctx, key, accountID, ttl).Err()
|
||
}
|
||
|
||
func (c *gatewayCache) RefreshSessionTTL(ctx context.Context, groupID int64, sessionHash string, ttl time.Duration) error {
|
||
key := buildSessionKey(groupID, sessionHash)
|
||
return c.rdb.Expire(ctx, key, ttl).Err()
|
||
}
|
||
|
||
// DeleteSessionAccountID 删除粘性会话与账号的绑定关系。
|
||
// 当检测到绑定的账号不可用(如状态错误、禁用、不可调度等)时调用,
|
||
// 以便下次请求能够重新选择可用账号。
|
||
//
|
||
// DeleteSessionAccountID removes the sticky session binding for the given session.
|
||
// Called when the bound account becomes unavailable (e.g., error status, disabled,
|
||
// or unschedulable), allowing subsequent requests to select a new available account.
|
||
func (c *gatewayCache) DeleteSessionAccountID(ctx context.Context, groupID int64, sessionHash string) error {
|
||
key := buildSessionKey(groupID, sessionHash)
|
||
return c.rdb.Del(ctx, key).Err()
|
||
}
|
||
|
||
// buildCascadeKey 构造 Windsurf Cascade ID 缓存 key。
|
||
// 上层已将 (groupID, accountID, modelUID, lsEndpoint, sessionHash, sysPromptHash) 哈希为单一字符串。
|
||
func buildCascadeKey(key string) string {
|
||
return cascadeIDPrefix + key
|
||
}
|
||
|
||
// GetCascadeID 读取 Cascade 会话 ID。redis.Nil 视为未命中(返回空串与 nil)。
|
||
func (c *gatewayCache) GetCascadeID(ctx context.Context, key string) (string, error) {
|
||
v, err := c.rdb.Get(ctx, buildCascadeKey(key)).Result()
|
||
if err == redis.Nil {
|
||
return "", nil
|
||
}
|
||
return v, err
|
||
}
|
||
|
||
// SetCascadeID 写入 Cascade 会话 ID。
|
||
func (c *gatewayCache) SetCascadeID(ctx context.Context, key string, cascadeID string, ttl time.Duration) error {
|
||
return c.rdb.Set(ctx, buildCascadeKey(key), cascadeID, ttl).Err()
|
||
}
|
||
|
||
// DeleteCascadeID 失效 Cascade 会话 ID。
|
||
func (c *gatewayCache) DeleteCascadeID(ctx context.Context, key string) error {
|
||
return c.rdb.Del(ctx, buildCascadeKey(key)).Err()
|
||
}
|
||
|