修复(#1): 服务器用户列表缓存按 protocol 隔离 + 兜底不写缓存

服务器用户列表缓存跨协议污染修复(HIF-1 / 详见 PR #5 四件套):

1. 缓存 key 加 protocol 维度(`server:user:{server_id}:{protocol}`),对齐 ServerConfig 已有约定
2. 显式枚举协议清除用户列表缓存(AllProtocols + ServerUserListCacheKeysForServer),不用 SCAN
3. 三个兜底分支不写缓存 + Errorw 日志(带 server_id + protocol 字段)
4. hysteria2 → hysteria 兼容归一化 + 6 个新单测

Closes HIF-1
This commit is contained in:
2026-06-03 05:37:03 -07:00
committed by GitHub
parent 8ff992e74c
commit 19d28a8f89
6 changed files with 220 additions and 14 deletions
+27 -3
View File
@@ -25,6 +25,31 @@ const (
ServerConfigCacheKey = "server:config:"
)
// AllProtocols 枚举所有客户端可能携带的 protocol。
// 用户列表缓存 key 形态为 `server:user:{server_id}:{protocol}`,按 server_id
// 失效时需要按协议精确删除。SCAN 在增量 rehash 期间可能漏 key,因此采用显式枚举。
// 包含 hysteria2(兼容字段,与 hysteria 同语义),多余的 Del 是 no-opover-deletion 安全。
var AllProtocols = []string{
"shadowsocks",
"vmess",
"vless",
"trojan",
"anytls",
"tuic",
"hysteria",
"hysteria2",
}
// ServerUserListCacheKeysForServer 返回给定 server 的所有 protocol 维度缓存 key。
// 用于 Del 路径——节点 / 订阅 / 流量统计触发缓存失效时一次性清掉该 server 下所有协议条目。
func ServerUserListCacheKeysForServer(serverId int64) []string {
keys := make([]string, 0, len(AllProtocols))
for _, protocol := range AllProtocols {
keys = append(keys, fmt.Sprintf("%s%d:%s", ServerUserListCacheKey, serverId, protocol))
}
return keys
}
// FilterParams Filter Server Params
type FilterParams struct {
Page int
@@ -134,7 +159,7 @@ func (m *customServerModel) ClearNodeCache(ctx context.Context, params *FilterNo
}
var cacheKeys []string
for _, node := range nodes {
cacheKeys = append(cacheKeys, fmt.Sprintf("%s%d", ServerUserListCacheKey, node.ServerId))
cacheKeys = append(cacheKeys, ServerUserListCacheKeysForServer(node.ServerId)...)
if node.Protocol != "" {
var cursor uint64
for {
@@ -162,8 +187,7 @@ func (m *customServerModel) ClearNodeCache(ctx context.Context, params *FilterNo
// ClearServerCache Clear Server Cache
func (m *customServerModel) ClearServerCache(ctx context.Context, serverId int64) error {
var cacheKeys []string
cacheKeys = append(cacheKeys, fmt.Sprintf("%s%d", ServerUserListCacheKey, serverId))
cacheKeys := ServerUserListCacheKeysForServer(serverId)
var cursor uint64
for {
keys, newCursor, err := m.Cache.Scan(ctx, cursor, fmt.Sprintf("%s%d*", ServerConfigCacheKey, serverId), 100).Result()