diff --git a/backend/api/redis_log_handler.py b/backend/api/redis_log_handler.py index e1c3c8a..0d0acc1 100644 --- a/backend/api/redis_log_handler.py +++ b/backend/api/redis_log_handler.py @@ -293,7 +293,8 @@ class RedisErrorLogHandler(logging.Handler): head["exc_text"] = entry.get("exc_text") head["exc_type"] = entry.get("exc_type") try: - pipe = client.pipeline() + # 集群模式下禁用 transaction,避免 CROSSSLOT(list_key 与 stats_key 不同 slot) + pipe = client.pipeline(transaction=False) pipe.lset(list_key, 0, json.dumps(head, ensure_ascii=False)) pipe.ltrim(list_key, 0, max_len - 1) pipe.incr(stats_key, 1) @@ -304,7 +305,8 @@ class RedisErrorLogHandler(logging.Handler): pass try: - pipe = client.pipeline() + # 集群模式下禁用 transaction,避免 CROSSSLOT(list_key 与 stats_key 不同 slot) + pipe = client.pipeline(transaction=False) pipe.lpush(list_key, json.dumps(entry, ensure_ascii=False)) pipe.ltrim(list_key, 0, max_len - 1) pipe.incr(stats_key, 1) diff --git a/backend/api/routes/system.py b/backend/api/routes/system.py index e277903..3660123 100644 --- a/backend/api/routes/system.py +++ b/backend/api/routes/system.py @@ -110,7 +110,9 @@ def _write_logs_config_and_trim(client, cfg: Dict[str, Any]) -> Dict[str, Any]: mapping["dedupe_consecutive"] = "1" if cfg.get("dedupe_consecutive") else "0" mapping["include_debug_in_info"] = "1" if cfg.get("include_debug_in_info") else "0" - pipe = client.pipeline() + # 注意:AWS Valkey/Redis 集群模式下,MULTI/EXEC 不能跨 slot + # 这里会同时操作多个 key(config hash + 3 个 list),所以必须禁用 transaction + pipe = client.pipeline(transaction=False) pipe.hset(_logs_config_key(), mapping=mapping) for g in LOG_GROUPS: key = _logs_key_for_group(g) @@ -161,7 +163,8 @@ async def logs_test_write( day = _beijing_yyyymmdd() stats_prefix = _logs_stats_prefix() - pipe = client.pipeline() + # 集群模式下禁用 transaction,避免 CROSSSLOT + pipe = client.pipeline(transaction=False) for g in LOG_GROUPS: key = _logs_key_for_group(g) max_len = int(cfg["max_len"][g]) @@ -185,7 +188,8 @@ async def logs_test_write( pipe.execute() # 返回写入后的 LLEN,便于你确认 - pipe2 = client.pipeline() + # 单 key LLEN 查询也不需要 transaction + pipe2 = client.pipeline(transaction=False) for g in LOG_GROUPS: pipe2.llen(_logs_key_for_group(g)) llens = pipe2.execute() @@ -387,7 +391,8 @@ async def logs_overview(x_admin_token: Optional[str] = Header(default=None, alia day = _beijing_yyyymmdd() stats_prefix = _logs_stats_prefix() - pipe = client.pipeline() + # 集群模式下禁用 transaction,避免 CROSSSLOT + pipe = client.pipeline(transaction=False) for g in LOG_GROUPS: pipe.llen(_logs_key_for_group(g)) for g in LOG_GROUPS: diff --git a/trading_system/redis_log_handler.py b/trading_system/redis_log_handler.py index bfd59ee..a65eb7f 100644 --- a/trading_system/redis_log_handler.py +++ b/trading_system/redis_log_handler.py @@ -324,7 +324,8 @@ class RedisErrorLogHandler(logging.Handler): head["exc_text"] = entry.get("exc_text") head["exc_type"] = entry.get("exc_type") try: - pipe = client.pipeline() + # 集群模式下禁用 transaction,避免 CROSSSLOT(list_key 与 stats_key 不同 slot) + pipe = client.pipeline(transaction=False) pipe.lset(list_key, 0, json.dumps(head, ensure_ascii=False)) pipe.ltrim(list_key, 0, max_len - 1) pipe.incr(stats_key, 1) @@ -336,7 +337,8 @@ class RedisErrorLogHandler(logging.Handler): pass try: - pipe = client.pipeline() + # 集群模式下禁用 transaction,避免 CROSSSLOT(list_key 与 stats_key 不同 slot) + pipe = client.pipeline(transaction=False) pipe.lpush(list_key, json.dumps(entry, ensure_ascii=False)) pipe.ltrim(list_key, 0, max_len - 1) pipe.incr(stats_key, 1)