Incident: W4 API Pending Forever (Redis auth hang)
| Pole | Wartość |
|---|---|
| ID | INCIDENT-2026-07-01-001 |
| Platforma | W4 (w4.pinbox24.com, bms-1) |
| Severność | P0 (produkcja down — wszystkie authenticated API calls wisely) |
| Status | RESOLVED (hotfix in-container) |
| Wykryto | 2026-07-01T~08:00Z |
| Rozwiązano | 2026-07-01T~11:00Z |
| GH Issue | radieu/p24-infra#2415 |
Symptom
Wszystkie requesty w dashboardzie W4 wisiały w stanie “Pending” bez odpowiedzi. Dotyczyło:
GET /api/offices/— spinner bez końcaGET /api/profile/— spinner bez końcaPOST /api/i18n/langs— 500 z “HMGET can’t be processed”GET /api/offices/work-space/:id— 500
Użytkownicy nie mogli zalogować się ani załadować żadnych danych.
Przyczyna
Incydent #2350 (rotacja credentiali) spowodował zmianę REDIS_PASSWORD w backend-environment.env na bms-1. Jednak OVH Redis DBaaS (kr40258-001.dbaas.ovh.net:35689) ma trzecie, nieznane hasło — ani stare (10-char), ani nowe (32-char) z rotacji nie działają → WRONGPASS.
Redis v2.x ma domyślnie enable_offline_queue: true. Gdy klient nie jest w stanie ready (WRONGPASS → brak auth), wszystkie komendy kolejkują się w nieskończoność. Żadna Promise nigdy się nie resolve/reject.
authGuardMiddleWare wywołuje redisGetData(token._id) dla każdego authenticated request:
// authGuard.middleware.js
const redisUser = yield redis_helper_1.redisGetData(token._id);
// ↑ ta Promise nigdy nie resolveuje gdy Redis offline queue = true
next(); // nigdy nie osiąganeOś czasu
| Czas (UTC) | Zdarzenie |
|---|---|
| ~08:00 | Wykryto: dashboard W4 — wszystkie requesty Pending |
| ~09:00 | Analiza: redis.config.js, authGuard.middleware.js |
| ~09:30 | Root cause: enable_offline_queue: true + WRONGPASS |
| ~10:00 | Patch 1: redis.config.js — enable_offline_queue: false |
| ~10:30 | Patch 2: redis.helper.js — safeRedis() na redisGetData |
| ~11:00 | Patch 3: redis.helper.js — safeRedis() na WSZYSTKICH funkcjach |
| ~11:15 | Weryfikacja OK — dashboard ładuje się, offices/profile 200 |
Zmienione pliki / kontenery
| Kontener/Plik | Zmiana | Rollback |
|---|---|---|
v42-prod:/app/dist/config/redis.config.js | Dodano { enable_offline_queue: false, retry_unfulfilled_commands: false } do createClient | docker exec v42-prod cp /tmp/redis.config.js.bak /app/dist/config/redis.config.js |
v42-prod:/app/dist/globalHelpers/redisHelpers/redis.helper.js | Kompletny rewrite — wszystkie funkcje używają safeRedis() z fallback null/[] i timeoutem 1500ms | Backup w /tmp/redis.helper.js.bak wewnątrz kontenera |
Patch marker
Plik redis.helper.js zawiera marker REDIS_HOTFIX_ALL_SAFE na początku i REDIS_HOTFIX_TIMEOUT przy funkcji safeRedis.
Script re-apply
Skrypt do ponownego nałożenia patcha po docker recreate:
docs/pinbox24/patch_redis_config.py— redis.config.jsdocs/pinbox24/patch_redis_helper_full.py— redis.helper.js (full rewrite)
Root cause (szczegółowo)
// Przed patchem redis.config.js:
exports.redisClient = redis_1.default.createClient(
process.env.REDIS_PORT, process.env.REDIS_HOST
);
// enable_offline_queue: true (domyślnie) — komendy kolejkują się gdy Redis nie ready
// Po patchem:
exports.redisClient = redis_1.default.createClient(
process.env.REDIS_PORT, process.env.REDIS_HOST,
{ enable_offline_queue: false, retry_unfulfilled_commands: false }
);
// Komendy failują natychmiastowo z AbortError: NR_CLOSED// safeRedis() — klucz do naprawy:
function safeRedis(fn, fallback) {
return new Promise((resolve) => {
const t = setTimeout(() => resolve(fallback), 1500); // fallback po 1.5s
try {
fn((error, data) => {
clearTimeout(t);
if (error) { resolve(fallback); } // błąd → fallback, nie reject
else { resolve(data); }
});
} catch(e) {
clearTimeout(t);
resolve(fallback); // wyjątek → fallback
}
});
}Weryfikacja fix
# Na bms-1:
curl -s -o /dev/null -w "%{http_code}" "https://api.w4.pinbox24.com/api/profile/" \
-H "Authorization: Bearer <token>"
# → 200 (wcześniej timeout)
curl -s -o /dev/null -w "%{http_code}" "https://api.w4.pinbox24.com/api/offices/" \
-H "Authorization: Bearer <token>"
# → 200Dashboard załadował się, workspace/i18n 200, uploads działają.
Długoterminowy fix
- OVH Manager: Zresetować hasło Redis
kr40258-001.dbaas.ovh.net:35689i zaktualizować:secrets/n8n-bms4.env.sops→ kluczREDIS_PASSWORDbackend-environment.envna bms-1- Recreate v42-prod z nowym hasłem
- Monitoring: Alert gdy Redis
kr40258-001nie jest osiągalne z bms-1 przez >30s - Przed każdym
docker recreate v42-prod: Re-apply oba patche (redis.config.js + redis.helper.js) - Issue: radieu/p24-infra#2415
Lekcje
- Redis v2.x
enable_offline_queue: trueto pułapka — nigdy nie używać bezsafeRedis()wrappera - Rotacja credentiali powinna obejmować weryfikację połączenia przed deployem
- OVH Redis DBaaS ma własne hasło niezależne od rotacji — należy je przechowywać w
secrets/n8n-bms4.env.sopsosobno od n8n Redis authGuardMiddleWare→ Redis na każdym requeście = single point of failure dla całego API