Files
server-manager/REDIS_AUDIT.md
2026-02-28 07:15:46 -05:00

230 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Аудит Redis — полный отчёт
**Дата:** 2026-02-28
**Версия:** v1.8.65
**Тестовый сервер:** Reddis main ovh (Redis 8.2.1, 27M+ ключей, 2.82GB RAM)
---
## 1. Живое тестирование через CLI
### Работает корректно
| Команда | Результат |
|---------|-----------|
| `--redis-info ALIAS` | Версия, память, клиенты, keyspace |
| `--redis-keys ALIAS "*"` | SCAN до 1000 ключей, сортировка |
| `--redis ALIAS "PING"` | `True` |
| `--redis ALIAS "DBSIZE"` | `27199166` |
| `--redis ALIAS "GET key"` | Возвращает значение |
| `--redis ALIAS "SET key value"` | `True` |
| `--redis ALIAS "DEL key1 key2"` | Число удалённых |
| `--redis ALIAS "KEYS pattern"` | Список с нумерацией |
| `--redis ALIAS "INFO memory"` | Полный вывод секции |
| `--redis ALIAS "TYPE key"` | Тип данных |
| `--redis ALIAS "TTL key"` | Секунды / -1 |
| `--redis ALIAS "SELECT 1"` | `True` |
| `--redis ALIAS "MSET k1 v1 k2 v2"` | `True` |
| `--status` (redis) | `ONLINE` |
### Сломано
| Команда | Ошибка | Баг |
|---------|--------|-----|
| `SET key "hello world"` | `syntax error` | #1 |
| `SET key '{"a":1, "b":2}'` | `syntax error` | #1 |
| Любое значение с пробелами | `syntax error` | #1 |
---
## 2. Найденные баги
### БАГ #1 — КРИТИЧНЫЙ: Парсер команд ломает значения с пробелами
**Файлы:** `tools/ssh.py:958`, `core/redis_client.py:78`
**Проблема:** Используется `command.split()` вместо `shlex.split()`.
```python
# Текущий код:
parts = command.split()
# "SET key 'hello world'" → ['SET', 'key', "'hello", "world'"]
# Redis получает 4 аргумента вместо 3 → syntax error
```
**Воздействие:**
- Невозможно SET/GET значения с пробелами
- Невозможно работать с JSON-данными содержащими пробелы
- Невозможно выполнить EVAL с Lua-скриптами
- Сломаны команды с multi-word аргументами (SORT, CONFIG SET и др.)
**Затронуты:** CLI (`tools/ssh.py`) И GUI (`core/redis_client.py``redis_tab.py`)
---
### БАГ #2 — СРЕДНИЙ: GUI stats показывает отформатированную строку
**Файл:** `gui/tabs/redis_tab.py:192-208`
**Проблема:** `_refresh_stats()` вызывает `client.execute("DBSIZE")`, который возвращает строку `"(integer) 27199166"` (уже отформатированную через `_format()`). Эта строка отображается как есть.
```python
# redis_tab.py:198
keys_count = client.execute("DBSIZE") # Returns "(integer) 27199166"
keys_text = str(keys_count) # → "(integer) 27199166" in label
```
**Для INFO memory проблема другая:**
```python
# redis_tab.py:201
info = client.execute("INFO memory")
# _format() превращает dict в строку, но без \r\n
# Поиск по \r\n ничего не найдёт → memory = "—" всегда
for line in info.split("\r\n"): # \r\n не найдётся!
if line.startswith("used_memory_human:"):
memory = line.split(":")[1].strip()
```
**Воздействие:** Статистика в GUI:
- Keys показывает `(integer) 27199166` вместо `27199166`
- Memory всегда показывает `—` (парсинг не находит `\r\n`)
---
### БАГ #3 — СРЕДНИЙ: GUI RedisClient не поддерживает SSL
**Файл:** `core/redis_client.py:35-43`
**Проблема:** В конструкторе `redis.Redis()` отсутствует параметр `ssl`.
```python
# redis_client.py:35-43
self._conn = r.Redis(
host=self._host,
port=self._port,
password=self._password,
db=self._db,
decode_responses=True,
socket_timeout=5,
socket_connect_timeout=5,
# ❌ ОТСУТСТВУЕТ: ssl=...
)
```
При этом CLI (`tools/ssh.py:955-956`) SSL поддерживает:
```python
ssl_enabled = server.get("ssl", False)
r = redis_lib.Redis(..., ssl=ssl_enabled)
```
**Воздействие:** GUI не может подключиться к Redis с TLS/SSL.
**Связанная проблема:** В `server_dialog.py` поле `use_ssl`/`ssl` НЕ включено в `FIELD_MAP["redis"]`:
```python
"redis": ["password", "db_index"] # нет ssl!
```
Даже если добавить SSL в RedisClient, пользователь не сможет включить его в UI.
---
### БАГ #4 — НИЗКИЙ: CLI Redis ошибки выдают сырой traceback
**Файл:** `tools/ssh.py:957-962`
**Проблема:** `r.execute_command()` не обёрнут в try-except.
```python
# Текущий код:
result = r.execute_command(*parts) # При ошибке → необработанное исключение
```
**Пример:** `HGETALL` на string-ключе:
```
ERROR: ResponseError: WRONGTYPE Operation against a key holding the wrong kind of value
```
Выводится как traceback с двойным повтором вместо читаемого сообщения.
---
### БАГ #5 — НИЗКИЙ: Двойной PING при status check
**Файл:** `core/status_checker.py:98-110`
```python
def _check_redis(self, server: dict) -> bool:
client = RedisClient(server)
result = client.connect() # ← PING #1 внутри connect()
if result:
ok = client.check_connection() # ← PING #2
client.disconnect()
return ok
return False
```
`connect()` уже делает `self._conn.ping()``True`. Потом `check_connection()` вызывает `self._conn.ping()` ещё раз.
---
### БАГ #6 — НИЗКИЙ: CLI status для Redis без try-except
**Файл:** `tools/ssh.py:698-703`
```python
if stype == "redis":
r = redis_lib.Redis(...)
r.ping() # ← Нет try-except! Если offline → traceback
r.close()
return "ONLINE"
```
Сравни с SQL, где `conn.close()` в finally. Здесь если `ping()` упадёт, `r.close()` не вызовется.
---
### ПРОБЛЕМА #7 — Дублирование кода подключения
**Файлы:** `tools/ssh.py:946-956`, `tools/ssh.py:978-986`, `tools/ssh.py:1009-1017`
Три функции (`run_redis_cmd`, `redis_info`, `redis_keys`) создают одинаковое подключение к Redis копипастом:
```python
import redis as redis_lib
host = server["ip"]
port = server.get("port", 6379)
password = server.get("password", "") or None
db_index = server.get("db_index", 0)
ssl_enabled = server.get("ssl", False)
r = redis_lib.Redis(host=host, port=port, ...)
```
Этот блок повторяется 3 раза. Вынести в хелпер.
---
## 3. Карта проблем по файлам
| Файл | Строки | Баги |
|------|--------|------|
| `tools/ssh.py` | 958 | #1 (split), #4 (error handling), #6 (status), #7 (copypaste) |
| `core/redis_client.py` | 78, 35-43 | #1 (split), #3 (no SSL) |
| `gui/tabs/redis_tab.py` | 192-208 | #2 (stats parsing) |
| `core/status_checker.py` | 98-110 | #5 (double ping) |
| `gui/server_dialog.py` | 22 | #3 (no ssl field for redis) |
---
## 4. Влияние на пользователей
| Сценарий | Статус | Причина |
|----------|--------|---------|
| Простые команды (GET/SET/DEL) | ✅ Работает | Нет пробелов |
| Команды со значениями с пробелами | ❌ Сломано | Баг #1 |
| JSON-данные с пробелами | ❌ Сломано | Баг #1 |
| GUI статистика Keys | ⚠️ Некорректно | Баг #2 |
| GUI статистика Memory | ❌ Всегда "—" | Баг #2 |
| SSL-подключение через GUI | ❌ Невозможно | Баг #3 |
| SSL-подключение через CLI | ✅ Работает | — |
| Ошибки в CLI | ⚠️ Нечитаемые | Баг #4 |
| Status check | ✅ Работает (с оверхедом) | Баг #5 |