MongoDB 8 uz Linux 6.19 un jaunākiem kodoliem vairs nestartē. Procesa mongod vietā lietotājs saņem paziņojumu par zināmu nesaderību vai vienkārši izejas kodu 139. Vaina nav ne datubāzē, ne kodola kļūdā. Kodols pārstāja veikt vienu lieku ierakstu lietotāja telpas atmiņā, uz kuru gadiem balstījās Google atmiņas piešķīrējs TCMalloc.
Ierakstu izņēma optimizācija ar nosaukumu rseq: Optimize event setting, commit 39a167560a61. Pirms tās kodols katrā atgriešanās reizē no kodola uz lietotāja telpu pārrakstīja lauku cpu_id_start koplietotajā struktūrā struct rseq. Pēc tās kodols šo lauku atjauno tikai tad, kad pavediens patiešām pārceļas uz citu procesora kodolu. Dokumentētā ABI daļa palika neskarta. Ātrums rseq apstrādes ceļā pieauga aptuveni par 15 %.
Lauks, kuram vajadzēja būt tikai lasāmam
Restartējamās sekvences ļauj pavedienam izpildīt īsu kritisko posmu bez atomārām operācijām. Ja posmu pārtrauc pārplānošana vai signāls, kodols to vienkārši sāk no jauna. TCMalloc uz šī mehānisma būvē atsevišķu kešatmiņu katram procesora kodolam. Piešķiršana nolasa rādītāju, atdod objektu un ar vienu apliecinošu ierakstu samazina galvenes vērtību.
Ātrajā ceļā rādītāja pārrēķināšana maksā pārāk dārgi, tāpēc TCMalloc savas struktūras noliek tieši virs struct rseq tā, ka cpu_id_start aizņem iekšējā rādītāja augšējos četrus baitus. Rādītājam augšējais bits ir uzstādīts, jo īsti procesora numuri nekad nesniedzas līdz 2^31. Pārbaude ir divas instrukcijas: kods nolasa vārdu pirms __rseq_abi un pie nulles augšējā bita iet uz lēno ceļu.
Kodola ieraksts laukā cpu_id_start šo bitu nodzēsa. Rādītājs kļuva nederīgs, TCMalloc to pārrēķināja no jauna. Tā kā vecais kodols rakstīja bez nosacījumiem, signāls pienāca arī tad, kad pavediens palika uz tā paša procesora kodola. Triks ir atklāti aprakstīts TCMalloc dokumentācijā. Kodola saskarne to nekad nebija apsolījusi.
LWN raksta virsraksts gadījumu sasaista ar Hairuma likumu (Hyrum’s Law): ja saskarnei ir pietiekami daudz lietotāju, katra novērojamā uzvedība kļūst par kāda atkarību neatkarīgi no tā, kas rakstīts specifikācijā. Kodola rseq apraksts lauku cpu_id_start atzīmē kā tikai lasāmu no lietotāja telpas puses. TCMalloc tajā tomēr raksta savu rādītāju un rēķinās ar to, cik bieži laukam raksta pats kodols.
Kļūdu atrada MongoDB komanda
TCMalloc kļūdu izsekotājā problēma nonāca 13. martā. Vienības tests background_test_no_glibc_rseq stabili krita ar CHECK kļūdu failā percpu_tcmalloc.h 852. rindā, kam sekoja SIGABRT. Kodola uzturētāju atbilde pieteikumā numur 292 bija īsa: TCMalloc pārkāpj rseq ABI. Uzdevums joprojām ir atvērts un bez atbildīgā.
22. aprīlī Mathias Stearn no MongoDB komandas aiznesa jautājumu uz kodola izstrādātāju sarakstu. Viņš pieteica divas lietas: parastu 64 bitu Arm realizācijas kļūdu un dziļāko nesaderību ar 6.19. Tā pati atkarība no nedokumentētas uzvedības TCMalloc izsekotājā jau bija pieteikta 2022. gadā. Toreiz neviens neko nemainīja.
Regresiju noteikums pret nedokumentētu uzvedību
Tas skaidri pārkāpj mūsu regresiju noteikumus.
Linuss Torvalds par kodola izmaiņu, kas salauž agrāk strādājušu programmu
Noteikums kodolā ir vecs un vienkāršs. Ja lietotāja programma strādāja, tai jāstrādā arī pēc kodola atjauninājuma. Tas, ka programma balstījās uz uzvedību, kuru neviens nebija apsolījis, neko nemaina. Tomass Gleiksners uz kodola saraksta to nosauca par situāciju, kurā viens pārkāpējs pamatoti iegūst īpašumtiesības uz vispārēju koplietotu saskarni.
Struktūra aug no 32 uz 33 baitiem
Gleiksnera piedāvātais labojums neprasa izmaiņas ne TCMalloc, ne glibc pusē. struct rseq reģistrācijas izmērs pieaug no 32 uz 33 baitiem. Tas savukārt piespiež 64 baitu izlīdzināšanu.
Kas reģistrē veco 32 baitu struktūru vai neizlīdzina to pareizi, saņem uzvedību, kāda bija pirms 6.19: cpu_id_start tiek pārrakstīts vienmēr. Jaunākas iespējas, tostarp laika kvanta pagarināšana, šādam izsaucējam nav pieejamas. Kas padod 33 baitus ar 64 baitu izlīdzinājumu, iegūst pilnu 6.19 ātrdarbību. Detalizētu gaitu apraksta LWN raksts par rseq, TCMalloc un Hairuma likumu.
Matjē Denuajē ieteica vienkāršāku ceļu: karodziņu, ar ko pieprasīt ātro uzvedību. Gleiksners atteicās. Karodziņš sarežģī gadījumu, kad vienā procesā rseq izmanto vairāki dalībnieki, piemēram, glibc kopā ar pielinkotu TCMalloc. glibc rseq reģistrē pēc noklusējuma kopš 2.35. versijas, tāpēc šādu procesu ir daudz. Vecākas glibc kopijas jaunajā shēmā paliek uz lēnākā ceļa, kamēr kāds tās pārbūvē.
Ko darīt, kamēr labojuma nav
MongoDB versijas no 8.0 līdz 8.2 TCMalloc iekļauj savā kodā, tāpēc problēma skar visus izplatīšanas veidus: tar arhīvus, pakotņu repozitorijus un Docker attēlus. Kopienā pārbaudītā apeja ir izslēgt glibc rseq reģistrāciju vidē ar GLIBC_TUNABLES=glibc.pthread.rseq=0. Oficiālajā MongoDB dokumentācijā tā neparādās.
Pārbaude, vai serveris ir riska zonā, prasa divas komandas. uname -r parāda kodola versiju. nm -D vai strings uz izpildāmā faila parāda, vai tajā ir TCMalloc simboli. Sistēmas glibc versija pati par sevi neko nepasaka, jo MongoDB nes savu piešķīrēja kopiju. Programmas, kas atmiņu ņem caur sistēmas glibc vai jemalloc, defekts neskar.
Appwrite pašizvietotajā serverī 1.9.5 tā pati avārija tika pieteikta 4. jūlijā uz Ubuntu 26.04 ar kodolu 7.0.0-27-generic. Ansible kolekcijas, kas MongoDB izvieto ražošanā, tikmēr pievieno kodola versijas pārbaudi, kura krīt uz 6.19 un jaunākiem kodoliem, ar apejas karodziņu mongodb_skip_kernel_check: true.
MongoDB pozīcija jūnijā bija gaidīt izlaboto TCMalloc. Laidieni līdz 8.2 to nesatur.
Avoti
- Restartable sequences, TCMalloc, and Hyrum’s Law (LWN.net)
- RSEQ related crashes since linux-6.19 (google/tcmalloc, issue 292)
- Restartable Sequence Mechanism for TCMalloc
- MongoDB 8 crashes on Linux kernel 6.19+ (startechnica/ansible-collection-infra, issue 2)
- Self-Hosted MongoDB 8.2.5 requires GLIBC_TUNABLES on kernel 6.19+ (appwrite, issue 12786)
Titulbildes foto: Kevin Ache, Unsplash.
Komentāri
Šim rakstam vēl nav komentāru. Esi pirmais, kurš dalās ar savu viedokli.