OpenAI

2. novembar 2026.

Inženjering

Skaliranje Gita u kompaniji OpenAI

Izgradnja kontrole izvornog koda za obim rada agenata, uz ulaganje u Git ekosistem.

Autori: Aaron Friel, Taylor Blau i Ted Nyman, članovi tehničkog tima

Učitavanje…

Tokom protekle godine veličina našeg monorepozitorija i zahtjevi koje mora ispuniti rasli su zapanjujućom brzinom. Bilo je jasno da moramo obnoviti sistem za kontrolu izvornog koda kako bi podržao obim istovremenog rada koji Codex omogućava.

Izgradili smo Pando, novi pozadinski sistem za Git repozitorije, bez prekidanja inženjerskog rada ili promjene načina na koji naš tim koristi Git. Pando sada podržava sav razvoj u monorepozitoriju kompanije OpenAI. Pando je osmišljen za dva ambiciozna cilja: prvo, horizontalno skalabilne upise, optimizirane za istovremena slanja na mnogo grana. I drugo, maksimalan protok pri čitanju, kako bi klijenti mogli preuzimati podatke dovoljno brzo da u potpunosti iskoriste raspoloživu mrežnu propusnost.

Ostvarili smo te ciljeve uz očuvanje kompatibilnosti s Git protokolom i klijentom, što je našim inženjerima i agentima omogućilo da bez poteškoća uključe Pando u postojeće tokove rada. U produkciji je Pando smanjio medijan latencije slanja gotovo pet puta. Sada isporučuje gotovo petabajt Git podataka dnevno.

Ovdje ćemo objasniti zašto smo izgradili novi sistem za kontrolu izvornog koda i zašto smo odlučili zadržati kompatibilnost s Gitom. Uporedit ćemo naš sistem s uobičajenim pristupima skaliranju Gita i objasniti zašto Pando može ispuniti potrebe kompanije OpenAI za skalabilnošću na način na koji drugi pristupi ne mogu. Govorit ćemo i o narednim koracima te o tome kako ćemo nastaviti doprinositi Gitu da podrži obim rada koji donose agenti.

Zašto nam je trebao novi sistem za kontrolu izvornog koda

Codex je promijenio obim i tempo razvoja u kompaniji OpenAI, omogućivši inženjerima da obavljaju mnogo više posla paralelno. To znači više grana i slanja, više spajanja te više dohvata i kloniranja iz razvojnih okruženja, istraživačkih zadataka i sistema kontinuirane integracije (CI).

Ti zahtjevi se međusobno uvećavaju. Jedan inženjer sada ima više izmjena spremnih za CI istovremeno, a svako slanje može pokrenuti više zadataka koji dohvaćaju isti commit.

Otprilike od kraja prošle godine sistemi koji podržavaju naš monorepozitorij počeli su teško pratiti taj tempo. Suočavali smo se sa sporim dohvatima i slanjima te neuspjelim spajanjima, a pouzdanost je povremeno padala ispod 90%. Jesmo li dosegli granice samog Gita ili načina na koji opslužujemo naš monorepozitorij? Git je nastao u zajednici koja je zakrpe za jezgro operativnog sistema razmjenjivala e-poštom. Više od dvije decenije programeri prilagođavaju svoje alate Gitu u skladu s promjenama načina razvoja softvera. Kada smo počeli nailaziti na poteškoće u vlastitom monorepozitoriju, bilo nam je važno da možemo nastaviti graditi na tom radu.

Vidjeli smo način da zadržimo poznati tok rada, a obnovimo pozadinski sistem tako da paralelno obrađuje slanja na različite grane i istovremeno isporučuje istu historiju brojnim klijentima. Zadržavanje Gita kao temelja našeg toka rada za kontrolu izvornog koda značilo je da poboljšanja klijenta mogu biti korisna i izvan kompanije OpenAI. Željeli smo ta poboljšanja iz naše kopije Gita⁠(otvara se u novom prozoru) prenijeti u izvorni projekat i podijeliti ih sa širom zajednicom.

Projektovanje Gita za veliki obim rada

Skaliranje sistema za kontrolu izvornog koda ima dvije ključne dimenzije: mnogo klijenata koji šalju podatke na različite grane i mnogo klijenata koji dohvaćaju istu historiju. Pando je optimiziran za obje.

Prvo, Pando koordinira ažuriranja svake grane nezavisno. Ažuriranja nepovezanih grana mogu se odvijati paralelno, a klijent koji dohvaća granu dobija historiju 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 ponovo koristi paketne datoteke (Gitov format za zajedničku kompresiju više objekata) u različitim dohvatima, umjesto da generiše zasebnu paketnu datoteku za svakog klijenta. Klijent može preuzeti neke objekte koje već ima, ali server može isporučivati iste bajtove mnogim klijentima uz malo računanja.

Ovi izbori omogućavaju nam skaliranje upisa na različite grane i opsluživanje čitanja iz keša. Da bismo vidjeli kako se sve to uklapa, počnimo od načina na koji Git pohranjuje i prenosi historiju.

Osnove pohrane objekata u Gitu

Git mora moći održavati dva dijela repozitorija sinhroniziranim: objekte i reference.

Git repozitorij bilježi snimke datoteka i historiju koja ih povezuje u obliku grafa objekata. Objekt može biti blob, stablo, commit ili oznaka. Blobovi čuvaju sadržaj pojedinačne datoteke, a stabla povezuju nazive s blobovima ili drugim stablima kako bi formirala strukturu direktorija. Commiti upućuju na korijensko stablo i nula ili više roditeljskih commita, čime formiraju historiju repozitorija.

Objekti se identifikuju hash vrijednošću svog tipa i sadržaja. Objekti su i nepromjenjivi: bilježenje izmijenjene datoteke stvara novi blob, dok neizmijenjene datoteke i direktoriji mogu ponovo koristiti postojeće objekte.

Grane i oznake su reference, odnosno „refs”, koje upućuju na commite. Bilježenje izmjena na grani stvara novi commit i ažurira referencu grane tako da upućuje na njega.

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. Njegova stavka README.md upućuje na blob B3, a usage.md na blob B4.

Git objekti upućuju jedni na druge putem ID-jeva: commiti 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. Njegova stavka README.md upućuje na blob B3, a usage.md na blob B4.

Git pohranjuje objekte u „rasutom” ili „zapakovanom” obliku. Rasuti objekti pohranjuju se u zasebnim datotekama koje se identifikuju ID-jem objekta. Zapakovani objekti pohranjuju se zajedno u paketnoj datoteci, što omogućava da se slični objekti pohrane kao razlike, odnosno delte, u odnosu jedni na druge.

Dvije verzije datoteke parser.py prikazane su u $GIT_DIR/objects. Kao rasuti objekti, blob A sadrži parse(), a blob B sadrži i parse() i parse_file(). Paketna datoteka pohranjuje A u cijelosti, a B predstavlja kao upute za kopiranje cijelog A i dodavanje parse_file(). Prateća datoteka *.idx određuje položaj oba bloba u paketnoj datoteci prema ID-ju objekta.

Git može pohraniti blobove kao zasebne datoteke ili ih zapakovati zajedno, kodirajući jedan blob kao razliku u odnosu na drugi.

U Gitu je objekt dosegljiv iz commita ako ga Git može pronaći prateći veze od tog commita kroz njegove roditelje, stabla i blobove. Tokom dohvata klijent obavještava server koje commite traži, a koje već ima. Server pronalazi objekte dosegljive iz traženih commita, izostavlja one za koje zna da ih klijent već ima i šalje ostatak kao paketnu datoteku u istom formatu koji Git koristi na disku. Potpuno kloniranje počinje bez ijednog objekta, pa su mu potrebni svi objekti dosegljivi iz historije koja se klonira.

Slanje prenosi objekte u suprotnom smjeru, uz zahtjev serveru da ažurira jednu ili više referenci. Klijent koristi skup referenci koje server oglašava kako bi izbjegao slanje objekata koje server već ima. Paket koji se šalje zato može zavisiti od postojećih objekata: prijem samog paketa nije dovoljan za rekonstrukciju nove historije.

Repliciranje Git repozitorija znači održavanje dostupnosti i objekata i referenci na više servera. Prije nego što replika može isporučiti novu vrijednost reference, mora moći isporučiti objekte dosegljive iz te vrijednosti. Usluga također mora koordinirati konkurentna ažuriranja referenci, odnosno slanja koja pokušavaju pomjeriti istu granu s istog starog vrha na različite nove commite. Sistemi koji nameću jedinstven redoslijed svih upisa u repozitorij mogu dovesti do toga da slanja na nepovezane grane čekaju jedno na drugo.

Postojeći obrasci distribuiranih Git usluga

Pri projektovanju sistema Pando procijenili smo konvencionalne pristupe skaliranju kontrole izvornog koda pomoću Gita: jedan koji koordinira ažuriranja referenci među replikama repozitorija pomoću trofazne potvrde transakcije (3PC, protokol za koordinaciju potvrde na više čvorova) i drugi koji određuje redoslijed ažuriranja putem zajedničkog dnevnika prethodnog upisa (WAL, zapisa izmjena prije njihove primjene) u spremištu blobova.

Trofazna potvrda transakcije (3PC)

U modeliranom dizajnu s trofaznom potvrdom transakcije, Git usluga čuva potpunu kopiju repozitorija na nekoliko replika. Kada bilo koji server primi slanje, dolazna paketna datoteka kopira se i paralelno šalje svim replikama. Da bi se poslane izmjene objavile, većina replika mora prihvatiti ažuriranje reference. Server koji time upravlja je koordinator, a postupak zahtijeva više krugova komunikacije između koordinatora i svake druge replike.

Da 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 replikama koje prihvate ažuriranje šalje poruku pripreme za potvrdu. Kada većina potvrdi prijem te poruke, koordinator im nalaže da izvrše ažuriranje. Svaka ažurna replika zatim može oglašavati novi vrh grane iz svoje lokalne kopije repozitorija.

Održavanje potpunih replika omogućava lokalno opsluživanje čitanja i garantuje da klijenti dobiju konzistentan 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 razmjena zahtjeva i odgovora radi koordinacije ažuriranja reference među replikama.

Čekanje na kvorum čini trajanje slanja zavisnim od performansi diskova replika i latencije komunikacije. Kako obim upisa raste, replike moraju pratiti i dolazne pakete i saobraćaj čitanja.

Dnevnik prethodnog upisa (WAL)

U modeliranom dizajnu zasnovanom na WAL-u, Git usluga koristi zajednički manifest u spremištu blobova za određivanje redoslijeda ažuriranja u cijelom repozitoriju. Kada server primi slanje, prenosi dolaznu paketnu datoteku u spremište blobova i priprema ažuriranje reference u svojoj lokalnoj kopiji repozitorija. Da bi objavio poslane izmjene, dodaje stavku u zajednički manifest pohranjen u spremištu blobova, koja bilježi ažuriranje reference i upućuje na paketnu datoteku. Git server zatim ažurira manifest operacijom uporedi-i-zamijeni. Ta operacija atomski zamjenjuje manifest ili odbija izmjenu. Ako ga je u međuvremenu ažurirao drugi klijent, server čita u međuvremenu dodane stavke, provjerava je li njegovo ažuriranje reference i dalje valjano i pokušava ponovo s paketnom datotekom koju je već prenio.

Klijenti mogu paralelno prenositi svoje paketne datoteke, ali čak i slanja na nepovezane grane zahtijevaju ažuriranje istog manifesta. Vrijeme razmjene zahtjeva i odgovora za uspješnu operaciju uporedi-i-zamijeni ograničava koliko često repozitorij može objaviti novi manifest. Spremište blobova s niskom latencijom ubrzava taj korak. Grupisanje omogućava da jedan manifest istovremeno ažurira više referenci, ali uz cijenu čekanja ažuriranja u redu i povećane latencije.

Spremište blobova može paralelno primati paketne datoteke, ali ažuriranja zajedničkog manifesta i dalje se međusobno nadmeću. 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 isporučivati. Ako dolazni upisi prestignu taj rad, replike zaostaju i klijenti duže čekaju na dohvat upravo poslanih commita.

Projektovanje arhitekture sistema Pando

To nas dovodi do sistema Pando. Pando je nova arhitektura za opsluživanje Git repozitorija, osmišljena da se skalira za veliki broj klijenata koji istovremeno šalju podatke. Evo kako radi:

Preispitivanje ograničenja

Prije razvoja sistema Pando pretpostavljali smo da možemo ispuniti svoje zahtjeve bez kompromisa u pogledu Gita, ali smo morali popustiti kod nekih stvari koje većina Git sistema smatra obaveznim:

  • Konzistentnost na nivou cijelog repozitorija: Da bi se osigurao konzistentan snimak svih referenci u određenom trenutku, potrebna je koordinacija na nivou cijelog repozitorija. U sistemu koji određuje zajednički redoslijed svih ažuriranja referenci, slanja na nepovezane grane nadmeću se za istu tačku koordinacije.

  • Jedinstvene paketne datoteke za svakog klijenta: Ako dva klijenta s različitim lokalnim historijama dohvaćaju istu granu, server će možda morati generisati poseban paket za svakog kako ne bi slao objekte koje već imaju. Generisanje tih odgovora troši procesorske resurse i zahtijeva ulazno-izlazne operacije na disku, što otežava skaliranje za veliki broj klijenata koji čitaju podatke.

Umjesto toga, Pando koristi blaži model konzistentnosti (kauzalnu konzistentnost) za skaliranje upisa, a ponovo koristi paketne datoteke za skaliranje čitanja.

Kauzalna konzistentnost (CC)

Kada klijent šalje podatke na granu, Pando pohranjuje dolaznu paketnu datoteku kao nepromjenjivi blob u spremište blobova. Svaki put kada se grana pomjeri, Pando u spremište metapodataka zapisuje verziju reference koja sadrži novi vrh grane, paket iz kojeg potječe i sve ranije verzije referenci od kojih taj paket zavisi. I spremište blobova i spremište metapodataka mogu se horizontalno skalirati.

Dohvat commita ili reference iz sistema Pando zahtijeva da se klijentu redom isporuči skup verzija referenci (paketnih datoteka), čime se rekonstruira stanje klijenta koji je kreirao verziju reference koja se dohvaća. Time se osigurava slabiji oblik konzistentnosti na nivou cijelog repozitorija, uz serijalizabilnost ažuriranja pojedinačnih referenci. Klijenti dolaze u sukob samo kada istovremeno šalju podatke na istu referencu. Klijent koji čita više grana ne može pretpostaviti da ta čitanja predstavljaju stanje cijelog repozitorija u istom trenutku.

Lijevo su tri uzastopna paketa glavne grane s commitima A, B i C; D i E; te F. Desno se glavna i tematska grana razdvajaju kod C. Glavna grana napreduje do G, dok tematska grana napreduje kroz D, E i F. Najnoviji paket tematske grane sadrži D, E i F. Isprekidani okvir označava da su D i E prisutni i u ranijem paketu tematske grane. Plava strelica označava zavisnost novog paketa od glavne grane na G. Roditeljski commit za F u Gitu je E.

Slanje na glavnu granu dodaje inkrementalni paket koji sadrži jedan ili više commita. Svako slanje na sporednu granu ponovo uključuje ranije commite te grane, pa klijent s oglašenom verzijom glavne grane može dohvatiti sporednu granu u jednom paketu.

To je oblik kauzalne konzistentnosti. Da bi dohvatio referencu, klijent koji čita podatke vidi historiju klijenta koji ju je kreirao. Tokom slanja Pando kontroliše koju historiju klijent i server smatraju zajedničkom, čime osigurava da klijenti stvaraju paketne datoteke koje je lakše dijeliti.

Zbog toga Pando tokom Git operacija ne pruža stanje svih referenci u jednom trenutku niti garantuje redoslijed ažuriranja referenci unutar kloniranja ili dohvata. Konkretno, pretpostavimo da se grane foo i bar ažuriraju tokom pregovaranja o potpunom kloniranju. Tokom pregovaranja jedan klijent može vidjeti foo u verziji 1 i bar u verziji 2, dok drugi vidi foo u verziji 2 i bar u verziji 1. U praksi korisnici i agenti ne zavise od globalnog redoslijeda ažuriranja referenci, već od redoslijeda ažuriranja pojedinačne reference.

Poređenje pristupa

U ovoj simulaciji tri modela konzistentnosti postavili smo 1.000 klijenata tako da svaki odabere zasebnu granu, a zatim uzastopno šalje jedinstvene commite. Mjerimo ukupno proteklo vrijeme i latenciju od slanja do dohvata. Potonja metrika važna je za radna opterećenja jer mjeri vrijeme punog ciklusa potrebno korisnicima i agentima za saradnju ili korištenje drugih sistema koji se oslanjaju na kontrolu verzija. Simulacija modelira hardver u rasponu od standardnih do vrhunskih virtuelnih mašina i sistema za pohranu blobova. U ovoj simulaciji ublažili smo zahtjev za konzistentnošću cijelog repozitorija kod trofazne potvrde transakcije.

Simulating Git Server Implementations

Follow each writer through repeated pushes, and separately measure when an independent reader can check out each commit.

Three-phase commit

Ready
Simulated time
0 s
Mean push to fetch
—
Writers done
0 / 1,000

Each square represents one writer. All selected architectures run the same workload. Squares lighten as pushes are acknowledged and turn blue when a writer finishes. The chart shows writers finished over simulated time; independent readers can continue fetching afterward. Use arrow keys to select a writer, Home or End to jump, and Escape to clear.

0 / 10,000 pushes acknowledged · 0 fetched

Select a square to inspect that writer.

Object-store WAL

Ready
Simulated time
0 s
Mean push to fetch
—
Writers done
0 / 1,000

Each square represents one writer. All selected architectures run the same workload. Squares lighten as pushes are acknowledged and turn blue when a writer finishes. The chart shows writers finished over simulated time; independent readers can continue fetching afterward. Use arrow keys to select a writer, Home or End to jump, and Escape to clear.

0 / 10,000 pushes acknowledged · 0 fetched

Select a square to inspect that writer.

Pando

Ready
Simulated time
0 s
Mean push to fetch
—
Writers done
0 / 1,000

Each square represents one writer. All selected architectures run the same workload. Squares lighten as pushes are acknowledged and turn blue when a writer finishes. The chart shows writers finished over simulated time; independent readers can continue fetching afterward. Use arrow keys to select a writer, Home or End to jump, and Escape to clear.

0 / 10,000 pushes acknowledged · 0 fetched

Select a square to inspect that writer.

Writer progress through verified fetches

  • Three-phase commit
  • Object-store WAL
  • Pando
05001,00000 s
  • Waiting
  • Pushing
  • All pushes acknowledged
0 sLater

Synthetic teaching models, not production benchmarks. Mean push to fetch covers topic pushes, from the first attempt through retries until an independent reader verifies that commit. It includes waiting for a reader slot and excludes builds and tests. With merges enabled, writer completion includes the required fetches and trunk merge.

Experiment settings

Preparing simulation…

Results

Play the comparison to see the recorded results.

Synthetic teaching models, not production benchmarks. Mean push to fetch covers topic pushes, from the first attempt through retries until an independent reader verifies that commit. It includes waiting for a reader slot and excludes builds and tests. With merges enabled, writer completion includes the required fetches and trunk merge.

Pokrenite ovu simulaciju koja poredi Pando s još dva pristupa. Odaberite infrastrukturu od standardnog do vrhunskog hardvera, broj klijenata i druge postavke da biste vidjeli razlike.

Modelirani sistem s trofaznom potvrdom transakcije pokazuje znatno veću latenciju upisa pod opterećenjem jer diskovi dostižu granicu kapaciteta; sistemi s dnevnikom prethodnog upisa iz istog razloga pokazuju znatno veću latenciju čitanja, jer serveri ne uspijevaju dovoljno brzo čitati dnevnik. I u stvarnim testovima na našem monorepozitoriju i u ovoj simulaciji, Pando podržava više hiljada klijenata koji mogu istovremeno slati podatke bez pada performansi pod opterećenjem.

Pri obradi slanja Pando provjerava dolaznu paketnu datoteku dok je strimuje u spremište blobova, bez raspakivanja u lokalni repozitorij. Kada klijent pokrene dohvat, Pando odabire pohranjene pakete koji su mu potrebni, a klijent ih preuzima. Nijedan server ne mora primjenjivati paketne datoteke na lokalni repozitorij niti obavljati skupe operacije raspakivanja i pakovanja. Budući da prvenstveno obavljaju pregovaranje i odabir paketnih datoteka, Pando serveri mogu raditi na standardnom hardveru.

To omogućava sistemu Pando da se skalira do granica kapaciteta klijenata, mreže između klijenata i sistema Pando te usluge za pohranu blobova. Uz dovoljan kapacitet, svaki klijent koji radi na nezavisnoj grani može prenositi pakete najvećom brzinom koju dopušta njegova veza. Da bismo opslužili više klijenata tom brzinom, možemo dodavati Pando servere i zajedno s njima skalirati 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.

Stream the push

  • Packfile bytes
  • Metadata
  • Request / acknowledgment
  • Packfile URIs

Agent uređuje datoteke, lokalno kreira commit i pokreće git push, čime se stvara paketna datoteka s tim izmjenama radi ažuriranja grane. Pando prima paket i strimuje ga u spremište blobova. Nakon provjere dolaznog slanja dekodiranjem u jednom prolazu, Pando koristi transakciju nad više ključeva da zabilježi verziju reference u našem spremištu metapodataka i atomski ažurira pokazivač reference. Time Pando osigurava konzistentnost tipa „čitanje vlastitih upisa”. Cijela ova operacija upisa zahtijeva O(1)O(1) čitanja i upisa metapodataka.

Kada drugi klijent dohvaća granu, Pando odabire skup pohranjenih paketa na osnovu historije koju klijent već ima. Ova operacija zahtijeva O(log⁡Δ)O(\log \Delta) čitanja metapodataka, pri čemu je Δ\Delta najkraća udaljenost, izražena brojem verzija referenci, između onoga što klijent ima i onoga što traži. Server vraća URI-je paketnih datoteka, a klijent ih preuzima putem usluge Express, koja pohranjuje paketne datoteke u regionalne keševe.

Ovaj dizajn oslanja se na tri ključne usluge arhitekture sistema Pando: slanje, dohvat i kompakciju.

Skaliranje slanja

Kada klijent šalje podatke, konvencionalni Git server ponaša se poput igrača pokera koji pokazuje sve karte: otkriva klijentu svaku granu koju ima. Klijent zatim prikuplja commite, stabla i blobove kako bi kreirao paketnu datoteku koja izostavlja objekte za koje zna da ih server već ima.

Oglašavanje više referenci može smanjiti količinu podataka koja se šalje. Ali može i otežati ponovnu upotrebu paketa jer drugom klijentu mogu nedostajati objekti koje je pošiljalac izostavio.

Pando koristi drugačiji pristup. Umjesto da otkrije sve karte, klijentu oglašava samo odabrani skup grana. Za to postoje dva razloga:

  1. Pružanje konzistentnog prikaza svih referenci je skupo. Uz milione grana, samo oglas referenci može premašiti 100 MiB. Za to svaki server mora ili održavati ažurno preslikavanje, uz značajan trošak koordinacije, ili izvršavati upit nad bazom podataka čija složenost raste s brojem referenci.

  2. Ako klijentu pruži samo podskup referenci, klijent će generisati paket koji zavisi isključivo od onoga što mu server izričito dopušta da izostavi. Praćenje zavisnosti omogućava sistemu Pando da paketnu datoteku koju klijent generiše pri jednom slanju ponovo upotrijebi kao odgovor na kasniji dohvat.

Što više referenci Git server oglašava, to klijenti koji šalju podatke imaju više izvora na osnovu kojih mogu izostaviti objekte i tako proizvesti manje pakete. Međutim, za ponovnu upotrebu tih paketnih datoteka server mora moći dokazati da klijenti koji ih dohvaćaju imaju sve potrebne zavisnosti. U suprotnom, paketna datoteka može izostaviti objekte koji su klijentu potrebni.

S druge strane, ako Git server ne oglašava nijednu referencu, klijent bi generisao paketnu datoteku s cijelom historijom repozitorija. Takvu paketnu datoteku može koristiti bilo koji drugi klijent, ali uz značajan trošak: dohvat bilo koje grane praktično bi postao potpuno kloniranje.

Kriva pokazuje kako oglašene reference utječu na veličinu paketne datoteke: od cijele historije kada nema oglašenih referenci, preko oglašavanja glavne grane, do samo novih objekata kada su oglašene sve reference.

Oglašavanje više referenci rezultira manjim paketnim datotekama. Veći dio smanjenja veličine može se postići oglašavanjem samo jedne reference.

Prema zadanim postavkama, kada klijent šalje novu granu za razvoj funkcionalnosti, Pando kao osnovu oglašava samo određenu glavnu granu. Tako veličina oglasa ostaje ista dok broj grana u repozitoriju raste, ali to zahtijeva određene kompromise. Kada inženjer više puta šalje podatke na istu granu ili nadoveže jednu razvojnu granu na drugu, klijent mora ponovo poslati sve što nije u glavnoj grani.

Zato uzastopna slanja na granu postaju veća, mogu sadržavati suvišne objekte i dodatno troše mrežnu propusnost klijenta. U našem monorepozitoriju medijan veličine tako proizvedenih paketnih datoteka bio je ispod 20 KiB, a 99. percentil ispod 3 MiB. Za nas je to bio razuman kompromis jer klijent koji već ima historiju oglašene glavne grane može dohvatiti granu pomoću te jedne paketne datoteke.

Nakon odabira referenci koje će oglasiti klijentu, Pando prihvata slanje i ažurira spremište metapodataka. Pando dijeli ažuriranje metapodataka na sinhroni i asinhroni dio. Sinhrono ažuriranje metapodataka osigurava da klijenti mogu odmah dohvatiti upravo ažurirani commit. Time se zapisuje nova verzija reference i ažurira pokazivač tako da upućuje na nju, uz pohranu novog vrha grane, zavisnosti korištenih pri pregovaranju o paketnoj datoteci i veze do poslane paketne datoteke.

Istovremeno se u istoj transakciji bilježe i asinhroni zadaci, poput zakazivanja kompakcije. Kod vrlo velikih slanja samo se podskup commita zapisuje sinhrono. Tako je potreban samo konstantan broj čitanja i upisa u spremište metapodataka, bez obzira na veličinu slanja.

Važno je da Pando serveri mogu nezavisno prihvatati slanja na nepovezane reference. Jedini ključ za koji može doći do nadmetanja jeste pokazivač reference, koji štitimo atomskom operacijom nalik operaciji uporedi-i-zamijeni. Protok pri upisu možemo povećati dodavanjem Pando servera, jedinica za skaliranje spremišta metapodataka ili spremišta blobova.

Dohvat i kloniranje punom brzinom mrežne veze

Kada klijent obavlja dohvat, serveru šalje dva skupa ID-jeva commita, u protokolu poznata kao have i want. Konvencionalni Git server računa razliku između skupova objekata dosegljivih iz postojećih i traženih commita, komprimira potrebne objekte i šalje rezultat klijentu. Iako Git server otvorenog koda lokalno koristi razne indekse za ubrzavanje te operacije, generisanje ovih paketnih datoteka može biti skupo, a uglavnom ih je teško efikasno keširati.

Umjesto toga, Pando koristi preslikavanja commita zabilježena pri slanju da pronađe klijentove stavke have i want te ih poveže sa skupom verzija referenci koje sadrže te commite. Svaka od tih verzija referenci povezana je s paketnom datotekom koju je Pando ranije primio. Server na osnovu toga određuje šta 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.

Already heldFetchMissing branch dependency

Fetch 27 trunk packs.

Select a branch to include its trunk dependencies.

Pando preslikava postojeće i tražene commite iz Git protokola u niz verzija referenci. Zahtjev za sporednom granom podrazumijeva i zahtjev za historijom glavne grane do tačaka od kojih sporedna grana zavisi.

Git protokol dopušta slanje samo jedne paketne datoteke u HTTP odgovoru pri kloniranju ili dohvatu. Međutim, nedovoljno cijenjena mogućnost Git protokola, packfile-uri, omogućava serveru da vrati linkove za zasebno preuzimanje dodatnih paketa. Pando koristi isključivo tu mogućnost, a unutar samog odgovora šalje praznu paketnu datoteku. Kako bi podržao tu mogućnost, OpenAI je izvornom projektu doprinio nizom poboljšanja za packfile-uri.

Sistemi kompanije OpenAI rade u mnogim geografskim regijama, pa u svakoj održavamo Pando klastere s lokalnim keševima. Usluga za isporuku paketa Express kešira naše spremište blobova, dok zasebna usluga kešira spremište metapodataka. Ti keševi omogućavaju sistemu Pando da zahtjeve za ID commita ili posebno čitanje reference u potpunosti opsluži iz keširanog stanja u tom klasteru. Koristimo konzistentno hashiranje za usmjeravanje zahtjeva za paketne datoteke na Express servere, uz stopu pogodaka u kešu veću od 99%.

Ovaj sistem keševa pruža izuzetno visok protok, dovoljan da u potpunosti iskoristi mrežni kapacitet između klijenata i sistema Pando. Potvrdili smo da uz ove URI-je paketnih datoteka i lokalne keševe, koristeći samo standardne virtuelne mašine pružalaca usluga u oblaku, operacije kloniranja i dohvata u potpunosti iskorištavaju mrežne veze od 10 Gbps.

Kompakcija i vrijeme do prvog bajta

Na većini Git servera ponovno pakovanje i uklanjanje nekorištenih objekata neophodni su za održavanje performansi repozitorija koji raste. Oba postupka oslobađaju prostor, ali zahtijevaju mnogo računanja i ulazno-izlaznih operacija.

U repozitoriju koji se brzo mijenja, poput monorepozitorija kompanije OpenAI, nije izvodivo slati zaseban URI paketne datoteke za svako slanje od posljednjeg dohvata. Klijent bi svakog dana morao preuzimati desetine hiljada paketnih datoteka da bi ažurirao svoju glavnu granu.

Umjesto toga, Pando u pozadini objedinjuje pakete radi smanjenja tog dodatnog opterećenja, koristeći dizajn sličan kompakciji u stablima spajanja strukturiranim kao dnevnik (LSM). Svako slanje proizvodi ono što Pando naziva paketnom datotekom „L0”, najmanjom jedinicom podataka koju Pando može prenijeti. S vremenom pozadinski procesi grupišu te paketne datoteke u skupove koji pokrivaju niz verzija referenci, a čija veličina raste geometrijski počevši od nivoa L0. Danas naš monorepozitorij koristi takve skupove paketnih datoteka do nivoa L4.

Ti skupovi imaju dvostruku svrhu. Kada se skup dovoljno popuni, proces za kompakciju preuzima ga i proizvodi jednu paketnu datoteku. To poboljšava omjer kompresije reorganizacijom objekata unutar paketne datoteke. Ujedno poboljšava performanse pregovaranja pri dohvatu jer djeluje kao lista s preskakanjem nad verzijama referenci. Istovremenim pregledanjem indeksa na svakom nivou, Pando može u logaritamskom vremenu odabrati skup paketa za slanje klijentu. Ti skupovi paketnih datoteka, prepakovani ili ne, omogućavaju efikasnije planiranje dohvata i smanjuju zahtjeve u pogledu protoka i latencije za Pando.

Compaction in Pando

Small packfiles are grouped into larger covers across levels, improving compression and speeding up fetches.

Compaction in progressPublished cover

Kompakcija u pozadini objedinjuje paketne datoteke iz mnogih verzija referenci, omogućavajući efikasniji dohvat uz manje datoteka.

Za razliku od mnogih Git servera, paketne datoteke zadržavaju se na svakom nivou i ne odbacuju se niti uklanjaju kao nekorištene. Paketi na svim nivoima stalno se koriste za opsluživanje klijenata, na osnovu stavki have i want koje navode pri dohvatu. Za klijente koji koriste Pando kao API, čitanje proizvoljnog objekta iz paketne datoteke obično zahtijeva čitanje manje podataka iz L0 nego iz L4, jer je manja vjerovatnoća da je objekt komprimiran.

Kompakcija u sistemu Pando znatno smanjuje ukupnu količinu podataka koja se prenosi klijentima i omogućava da složenost planiranja paketa raste sublinearno u odnosu na to koliko klijent zaostaje. To je posebno važno za velike repozitorije koji se brzo mijenjaju, za koje očekujemo da će postati uobičajeni u razvoju softvera.

Izgradnja sistema Pando

Izgradili smo Pando uz Codex, od početka projekta do produkcije, za samo 10 sedmica. Jezgro sistema Pando osmislio je jedan inženjer, implementirali su ga timovi agenata, a u produkciju uvela nekolicina inženjera. Pando je počeo opsluživati produkcijski saobraćaj u aprilu ove godine i dio je šireg sistema alata za upravljanje izvornim kodom koji se koriste u kompaniji OpenAI.

Pri provjeri dizajna sistema Pando koristili smo širok raspon pristupa. Primijenili smo svoje principe za izgradnju sigurnijih softverskih temelja⁠. Budući da je riječ o projektu koji se razvija od nule, odlučili smo implementirati Pando u Rustu, bez naslijeđenih zavisnosti. Napisali smo skup testova usklađenosti kako bi agenti mogli provjeravati svoje izmjene u odnosu na standardno ponašanje Gita. Razvili smo i okvire za determinističko simulacijsko testiranje kako bismo provjeravali invarijante i svojstva sistema te pronalazili i ublažavali suptilne probleme pri istovremenom izvršavanju i regresije u pregovaranju i planiranju paketa.

Mnogi modeli doprinijeli su ovom sistemu; kako su novi modeli postajali dostupni, koristili smo ih za poboljšanje performansi i pouzdanosti sistema Pando. Koristeći GPT‑6 Astra, agenti su pisali kod u jezicima Lean i TLA+ kako bi formalizirali „teoriju sistema Pando”, koju smo koristili za pronalaženje i ispravljanje grešaka u implementaciji te dokazivanje svojstava sistema. Više neprekinutih sesija trajalo je stotinama sati, tokom kojih su se pronalazili i rješavali rubni slučajevi u sistemu Pando. Također neprekidno pokrećemo agente koji, koristeći modele sa sposobnostima Daybreak, provode sigurnosna testiranja Gita i sistema Pando simuliranjem napada.

Uvođenje sistema Pando

Pando smo prvobitno uveli kao posrednički server koji prosljeđuje čitanja i upise našem postojećem sistemu za kontrolu izvornog koda. To nam je omogućilo da održavamo Pando i prethodni sistem sinhroniziranim te sigurno preusmjeravamo saobraćaj na Pando bez prekida rada.

U sklopu svog mehanizma keširanja, Pando može i uklanjati duplirane zahtjeve i objedinjavati ih prije prosljeđivanja. Tako smo održavali postojeće alate stabilnim dok smo sisteme jedan po jedan prebacivali na Pando. Kako smo sve više radnih opterećenja prebacivali na Pando, njihova slanja punila su njegov keš i poboljšavala pouzdanost prethodnog pozadinskog sistema.

Da bismo optimizirali performanse pri korištenju sistema Pando, počeli smo isporučivati našu javnu distribuciju Gita na računare programera i unutar desktop aplikacije ChatGPT. Za postepeni prelazak klijenata na Pando koristili smo interni omotač oko Gitovog interfejsa komandne linije. Kreirali smo i mnoge druge alate za obradu zahtjeva za izmjene, pregled koda te API-je za automatizaciju. Uz Git saobraćaj, migrirali smo stotine integracija koje su zavisile od naslijeđenih API-ja, webhookova i preuzimanja repozitorija.

Sam Codex pomagao je programerima i timovima pri prelasku. Kreirali smo vještine za pripremu postojećih radnih kopija i usmjeravanje agenata na korištenje novog sistema. Zahvaljujući tome, programeri su mogli jednostavno zatražiti od Codexa da migrira njihova razvojna okruženja i usluge.

Prelazak na Pando poboljšao je pouzdanost spajanja grana i smanjio medijan latencije slanja za gotovo 80%.

Pando može isporučiti petabajt Git podataka dnevno sa svega 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 lokalnog diskovnog keša.

Ponovna upotreba paketa omogućava mnogim klijentima da preuzimaju istu historiju repozitorija uz malo računanja na čvorovima koji je isporučuju. Ovaj primjer prikazuje jedan repozitorij, ali Pando je osmišljen da podrži mnogo više njih. Kako potražnja raste, možemo nezavisno skalirati isporuku podataka i Git pregovaranje.

Doprinos projektu Git

Izgradnja sistema Pando na Git protokolu i klijentu pomogla nam je da otkrijemo greške i prilike za poboljšanje performansi samog Gita.

Naprimjer, Pando isporučuje višekratno upotrebljive pakete direktno klijentima, pa su njihovo preuzimanje i indeksiranje postali ključni za brzinu i pouzdanost sistema. Taj rad otkrio je rubne slučajeve pri istovremenim preuzimanjima, čišćenju nakon prekinutih prijenosa i nastavku preuzimanja koja su već bila završena. Ispravili smo te tokove izvršavanja, dodali provjere pri prihvatanju postojećeg paketa za dohvat i prenijeli ta poboljšanja u izvorni projekat.

Morali smo vidjeti i na šta se troše vrijeme i mrežna propusnost, pa smo dodali praćenje preuzimanja putem packfile-uri i brojanje bajtova pri generisanju paketa. Tako je lakše utvrditi troši li spor dohvat vrijeme na generisanje paketa ili na njihov prijenos.

U OpenAI izdanju Gita⁠(otvara se u novom prozoru) klijent može paralelno preuzimati i indeksirati nezavisne pakete. Dodali smo koncept „izričito navedenih postojećih commita”, tako da server može oglašavati trenutni vrh grane odvojeno od historije koju klijent pri slanju smije izostaviti iz paketa. U sistemu Pando to omogućava klijentima da koriste --force-with-lease kako bi provjerili da se razvojna grana nije pomjerila i izgradili njen paket samo u odnosu na glavnu granu. Svako ko ima tu historiju glavne grane zatim može dohvatiti granu koristeći isti paket.

Nekoliko tih poboljšanja već je uključeno u izvorni Git, a mnoga druga nalaze se u našem javnom repozitoriju. Nastavljamo sarađivati s Git zajednicom kako bi ova poboljšanja bila korisna i izvan sistema Pando. Taj rad možete pratiti u našem javnom sistemu za praćenje zakrpa⁠(otvara se u novom prozoru).

Šta slijedi

Preispitivanjem ustaljenih pristupa skaliranju Git repozitorija, Pando nam omogućava da skaliramo monorepozitorij bez mijenjanja Git tokova rada koje naši inženjeri i agenti već koriste. U našem monorepozitoriju Pando je omogućio veći stepen paralelnog rada, poboljšao pouzdanost spajanja grana i skratio vrijeme potrebno za objavu izmjena koda. Osim u monorepozitoriju, istražujemo i druge primjene sistema Pando unutar kompanije OpenAI, uključujući radna opterećenja u kojima veliki broj agenata treba sarađivati na zajedničkom Git repozitoriju.

Kako inženjeri i agenti budu stvarali više softvera, sve više timova izvan kompanije OpenAI suočavat će se s ograničenjima kontrole izvornog koda na koja smo mi naišli. Trenutno istražujemo načine da tehnologiju na kojoj se Pando zasniva podijelimo s našim korisnicima.

Pando se oslanja na dvije decenije iskustva korisnika i administratora Git repozitorija. Nastavit ćemo razvijati Pando i sarađivati sa zajednicom otvorenog koda okupljenom oko Gita kako bismo dijelili poboljšanja koja koriste široj zajednici Git korisnika.

Autor

OpenAI