Skaliranje Gita u tvrtki OpenAI
Izgradnja sustava za upravljanje izvornim kodom prilagođenog opsegu rada agenata, uz ulaganje u Git ekosustav.
Tijekom protekle godine veličina našeg monorepozitorija i zahtjevi koje mora zadovoljiti rasli su zapanjujućom brzinom. Bilo je jasno da moramo preoblikovati sustav za upravljanje izvornim kodom kako bi podržao razinu istodobnog rada koju omogućuje Codex.
Izradili smo Pando, novi pozadinski sustav za Git repozitorije, bez prekida razvojnog rada ili promjene načina na koji naš tim upotrebljava Git. Pando sada podržava sav razvoj u monorepozitoriju tvrtke OpenAI. Pando je osmišljen za dva ambiciozna cilja: prvi je horizontalno skalabilno pisanje, optimizirano za istodobna slanja na velik broj grana. Drugi je najveća moguća propusnost čitanja, kako bi klijenti mogli preuzimati podatke dovoljno brzo da u potpunosti iskoriste raspoloživu mrežnu propusnost.
Te smo ciljeve ostvarili uz zadržavanje kompatibilnosti s protokolom i klijentom Git, što je našim inženjerima i agentima omogućilo jednostavno uključivanje sustava Pando u postojeće radne procese. U produkciji je Pando smanjio medijan latencije slanja gotovo pet puta. Sada isporučuje gotovo petabajt Git podataka dnevno.
Ovdje ćemo objasniti što nas je potaknulo na izradu novog sustava za upravljanje izvornim kodom i zašto smo odlučili zadržati kompatibilnost s Gitom. Usporedit ćemo naš sustav s uobičajenim pristupima skaliranju Gita i objasniti zašto je Pando posebno prikladan za potrebe skaliranja tvrtke OpenAI. Razmotrit ćemo i sljedeće korake te kako ćemo nastaviti doprinositi Gitu radi podrške opsegu rada koji donose agenti.
Codex je promijenio opseg i tempo razvoja u tvrtki OpenAI, omogućivši inženjerima da paralelno obavljaju mnogo više posla. To znači više grana i slanja, više spajanja te više dohvaćanja i kloniranja iz razvojnih okruženja, istraživačkih zadataka i sustava kontinuirane integracije (CI).
Ti se zahtjevi međusobno umnažaju. Jedan inženjer sada ima više promjena istodobno spremnih za CI, a svako slanje može pokrenuti više zadataka koji dohvaćaju isti commit.
Otprilike od kraja prošle godine sustavi koji podržavaju naš monorepozitorij teško su pratili taj tempo. Suočavali smo se sa sporim dohvaćanjem i slanjem te neuspjelim spajanjima, a pouzdanost je povremeno padala ispod 90 %. Jesmo li dosegnuli granice samog Gita ili načina na koji opslužujemo naš monorepozitorij? Git je nastao u zajednici koja je zakrpe za jezgru operacijskog sustava razmjenjivala e-poštom. Već više od dva desetljeća razvojni inženjeri prilagođavaju svoje alate Gitu kako se mijenja način na koji izrađuju softver. Kad smo počeli nailaziti na poteškoće u vlastitom monorepozitoriju, bilo nam je važno da možemo nastaviti graditi na tim temeljima.
Vidjeli smo način da zadržimo poznati način rada, a istodobno preoblikujemo pozadinski sustav kako bi paralelno obrađivao slanja na različite grane i istu povijest istodobno isporučivao brojnim klijentima koji je čitaju. Zadržavanje Gita kao temelja našeg rada s izvornim kodom značilo je da poboljšanja klijenta mogu koristiti i drugima izvan tvrtke OpenAI. Ta smo poboljšanja iz naše kopije Gita(otvara se u novom prozoru) željeli prenijeti u izvorni projekt i podijeliti ih sa širom zajednicom.
Skaliranje sustava za upravljanje izvornim kodom ima dvije ključne dimenzije: velik broj klijenata koji šalju na različite grane i velik broj klijenata koji dohvaćaju istu povijest. Pando je optimiziran za oboje.
Prvo, Pando neovisno koordinira ažuriranja svake grane. Ažuriranja nepovezanih grana mogu se odvijati paralelno, a klijent koji dohvaća granu dobiva povijest potrebnu za tu reviziju. Kompromis je u tome što klijent koji pregledava više grana može vidjeti njihova stanja iz različitih trenutaka.
Drugo, Pando ponovno upotrebljava paketne datoteke (Gitov format za zajedničko sažimanje više objekata) pri dohvaćanjima, umjesto da stvara zasebnu paketnu datoteku za svakog klijenta. Klijent može preuzeti neke objekte koje već ima, ali poslužitelj može iste bajtove isporučiti brojnim klijentima uz malo računanja.
Ti nam odabiri omogućuju skaliranje pisanja na različite grane i posluživanje čitanja iz predmemorije. Kako bismo vidjeli kako se ti pristupi nadopunjuju, počnimo s načinom na koji Git pohranjuje i prenosi povijest.
Git mora moći održavati sinkronizaciju dvaju dijelova repozitorija: objekata i referenci.
Git repozitorij bilježi snimke stanja datoteka i povijest koja ih povezuje u obliku grafa objekata. Objekt može biti blob, stablo, commit ili oznaka. Blobovi sadrže sadržaj pojedinačnih datoteka, a stabla povezuju nazive s blobovima ili drugim stablima i tako tvore strukturu direktorija. Commitovi upućuju na korijensko stablo i nula ili više roditeljskih commitova te tvore povijest repozitorija.
Objekti se identificiraju sažetkom (hashom) svoje vrste i sadržaja. Objekti su i nepromjenjivi: zapisivanjem izmijenjene datoteke stvara se novi blob, dok neizmijenjene datoteke i direktoriji mogu ponovno upotrijebiti postojeće objekte.
Grane i oznake reference su, odnosno „refs”, koje upućuju na commitove. Bilježenjem promjena na grani stvara se novi commit, a referenca grane ažurira se tako da upućuje na njega.
Git objekti međusobno upućuju jedni na druge putem ID-jeva: commitovi na stabla, a stabla na druga stabla i blobove. Grana refs/heads/main upućuje na commit C2, čiji je roditelj C1. C1 upućuje na korijensko stablo T1, u kojem parser.py upućuje na blob B1. C2 upućuje na korijensko stablo T2, u kojem parser.py upućuje na blob B2. U oba korijenska stabla docs/ upućuje na isto stablo, T3. Njegov unos README.md upućuje na blob B3, a usage.md na blob B4.
Git pohranjuje objekte u „rasutom” ili „pakiranom” obliku. Rasuti objekti pohranjuju se u zasebnim datotekama koje se identificiraju ID-jem objekta. Pakirani objekti pohranjuju se zajedno u paketnoj datoteci, što omogućuje pohranu sličnih objekata kao razlika u odnosu na druge objekte.
Git može pohranjivati blobove kao zasebne datoteke ili ih pakirati zajedno, pri čemu jedan blob kodira kao razliku u odnosu na drugi.
U Gitu je objekt dostupan iz commita ako ga Git može pronaći slijedeći veze od tog commita preko njegovih roditelja, stabala i blobova. Tijekom dohvaćanja klijent govori poslužitelju koje commitove želi, a koje već ima. Poslužitelj pronalazi objekte dostupne iz traženih commitova, isključuje one za koje zna da ih klijent već ima i šalje ostatak kao paketnu datoteku u istom formatu koji Git upotrebljava na disku. Potpuno kloniranje počinje bez ijednog objekta, pa su potrebni svi objekti dostupni iz povijesti koja se klonira.
Slanje prenosi objekte u suprotnom smjeru, uz zahtjev poslužitelju da ažurira jednu ili više referenci. Klijent upotrebljava skup referenci koje poslužitelj oglašava kako bi izbjegao slanje objekata koje poslužitelj već ima. Paket koji se šalje stoga može ovisiti o postojećim objektima: primitak samog paketa nije dovoljan za rekonstrukciju nove povijesti.
Replikacija Git repozitorija podrazumijeva održavanje dostupnosti objekata i referenci na više poslužitelja. Prije nego što replika može poslužiti novu vrijednost reference, mora moći isporučiti objekte dostupne iz te vrijednosti. Usluga mora koordinirati i konkurentna ažuriranja referenci, odnosno slanja koja pokušavaju istu granu pomaknuti s istog starog vrha na različite nove commitove. Sustavi koji nameću jedinstven redoslijed svih pisanja u repozitorij mogu uzrokovati da slanja na nepovezane grane čekaju jedno drugo.
Pri projektiranju sustava Pando procijenili smo uobičajene pristupe skaliranju upravljanja izvornim kodom uz Git: pristup koji koordinira ažuriranja referenci među replikama repozitorija trofaznom potvrdom (3PC, protokol za koordinaciju potvrde na više čvorova) i pristup koji određuje redoslijed ažuriranja putem zajedničkog dnevnika prethodnog zapisivanja (WAL, zapis promjena prije njihove primjene) u spremištu blobova.
U modeliranom dizajnu s trofaznom potvrdom usluga Git održava potpunu kopiju repozitorija na nekoliko replika. Kad bilo koji poslužitelj primi poslane podatke, dolazna paketna datoteka kopira se i paralelno šalje svim replikama. Da bi se poslane promjene objavile, većina replika mora prihvatiti ažuriranje reference. Poslužitelj koji time upravlja jest koordinator, a postupak zahtijeva više krugova komunikacije između koordinatora i svake druge replike.
Kako bi prihvatila ažuriranje reference, svaka replika zaključava svoju kopiju reference i provjerava upućuje li ona još na commit koji klijent očekuje. Koordinator zatim šalje poruku pre-commit replikama koje su prihvatile zahtjev. Kad većina potvrdi primitak poruke pre-commit, koordinator im nalaže da provedu ažuriranje. Svaka ažurna replika tada može oglašavati novi vrh grane iz svoje lokalne kopije repozitorija.
Održavanje potpunih replika omogućuje lokalno posluživanje čitanja i jamči klijentima dosljedan prikaz svih referenci bez obzira na to koju repliku odaberu. Zbog toga se reference ažuriraju jedna po jedna, a svako slanje zahtijeva više ciklusa komunikacije radi koordinacije ažuriranja reference među replikama.
Zbog čekanja kvoruma performanse diskova replika i komunikacijska latencija izravno utječu na trajanje slanja. Kako opseg pisanja raste, replike moraju držati korak i s dolaznim paketima i s prometom čitanja.
U modeliranom dizajnu temeljenom na WAL-u usluga Git upotrebljava zajednički manifest u spremištu blobova kako bi odredila redoslijed ažuriranja u cijelom repozitoriju. Kad poslužitelj primi poslane podatke, prenosi dolaznu paketnu datoteku u spremište blobova i priprema ažuriranje reference u svojoj lokalnoj kopiji repozitorija. Kako bi objavio poslane promjene, u zajednički manifest u spremištu blobova dodaje unos koji bilježi ažuriranje reference i upućuje na paketnu datoteku. Git poslužitelj zatim ažurira manifest operacijom usporedbe i zamjene. Ta operacija atomski zamjenjuje manifest ili, u suprotnom, odbija promjenu. Ako ga je drugi klijent u međuvremenu ažurirao, poslužitelj čita nove unose, provjerava je li njegovo ažuriranje reference još valjano i pokušava ponovno uz već prenesenu paketnu datoteku.
Klijenti mogu paralelno prenositi svoje paketne datoteke, ali čak i slanja na nepovezane grane zahtijevaju ažuriranje istog manifesta. Vrijeme punog ciklusa uspješne usporedbe i zamjene ograničava učestalost kojom repozitorij može objavljivati novi manifest. Spremište blobova s malom latencijom ubrzava taj korak. Grupiranje omogućuje da se jednim manifestom istodobno ažurira više referenci, ali uz stavljanje ažuriranja u red čekanja i povećanje latencije.
Spremište blobova može paralelno primati paketne datoteke, ali ažuriranja zajedničkog manifesta i dalje se međusobno natječu za pristup. Samo povećanje kapaciteta spremišta blobova ne uklanja to usko grlo. Svaka replika i dalje mora pročitati i indeksirati pakete iz spremišta blobova prije nego što ih može lokalno posluživati. Ako dolazna pisanja premaše brzinu tog rada, replike zaostaju, a klijenti dulje čekaju na dohvaćanje upravo poslanih commitova.
To nas dovodi do sustava Pando. Pando je nova arhitektura za posluživanje Git repozitorija, osmišljena za skaliranje na velik broj klijenata koji istodobno šalju podatke. Evo kako radi:
Prije razvoja sustava Pando pretpostavljali smo da možemo ispuniti svoje zahtjeve bez kompromisa u pogledu Gita, no morali smo pristati na kompromise oko nekih značajki koje većina sustava temeljenih na Gitu smatra nužnima:
Dosljednost na razini cijelog repozitorija: Dosljedan prikaz svih referenci u određenom trenutku zahtijeva koordinaciju u cijelom repozitoriju. U sustavu koji određuje zajednički redoslijed svih ažuriranja referenci, slanja na nepovezane grane natječu se za pristup jednoj koordinacijskoj točki.
Jedinstvene paketne datoteke za svakog klijenta: Ako dva klijenta s različitim lokalnim povijestima dohvaćaju istu granu, poslužitelj će možda morati izraditi zaseban paket za svakog kako ne bi slao objekte koje već imaju. Izrada tih odgovora troši resurse procesora i diskovne ulazno-izlazne operacije, što otežava skaliranje za velik broj klijenata koji čitaju podatke.
Pando umjesto toga primjenjuje blaži model dosljednosti (uzročnu dosljednost) radi skaliranja pisanja, a ponovno upotrebljava paketne datoteke radi skaliranja čitanja.
Kad klijent šalje na granu, Pando pohranjuje dolaznu paketnu datoteku kao nepromjenjivi blob u spremištu blobova. Svaki put kad se grana pomakne, Pando u spremište metapodataka zapisuje verziju reference koja sadrži novi vrh grane, paket iz kojeg potječe i sve ranije verzije referenci o kojima paket ovisi. I spremište blobova i spremište metapodataka mogu se horizontalno skalirati.
Dohvaćanje commita ili reference iz sustava Pando zahtijeva slanje skupa verzija referenci (paketnih datoteka) klijentu, čime se rekonstruira stanje klijenta koji je stvorio dohvaćenu verziju reference. Time se osigurava slabiji oblik dosljednosti na razini cijelog repozitorija, uz serijalizabilnost ažuriranja pojedinačnih referenci. Klijenti dolaze u sukob samo kad istodobno šalju na istu referencu. Klijent koji čita više grana ne može pretpostaviti da ta čitanja prikazuju stanje cijelog repozitorija u istom trenutku.
Slanjem na glavnu granu dodaje se inkrementalni paket koji sadrži jedan ili više commitova. Svako slanje na sporednu granu ponovno uključuje njezine ranije commitove, pa klijent s oglašenom verzijom glavne grane može dohvatiti tematsku granu u jednom paketu.
To je oblik uzročne dosljednosti. Kako bi dohvatio referencu, klijent koji čita podatke vidi povijest klijenta koji ju je zapisao. Tijekom slanja Pando određuje koju povijest klijent i poslužitelj smatraju zajedničkom, čime osigurava da klijenti stvaraju paketne datoteke koje se lakše mogu dijeliti.
Zbog toga Pando tijekom operacija Gita ne pruža stanje svih referenci u jednom trenutku ni redoslijed ažuriranja referenci unutar kloniranja ili dohvaćanja. Konkretno, pretpostavimo da se grane foo i bar ažuriraju tijekom pregovaranja o potpunom kloniranju. Tijekom pregovaranja jedan klijent može vidjeti granu foo u verziji 1 i granu bar u verziji 2, dok drugi vidi granu foo u verziji 2 i granu bar u verziji 1. U praksi korisnici i agenti ne ovise o globalnom redoslijedu ažuriranja referenci, nego o redoslijedu ažuriranja pojedinačne reference.
U ovoj simulaciji triju modela dosljednosti postavljamo 1000 klijenata tako da svaki odabere zasebnu granu i zatim opetovano šalje jedinstvene commitove. Pritom mjerimo ukupno proteklo vrijeme i latenciju od slanja do dohvaćanja. Potonje je važna metrika za radna opterećenja jer mjeri vrijeme punog ciklusa potrebnog korisnicima i agentima za suradnju ili upotrebu drugih sustava koji se oslanjaju na upravljanje verzijama. Simulacija obuhvaća hardver od standardnih do vrhunskih virtualnih strojeva i sustava za pohranu blobova. U ovoj smo simulaciji ublažili zahtjev za dosljednošću cijelog repozitorija pri trofaznoj potvrdi.
Simulating Git Server Implementations
Follow each writer through repeated pushes, and separately measure when an independent reader can check out each commit.
Pokrenite ovu simulaciju koja uspoređuje Pando s još dvama pristupima. Odaberite infrastrukturu od standardnog do vrhunskog hardvera, broj klijenata i druge postavke kako biste vidjeli usporedbu.
Modelirani sustav s trofaznom potvrdom pokazuje znatno veću latenciju pisanja pod opterećenjem kad diskovi dosegnu granice kapaciteta. Sustavi s dnevnikom prethodnog zapisivanja iz istog razloga pokazuju znatno veću latenciju čitanja jer poslužitelji teško prate tempo čitanja iz dnevnika. I u stvarnim testovima na našem monorepozitoriju i u ovoj simulaciji Pando podržava više tisuća klijenata koji mogu istodobno slati podatke bez utjecaja na performanse pod opterećenjem.
Pri obradi slanja Pando provjerava dolaznu paketnu datoteku dok je istodobno prenosi u spremište blobova, bez raspakiravanja u lokalni repozitorij. Kad klijent započne dohvaćanje, Pando odabire potrebne pohranjene paketne datoteke, a čitač ih preuzima. Nijedan poslužitelj ne mora primjenjivati paketne datoteke na lokalni repozitorij ni provoditi skupe operacije raspakiravanja i ponovnog pakiranja. Budući da prvenstveno pregovaraju i odabiru paketne datoteke, poslužitelji sustava Pando mogu raditi na standardnom hardveru.
To omogućuje skaliranje sustava Pando do granica klijenata, mreže između klijenata i sustava Pando te usluge pohrane blobova. Uz dovoljan kapacitet, klijenti koji rade na neovisnim granama mogu prenositi pakete najvećom brzinom koju im dopušta vlastita veza. Kako bismo tom brzinom opsluživali više klijenata, možemo dodavati poslužitelje sustava Pando i usporedno širiti spremišta metapodataka i blobova.
Pushing and Fetching a Branch in Pando
Pando stores the writer’s packfile in blob storage and records the ref version in metadata service. On fetch, Pando selects the packs the reader needs, and Express serves them.
Agent lokalno uređuje datoteke i stvara commit te pokreće git push, čime nastaje paketna datoteka tih promjena za ažuriranje grane. Pando prima paket i strujno ga prenosi u spremište blobova. Nakon provjere dolaznih podataka dekodiranjem u jednom prolazu, Pando transakcijom nad više ključeva zapisuje verziju reference u naše spremište metapodataka i atomski ažurira pokazivač reference. To sustavu Pando omogućuje dosljednost tipa „čitaj ono što si zapisao”. Cijela ta operacija pisanja zahtijeva čitanja i pisanja metapodataka.
Kad drugi klijent dohvaća granu, Pando odabire skup pohranjenih paketa na temelju povijesti koju klijent već ima. Ta operacija zahtijeva čitanja metapodataka, pri čemu je najkraća udaljenost u verzijama referenci između onoga što klijent ima i onoga što traži. Poslužitelj vraća URI-je paketnih datoteka, a klijent ih preuzima putem usluge Express, koja paketne datoteke pohranjuje u regionalnim predmemorijama.
Taj se dizajn oslanja na tri središnje usluge arhitekture sustava Pando: slanje, dohvaćanje i sažimanje.
Kad klijent šalje podatke, uobičajeni Git poslužitelj ponaša se poput igrača pokera koji otkriva sve karte: klijentu pokazuje svaku granu koju ima. Klijent zatim prikuplja commitove, stabla i blobove kako bi izradio paketnu datoteku koja isključuje objekte za koje zna da ih poslužitelj već ima.
Oglašavanjem više referenci može se smanjiti količina podataka za slanje. No to može i otežati ponovnu upotrebu paketa jer drugom klijentu mogu nedostajati objekti koje je pošiljatelj izostavio.
Pando primjenjuje drukčiji pristup. Umjesto da otkrije sve karte, klijentu oglašava samo odabrani skup grana. Za to postoje dva razloga:
Pružanje dosljednog prikaza svih referenci skupo je. Uz milijune grana, samo oglašavanje referenci može premašiti 100 MiB. Za to svaki poslužitelj mora održavati ažurno preslikavanje, uz znatan trošak koordinacije, ili izvršiti upit bazi podataka čiji opseg raste s brojem referenci.
Ako klijentu pruži samo podskup referenci, klijent će izraditi paket koji ovisi samo o onome što mu poslužitelj izričito dopusti izostaviti. Praćenje ovisnosti omogućuje sustavu Pando da paketnu datoteku koju klijent izradi pri jednom slanju ponovno upotrijebi kao odgovor na kasnije dohvaćanje.
Što više referenci Git poslužitelj oglašava, to klijenti koji šalju podatke imaju više izvora iz kojih mogu izostaviti objekte i tako izraditi manje pakete. Međutim, za ponovnu upotrebu tih paketnih datoteka poslužitelj mora moći dokazati da klijenti koji ih dohvaćaju imaju sve potrebne ovisnosti. U suprotnom paketna datoteka može izostaviti objekte koji su klijentu potrebni.
S druge strane, kad Git poslužitelj ne bi oglašavao nijednu referencu, klijent bi izradio paketnu datoteku s cijelom poviješću repozitorija. Tu bi datoteku mogao upotrijebiti bilo koji drugi klijent, no uz znatan trošak: dohvaćanje bilo koje grane praktički bi postalo potpuno kloniranje.
Oglašavanje više referenci rezultira manjim paketnim datotekama. Većina smanjenja veličine može se postići oglašavanjem samo jedne reference.
Prema zadanim postavkama, kad klijent šalje novu granu za razvoj značajke, Pando kao osnovu oglašava samo određenu glavnu granu. Tako veličina oglasa ostaje ista dok raste broj grana u repozitoriju, ali to donosi i kompromise. Kad inženjer više puta šalje na istu granu ili jednu granu za razvoj značajke nadograđuje na drugu, klijent mora ponovno poslati sve što nije u glavnoj grani.
Zbog toga uzastopna slanja na granu postaju veća, sadrže potencijalno suvišne objekte i troše dodatnu mrežnu propusnost klijenta. U našem je monorepozitoriju medijan veličine tako izrađene paketne datoteke bio manji od 20 KiB, a 99. percentil manji od 3 MiB. Za nas je to bio razuman kompromis jer klijent koji već ima oglašenu povijest glavne grane može dohvatiti granu pomoću te jedne paketne datoteke.
Nakon odabira skupa referenci koje će oglasiti klijentu, Pando prihvaća poslane podatke i ažurira spremište metapodataka. Pando dijeli ažuriranje metapodataka na sinkroni i asinkroni dio. Sinkrono ažuriranje metapodataka osigurava da klijenti mogu odmah dohvatiti upravo ažurirani commit. Pritom se zapisuje nova verzija reference i ažurira preslikavanje pokazivača reference na tu verziju, uz pohranu novog vrha grane, ovisnosti upotrijebljenih za pregovaranje o paketnoj datoteci i poveznice na poslanu paketnu datoteku.
Istodobno se u istoj transakciji bilježe asinkroni zadaci, poput zakazivanja sažimanja. Pri vrlo velikim slanjima samo se dio commitova zapisuje sinkrono. Zahvaljujući tome potreban nam je samo stalan broj čitanja i pisanja u spremište metapodataka, neovisno o veličini slanja.
Važno je da poslužitelji sustava Pando mogu neovisno prihvaćati slanja na nepovezane reference. Jedini ključ za koji se natječu jest pokazivač reference, koji štitimo atomskom operacijom nalik usporedbi i zamjeni. Propusnost pisanja možemo povećavati dodavanjem poslužitelja sustava Pando, jedinica za skaliranje spremišta metapodataka ili spremišta blobova.
Kad klijent dohvaća podatke, poslužitelju šalje dva skupa ID-jeva commitova, koji se u protokolu nazivaju have i want. Uobičajeni Git poslužitelj izračunava razliku između skupa objekata dostupnih iz traženih commitova i skupa objekata dostupnih iz postojećih commitova, sažima ih i šalje rezultat klijentu. Iako Git poslužitelj otvorenog koda ima različite lokalne indekse za ubrzavanje te operacije, izrada tih paketnih datoteka može biti skupa i one uglavnom nisu pogodne za predmemoriranje.
Pando umjesto toga upotrebljava preslikavanja commitova zabilježena pri slanju kako bi pronašao klijentove unose have i want te ih razriješio u skup verzija referenci koje sadrže te commitove. Svaka od tih verzija referenci povezana je s paketnom datotekom koju je Pando prethodno primio. Poslužitelj na temelju toga utvrđuje što klijentu treba i odabire paketne datoteke koje pokrivaju skup potrebnih objekata.
Planning fetches in Pando
Pando compares what a client has with what it wants, then selects the stored packfiles needed—including any missing branch dependencies.
Fetch 27 trunk packs.
Select a branch to include its trunk dependencies.
Pando preslikava postojeće i tražene commitove iz protokola Git u niz verzija referenci. Zahtjev za sporednom granom podrazumijeva i dohvaćanje povijesti do njezinih ovisnosti na glavnoj grani.
Protokol Git dopušta slanje samo jedne paketne datoteke u HTTP odgovoru pri kloniranju ili dohvaćanju. Međutim, nedovoljno cijenjena značajka protokola Git, packfile-uri, omogućuje poslužitelju da vrati poveznice za zasebno preuzimanje dodatnih paketa. Pando se oslanja isključivo na tu značajku, a izravno u odgovoru šalje praznu paketnu datoteku. Kako bi podržao tu značajku, OpenAI je izvornom projektu pridonio nizom poboljšanja značajke packfile-uri.
Sustavi tvrtke OpenAI rade u brojnim geografskim regijama pa u svakoj održavamo klastere sustava Pando s lokalnim predmemorijama. Usluga za isporuku paketa Express predmemorira naše spremište blobova, a zasebna usluga predmemorira spremište metapodataka. Te predmemorije omogućuju sustavu Pando da zahtjeve za ID commita ili posebno čitanje reference u cijelosti posluži iz predmemoriranog stanja u tom klasteru. Dosljednim raspršivanjem usmjeravamo zahtjeve za paketne datoteke na poslužitelje usluge Express, uz stopu pogodaka u predmemoriji veću od 99 %.
Taj sustav predmemorija pruža iznimno visoku propusnost i može potpuno iskoristiti mrežni kapacitet između klijenata i sustava Pando. Upotrebom tih URI-ja paketnih datoteka i lokalnih predmemorija potvrdili smo da operacije kloniranja i dohvaćanja mogu potpuno iskoristiti mrežne veze od 10 Gbps, i to samo uz standardne virtualne strojeve pružatelja usluga u oblaku.
Na većini Git poslužitelja ponovno pakiranje i uklanjanje nekorištenih objekata nužni su za održavanje performansi repozitorija koji raste. Oba koraka oslobađaju prostor, ali zahtijevaju mnogo računalnih resursa i ulazno-izlaznih operacija.
U repozitoriju koji se brzo mijenja, poput monorepozitorija tvrtke OpenAI, nije izvedivo slati zaseban URI paketne datoteke za svako slanje od posljednjeg dohvaćanja. Klijent bi svaki dan morao preuzeti desetke tisuća paketnih datoteka kako bi ažurirao svoju glavnu granu.
Pando umjesto toga u pozadini objedinjuje pakete kako bi smanjio taj trošak, primjenjujući dizajn sličan sažimanju u stablima spajanja sa strukturom dnevnika (LSM stablima). Svako slanje stvara paketnu datoteku koju Pando naziva „L0”, a koja predstavlja najmanju jedinicu podataka koju može prenijeti. S vremenom pozadinski procesi okupljaju te paketne datoteke u pokrivne skupove koji obuhvaćaju niz verzija referenci, a njihov opseg geometrijski raste od razine L0. Danas naš monorepozitorij upotrebljava pokrivne skupove paketnih datoteka do razine L4.
Ti pokrivni skupovi imaju dvojaku svrhu. Kad se pokrivni skup dovoljno napuni, proces za sažimanje preuzima ga i izrađuje jednu paketnu datoteku. Time se poboljšava omjer kompresije jer se objekti unutar paketne datoteke reorganiziraju. No poboljšavaju se i performanse pregovaranja pri dohvaćanju jer skup služi kao lista s preskakanjem verzija referenci. Istodobnim pregledavanjem indeksa na svakoj razini Pando može u logaritamskom vremenu odabrati skup paketa za slanje klijentu. Ti pokrivni skupovi paketnih datoteka, bez obzira na to jesu li ponovno zapakirani, omogućuju učinkovitije planiranje dohvaćanja te smanjuju zahtjeve za propusnošću i latencijom sustava Pando.
Compaction in Pando
Small packfiles are grouped into larger covers across levels, improving compression and speeding up fetches.
Sažimanje u pozadini objedinjuje paketne datoteke iz brojnih verzija referenci, što omogućuje učinkovitije dohvaćanje uz manje datoteka.
Za razliku od mnogih Git poslužitelja, paketne datoteke zadržavaju se na svakoj razini i ne odbacuju se niti uklanjaju kao nekorišteni objekti. Paketi na svim razinama neprekidno se upotrebljavaju za posluživanje klijenata, na temelju unosa have i want koje klijenti šalju pri dohvaćanju. Za klijente koji Pando upotrebljavaju kao API, čitanje proizvoljnog objekta iz paketne datoteke obično zahtijeva čitanje manje podataka iz datoteke L0 nego iz L4 jer je manja vjerojatnost da je objekt komprimiran.
Sažimanje u sustavu Pando znatno smanjuje ukupnu količinu podataka koja se prenosi klijentima, a omogućuje i da trošak planiranja paketa raste sporije od linearnog rasta zaostatka klijenta. To je osobito važno za velike repozitorije koji se brzo mijenjaju, za koje očekujemo da će postati nova norma u razvoju softvera.
Pando smo izradili uz Codex, od pokretanja projekta do produkcije, u samo 10 tjedana. Jezgru sustava Pando osmislio je jedan inženjer, implementirali su je timovi agenata, a u produkciju ju je uvelo nekoliko inženjera. Pando je počeo obrađivati produkcijski promet u travnju ove godine i dio je šireg sustava alata za upravljanje izvornim kodom koji se upotrebljava u tvrtki OpenAI.
Pri provjeri dizajna sustava Pando primijenili smo brojne pristupe. Primijenili smo svoja načela za izgradnju sigurnijih softverskih temelja. Budući da smo kretali od nule, odlučili smo Pando implementirati u Rustu i bez naslijeđenih ovisnosti. Napisali smo skup testova sukladnosti kako bi agenti mogli provjeravati svoje promjene u odnosu na standardno ponašanje Gita. Razvili smo i testne sklopove za determinističke simulacije koji nam omogućuju provjeru invarijanti i svojstava sustava. Tako smo mogli pronaći i ublažiti teško uočljive probleme uzrokovane redoslijedom istodobnih operacija te regresije u pregovaranju i planiranju paketa.
Mnogi su modeli pridonijeli tom sustavu. Kako su novi modeli postajali dostupni, upotrebljavali smo ih za poboljšanje performansi i pouzdanosti sustava Pando. Uz GPT‑6 Astra agenti su pisali u jezicima Lean i TLA+ kako bi formalizirali „teoriju sustava Pando”, kojom smo se koristili za pronalaženje i ispravljanje pogrešaka u implementaciji te dokazivanje svojstava sustava. Više neprekidnih sesija trajalo je stotinama sati, tijekom kojih su se pronalazili i ispravljali rubni slučajevi u sustavu Pando. Agente pokrećemo i danonoćno kako bi uz modele s mogućnostima sustava Daybreak provodili napadačko sigurnosno testiranje Gita i sustava Pando.
Pando smo isprva uveli kao posrednički poslužitelj koji prosljeđuje čitanja i pisanja našem postojećem sustavu za upravljanje izvornim kodom. Tako smo mogli održavati sinkronizaciju sustava Pando i prethodnog sustava te sigurno prebacivati promet na Pando bez prekida rada.
U sklopu svojeg mehanizma predmemoriranja Pando može i uklanjati duplikate zahtjeva te ih objedinjavati prije prosljeđivanja. To nam je omogućilo da postojeći alati nastave pouzdano raditi dok smo sustave jedan po jedan prebacivali na Pando. Kako smo sve više radnih opterećenja prebacivali na Pando, njihova su slanja punila njegovu predmemoriju i poboljšavala pouzdanost prethodnog pozadinskog sustava.
Kako bismo optimizirali performanse pri upotrebi sustava Pando, počeli smo isporučivati svoju javnu distribuciju Gita na računala razvojnih inženjera i unutar aplikacije ChatGPT za stolna računala. Za postupni prijelaz klijenata na Pando upotrijebili smo interni omotač oko Gitova sučelja naredbenog retka. Izradili smo i mnoge druge alate za obradu zahtjeva za promjenama i pregled koda te API-je za automatizaciju. Uz Git promet migrirali smo stotine integracija koje su ovisile o naslijeđenim API-jima, webhookovima i preuzimanjima repozitorija.
I sam Codex pomogao je razvojnim inženjerima i timovima pri prelasku. Izradili smo vještine za pripremu postojećih radnih kopija i usmjeravanje agenata na upotrebu novog sustava. Zahvaljujući tome, razvojni inženjeri mogli su jednostavno zatražiti od Codexa da migrira njihova razvojna okruženja i usluge.
Prelazak na Pando poboljšao je pouzdanost spajanja i smanjio medijan latencije slanja za gotovo 80 %.
Pando može isporučiti petabajt Git podataka dnevno uz samo nekoliko procesorskih jezgri. Pando je u jednom satu isporučio više od 57 terabajta Git paketa, pri čemu je više od 99 % tih bajtova došlo iz lokalne diskovne predmemorije.
Ponovna upotreba paketa omogućuje brojnim klijentima preuzimanje iste povijesti repozitorija uz malo računanja na čvorovima koji ih poslužuju. Ovaj primjer prikazuje jedan repozitorij, ali Pando je osmišljen tako da podržava mnogo više njih. Kako potražnja raste, isporuku podataka i pregovaranje u Gitu možemo skalirati neovisno.
Izgradnja sustava Pando na protokolu i klijentu Git pomogla nam je prepoznati pogreške i prilike za poboljšanje performansi samog Gita.
Primjerice, Pando izravno isporučuje klijentima pakete za ponovnu upotrebu, pa su preuzimanje i indeksiranje tih paketa postali ključni za brzinu i pouzdanost sustava. Taj je rad otkrio rubne slučajeve pri istodobnim preuzimanjima, čišćenju nakon prekinutih prijenosa i nastavku već dovršenih preuzimanja. Ispravili smo te dijelove koda, dodali provjere pri prihvaćanju postojećeg paketa za dohvaćanje i prenijeli ta poboljšanja u izvorni projekt.
Trebali smo vidjeti i na što se troše vrijeme i mrežna propusnost pa smo dodali praćenje preuzimanja putem značajke packfile-uri i brojanje bajtova pri izradi paketa. Tako je lakše utvrditi troši li sporo dohvaćanje vrijeme na izradu paketa ili na njihov prijenos.
U izdanju Gita tvrtke OpenAI(otvara se u novom prozoru) klijent može paralelno preuzimati i indeksirati neovisne pakete. Dodali smo koncept „izričito navedenih postojećih commitova” kako bi poslužitelj mogao oglašavati trenutačni vrh grane odvojeno od povijesti koju klijent pri slanju smije izostaviti iz svojeg paketa. U sustavu Pando to omogućuje klijentima da upotrijebe --force-with-lease kako bi provjerili da se grana za razvoj značajke nije pomaknula te izradili njezin paket samo u odnosu na glavnu granu. Svatko tko ima tu povijest glavne grane može zatim dohvatiti granu pomoću istog paketa.
Nekoliko tih poboljšanja već je uključeno u izvorni Git, a mnoga druga nalaze se u našem javnom repozitoriju. Nastavljamo surađivati sa zajednicom Gita kako bi ta poboljšanja bila korisna i izvan sustava Pando. Taj rad možete pratiti u našem javnom sustavu za praćenje zakrpa(otvara se u novom prozoru).
Preispitivanjem uvriježenih pristupa skaliranju Git repozitorija, Pando nam omogućuje skaliranje monorepozitorija bez promjene načina rada s Gitom kojima se naši inženjeri i agenti već služe. U našem je monorepozitoriju Pando omogućio veću paralelnost, poboljšao pouzdanost spajanja i skratio vrijeme objavljivanja promjena koda. Osim primjene u monorepozitoriju, istražujemo i druge primjene sustava Pando unutar tvrtke OpenAI, uključujući radna opterećenja pri kojima velik broj agenata mora surađivati na zajedničkom Git repozitoriju.
Kako inženjeri i agenti budu stvarali sve više softvera, sve više timova izvan tvrtke OpenAI naići će na ograničenja upravljanja izvornim kodom s kojima smo se i mi susreli. Trenutačno istražujemo načine da tehnologiju na kojoj se temelji Pando podijelimo s korisnicima.
Pando se nadovezuje na dva desetljeća iskustva korisnika i administratora Git repozitorija. Nastavit ćemo razvijati Pando i surađivati sa zajednicom otvorenog koda oko Gita kako bismo podijelili sva poboljšanja koja mogu koristiti široj zajednici njegovih korisnika.


