Vazba NIXL pro Rust běžela ve dvou obrazech Dynama na náhradních funkcích
NVIDIA vydala 29. srpna opravu Dynama 1.4.2. Ve dvou jeho kontejnerových obrazech ležela knihovna NIXL v adresáři, kam dynamický zavaděč nedohlédne; přitom je to ona, kdo při rozděleném běhu přenáší data mezi uzly. Vazba na ni psaná v Rustu se proto přepnula na náhradní funkce a odmítla založit agenta, kdežto pythonová vazba v témž procesu fungovala.

Dynamo je otevřená platforma NVIDIE pro rozdělenou inferenci. Rozvrhuje dotazy mezi uzly, drží společný index KV cache a umí oddělit fázi předvyplnění od fáze generování. Data mezi uzly za ní přenáší NIXL, tedy NVIDIA Inference Xfer Library: knihovna pod licencí Apache 2.0, která podle vlastního popisu zrychluje přímou komunikaci dvou bodů v inferenčních rámcích a přes zásuvné moduly schovává rozdíl mezi pamětí procesoru, pamětí akcelerátoru a úložištěm.
Vydání 1.4.2 z 29. srpna je záplata nad verzí 1.4.1 a řeší v podstatě jedinou věc. V obrazu frontendu a v běhovém obrazu pro SGLang byla knihovna NIXL nainstalovaná tak, že ji dynamický zavaděč nenašel.
Zavaděč hledá podle jména, kolo si knihovnu schovává
Vazba NIXL pro Rust, balíček nixl-sys, se dá přeložit i tam, kde NIXL při stavbě vůbec není. Do sestavení se pak zakompilují náhradní funkce, anglicky stubs: tenké obálky, které si skutečnou knihovnu otevřou až při prvním použití a pak jí každé volání předají. Dělají to jediným řádkem, dlopen("libnixl_capi.so", RTLD_NOW | RTLD_LOCAL). Je to holé jméno bez cesty, takže rozhoduje, co má proces na cestě zavaděče. Ověřit se to dá i bez GitHubu: řádek je na devětačtyřicáté pozici v souboru stubs.cpp balíčku nixl-sys 1.0.1, vydaného 14. dubna.
Kolo s NIXL z PyPI si nativní knihovny ukládá do soukromého adresáře .nixl_cu13.mesonpy.libs. Vede do něj jen relativní odkaz zapsaný v pythonovském rozšíření. Rozšíření Dynama dynamo/_core.abi3.so žádný takový odkaz nemá, a tak dlopen selže. Funkce nixl_capi_is_stub() vrátí pravdu; v náhradách se celé její tělo scvrkne na otázku, jestli se otevření nepovedlo. A zakládání agenta skončí hláškou „NIXL is not supported in stub mode“.
Tiché to bylo při stavbě, ne při volání
Poznámky k vydání i oba opravné návrhy píšou, že vazba pro Rust běžela na náhradách silently, tedy potichu. Přesnější je říct, kdy potichu. Obraz se postavil, prošel a rozeslal se bez jediného varování, protože nic ve stavbě neověřovalo, kde knihovna skončila. Sama chvíle selhání tichá není: Dynamo vrátí jmenovitou chybu. Tiché bylo všechno do té chvíle.
Proč se toho nikdo nevšiml dřív, naznačuje komentář v šabloně běhového obrazu pro vLLM. Opatření tam prý drží rustovské dlopen mimo náhradní režim „v procesech, které si napřed nenaimportují pythonový balíček nixl“. Když si tedy proces knihovnu natáhne přes Python dřív, všechno chodí; podle nás proto, že zavaděč si otevřené objekty vede podle jména a druhé volání už nic nehledá. Ověření „je NIXL v obrazu?“ udělané v Pythonu proto vyšlo kladně i tam, kde rustovská polovina knihovnu neměla.
Ve vLLM se totéž spravilo o pět týdnů dřív
Ten komentář je datovatelný. V šabloně pro vLLM stojí od vydání 1.3.0 z 22. července, kde obraz začal vytahovat libnixl.so z kola a vystavovat ji pod stálou cestou. U frontendu a u SGLangu na řadu došlo až teď. První z obou návrhů je z 19. srpna a přijali ho 21. srpna, druhý z 21. srpna a přijali ho 26. srpna. Vydání 1.4.1 vyšlo mezitím, 22. srpna, a nemělo ani jeden z nich.
Od kdy se vada v obrazech projevovala, poznámky k vydání neuvádějí a z kódu se to spolehlivě odečíst nedá. Doložit jde jen stáří toho tvaru instalace: šablona obrazu frontendu doplňuje kolo s NIXL a nikde nezapisuje jeho adresář na cestu zavaděče už ve vydání 1.0.0 z 13. března. Není to samo o sobě doklad, že tenkrát obraz nefungoval, protože se od té doby měnilo i to, jak se rustovská část sestavuje.
Dvě knihovny místo jedné
Oprava frontendu při té příležitosti odklidila i druhou nejasnost. Metabalíček nixl žádá oba proudy pro CUDA bez podmínky a jeho volby cu12 a cu13 jsou podle popisu návrhu jen zbytkové. Dosavadní zápis nixl[cu13] proto doinstaloval i sestavení pro CUDA 12, které obraz nikdy nepoužil, a zavaděč měl na výběr ze dvou souborů libnixl_capi.so. Nově se instaluje nejprve nixl-cu13 a teprve pak metabalíček bez závislostí, takže zbude jedna.
Přibyly pojistky. Stavba obrazu skončí chybou, když libnixl_capi.so nebo adresář se zásuvnými moduly nejsou tam, kde se čekají, a skript pro SGLang po ldconfig navíc ověří, že se jméno knihovny opravdu dá přeložit. Ze spouštěcího skriptu pro obrazové modely naopak zmizela ruční berlička, která si tentýž adresář vyváděla do LD_LIBRARY_PATH sama, a přitom obracela pořadí, ve kterém se hledají knihovny UCX.
Proč frontend vůbec potřebuje knihovnu na přenos dat, vysvětluje poznámka v testech. Přepínač --frontend-decoding nechá frontend rozbalovat multimodální vstupy a při tom založí skutečného agenta NIXL. Test s TensorRT-LLM, který na to narazil, je kvůli náhradnímu režimu odsunutý do nočního kola a ve stromu verze 1.4.2 tam i s tou poznámkou pořád je. Vedle oprav přineslo vydání ještě Dynamo Enterprise, tedy tytéž artefakty s příponou -enterprise v katalogu NGC; podle poznámek k vydání se od otevřených neliší funkčně ani binárně a mění se jen nárok na podporu.
Pět odkazů na jednu knihovnu
Jedna nesouměrnost zůstává. Soubor context.yaml drží v jediném vydání pět různých odkazů na NIXL: v1.0.1 pro rustovskou vazbu v běhovém cíli, v1.3.2 pro frontend i pro vLLM, v1.3.0 pro SGLang a v1.3.1 pro TensorRT-LLM. Mezi nejstarším a nejnovějším z nich jsou víc než tři měsíce vývoje. Dokud se knihovna otevírá podle holého jména, o tom, kterou z nich proces opravdu dostane, rozhoduje cesta zavaděče, ne to, co je napsané v konfiguraci stavby.
Chyba, která přežije několik vydání, není v tomhle oboru výjimkou: přepínač pro kvantizaci v SGLangu hlásil devět vydání po sobě jen chybu.