400 MB na minutę: jak Mistral znalazł wyciek pamięci poza stertą vLLM
Mistral opisał debugowanie wycieku RSS w disaggregated serving vLLM, NIXL i UCX. Heaptrack nie widział problemu; pomogły pmap, eBPF i GDB.
- AUTOR
- Karol Rapacz / CEO Breachroad · OSCP · PNPT
- PUBLIKACJA
- 21 stycznia 2026
- CZAS CZYTANIA
- 14 min czytania
- TEMAT
- Bezpieczeństwo AI
21 stycznia 2026 roku Mistral opublikował analizę wycieku pamięci w przedprodukcyjnym środowisku vLLM. Przy Mistral Medium 3.1, kompilacji grafu i rozdzieleniu faz prefill/decode pamięć systemowa procesu decode rosła o około 400 MB na minutę pod ruchem zbliżonym do produkcyjnego. Po kilku godzinach kończyło się to OOM.
To nie był uniwersalny błąd każdej instalacji vLLM. Pojawiał się w konkretnym disaggregated serving z NIXL, który korzystał z UCX do transferu KV cache przez szybkie połączenia. Właśnie precyzyjne zawężenie konfiguracji jest największą wartością tej historii.
Dlaczego profiler „nie widział” wycieku
Memray i Guppy nie pokazały problemu. Heaptrack widział prawidłową równowagę malloc/free, mimo że RSS stale rósł. Powód: pamięć nie ginęła na klasycznej stercie. pmap pokazał rosnące anonimowe mapowania, a BPFtrace ujawnił bezpośrednie wywołania mmap przez surowy syscall, omijające hooki glibc.
Zautomatyzowany GDB zatrzymywał tylko konkretne wywołanie mmap i zebrał pełny stos. Ślad prowadził przez UCX. Biblioteka przechwytywała mapowania pamięci na potrzeby RDMA i po munmap umieszczała regiony w kolejce unieważnienia, zamiast od razu je zwolnić. Pula tej kolejki rosła, a domyślna wartość UCX_RCACHE_MAX_UNRELEASED=inf nie wymuszała porządkowania w obserwowanym edge case.
Naprawa i obejście
W konfiguracji vLLM transfer dotyczył jednego dużego, ciągłego obszaru KV cache, więc wyłączenie hooków mmap rozwiązało wyciek bez mierzalnej straty wydajności według Mistrala. Alternatywnie ustawienie UCX_RCACHE_MAX_UNRELEASED=1024 ograniczało kolejkę i wymuszało cleanup. Zespół vLLM połączył poprawkę, a zespoły UCX/NIXL planowały zmianę zachowania w przyszłym wydaniu.
Nie należy kopiować zmiennej środowiskowej bez testu. Limit wpływa na zachowanie cache rejestracji pamięci i w innym wzorcu RDMA może zmienić wydajność. Poprawna kolejność to reprodukcja na canary, obserwacja RSS i przepustowości, aktualizacja zależności, test soak oraz dopiero rollout.
Runbook obserwowalności inference
Monitoruj osobno VRAM, RSS, anonymous mappings, open file descriptors i pamięć cgroup. Alarm oparty tylko na stercie Pythona pominie biblioteki C/C++, sterowniki i RDMA. Dla soak testu użyj realnego rozkładu długości promptu, równoległości i transferów KV cache.
Zapisuj dokładne wersje vLLM, NIXL, UCX, PyTorch, sterownika i firmware. Bez tego incydent staje się „czasem rośnie pamięć” i jest niemożliwy do odtworzenia. Szerszy model obserwowalności opisujemy w monitoringu agentów AI z OpenTelemetry.
Ta historia pokazuje, że „wyciek pamięci” może być w kolejce invalidation biblioteki RDMA, a nie w Pythonie. Przegląd produkcyjnego AI powinien obejmować testy długotrwałe i pełny stos natywnych zależności.
Jak rozróżnić leak, cache i fragmentację
Rosnący RSS nie zawsze oznacza, że pamięć jest nieosiągalna. Cache może celowo utrzymywać regiony, allocator może ich nie zwracać kernelowi, a fragmentacja pozostawia wolne kawałki, których nie da się połączyć. Wykonaj eksperyment: zatrzymaj ruch, wymuś okres bezczynności i obserwuj, czy pamięć osiąga plateau lub spada po kontrolowanym cleanup.
Porównaj VmRSS, RssAnon, RssFile, mapowania z /proc/<pid>/smaps_rollup i pamięć cgroup. W GPU osobno mierz pamięć zaalokowaną oraz zarezerwowaną przez framework. Dopiero trend pod stałym workloadem i brak odzyskania po zakończeniu żądań uzasadniają hipotezę wycieku.
Soak test, który ma wartość
Krótki benchmark throughputu może nie ujawnić wzrostu 400 MB/min, jeśli proces kończy się po kilku minutach. Soak powinien trwać dłużej niż oczekiwany czas do OOM przy realnym limicie cgroup i mieć fazy: narastanie, steady state, spadek ruchu oraz pełny idle. Zapisuj slope pamięci, nie tylko maksimum.
Wprowadzaj jedną zmianę naraz: model, graph compilation, P/D disaggregation, backend KV transfer i wersję UCX. Macierz eksperymentów pozwoli wskazać warunek konieczny. Canary powinien reprezentować ten sam NUMA, RDMA i kernel co produkcja.
Wpływ operacyjny OOM
OOMKill może usunąć worker w połowie odpowiedzi, a retry zwiększyć ruch na pozostałych. Ustaw readiness tak, by proces z krytycznym trendem pamięci przestał przyjmować nowe zadania przed awarią. Router powinien odróżnić retry bezpieczny od zadania z wykonanym narzędziem.
Dlaczego zwykły APM nie wystarczył? Agenty APM zwykle widzą runtime aplikacji i klasyczne alokacje, nie surowe syscalls oraz wewnętrzne pule UCX. Potrzebna jest obserwowalność systemowa.
Lekcja dla postmortem
Postmortem powinien oddzielić objaw, warunek, mechanizm i poprawkę. Objawem był liniowy RSS, warunkiem konkretny P/D z NIXL, mechanizmem kolejka UCX, a poprawką zmiana hooków lub limitu. Takie rozdzielenie zapobiega błędnemu wnioskowi „vLLM zawsze wycieka”. Do raportu dołącz wykres slope, macierz eksperymentów i wersję każdej zależności.
Źródło pierwotne: Mistral AI — Heaps do lie: debugging a memory leak in vLLM.


