OpenAI

2. november 2026

Tehnologija

Skaliranje Gita v OpenAI

Razvoj nadzora izvorne kode za obseg dela z agenti ob hkratnem vlaganju v ekosistem Git.

Avtorji: Aaron Friel, Taylor Blau in Ted Nyman, člani tehnične ekipe

Nalaganje …

V zadnjem letu sta se velikost našega monorepozitorija in zahtevnost njegove uporabe povečevali z osupljivo hitrostjo. Jasno je bilo, da moramo prenoviti sistem za nadzor izvorne kode, da bo kos obsegu sočasnega dela, ki ga omogoča Codex.

Razvili smo Pando, novo zaledje za repozitorije Git, ne da bi prekinili razvojno delo ali spremenili način, kako naša ekipa uporablja Git. Pando zdaj podpira ves razvoj v monorepozitoriju OpenAI. Pando je zasnovan za doseganje dveh ambicioznih ciljev: prvi je horizontalno skaliranje pisanja, optimizirano za sočasne potiske v številne veje. Drugi pa je čim večja prepustnost branja, da lahko odjemalci prenašajo podatke dovolj hitro, da v celoti izkoristijo razpoložljivo omrežno pasovno širino.

Ta cilja smo dosegli ob ohranitvi združljivosti s protokolom in odjemalcem Git, kar je našim inženirjem in agentom omogočilo nemoteno vključitev sistema Pando v obstoječe delovne postopke. V produkciji je Pando zmanjšal mediano zakasnitve potiskov na skoraj petino prejšnje vrednosti. Zdaj dnevno dostavi skoraj petabajt podatkov Git.

V nadaljevanju predstavljamo razloge za razvoj novega sistema za nadzor izvorne kode in pojasnjujemo, zakaj smo ohranili združljivost z Gitom. Naš sistem bomo primerjali z običajnimi pristopi k skaliranju Gita in pojasnili, zakaj prav Pando lahko izpolni potrebe OpenAI po skalabilnosti. Predstavili bomo tudi naslednje korake in pojasnili, kako bomo še naprej prispevali k razvoju Gita, da bo podpiral obseg dela, ki ga prinašajo agenti.

Zakaj smo potrebovali nov sistem za nadzor izvorne kode

Codex je spremenil obseg in tempo razvoja v OpenAI ter inženirjem omogočil veliko več vzporednega dela. To pomeni več vej in potiskov, več združevanj ter več prevzemov in kloniranj iz razvojnih okolij, raziskovalnih opravil in sistemov za neprekinjeno integracijo (CI).

Te zahteve se medsebojno stopnjujejo. Posamezen inženir ima zdaj za CI hkrati pripravljenih več sprememb, vsak potisk pa lahko sproži več opravil, ki prevzemajo isto potrditev.

Približno ob koncu lanskega leta so sistemi, ki podpirajo naš monorepozitorij, začeli težko dohajati ta tempo. Soočali smo se s počasnimi prevzemi in potiski ter neuspešnimi združitvami, zanesljivost pa je včasih padla pod 90 %. Smo dosegli meje samega Gita ali načina, kako strežniško podpiramo naš monorepozitorij? Git je zrasel iz skupnosti, ki si je popravke jedra izmenjevala po e-pošti. Razvijalci že več kot dve desetletji prilagajajo svoja orodja Gitu, medtem ko se način razvoja programske opreme spreminja. Ko smo v svojem monorepozitoriju začeli opažati težave, nam je bilo pomembno, da lahko še naprej gradimo na tem delu.

Videli smo možnost, da ohranimo znani delovni postopek, zaledje pa prenovimo tako, da vzporedno obdeluje potiske v različne veje in isto zgodovino sočasno posreduje številnim bralcem. Ker je Git ostal temelj našega delovnega postopka za nadzor izvorne kode, bi lahko izboljšave odjemalca koristile tudi zunaj OpenAI. Te izboljšave iz naše kopije Gita⁠(odpre se v novem oknu) smo želeli prispevati v izvorni projekt in jih tako deliti s širšo skupnostjo.

Zasnova Gita za velik obseg uporabe

Skaliranje sistema za nadzor izvorne kode ima dve ključni razsežnosti: veliko piscev, ki potiskajo v različne veje, in veliko bralcev, ki prevzemajo isto zgodovino. Pando je optimiziran za oboje.

Prvič, Pando usklajuje posodobitve vsake veje neodvisno. Posodobitve nepovezanih vej lahko potekajo vzporedno, bralec, ki prevzema vejo, pa dobi zgodovino, potrebno za to revizijo. Kompromis je v tem, da lahko bralec, ki pregleduje več vej, vidi njihova stanja iz različnih trenutkov.

Drugič, Pando pri prevzemih ponovno uporablja paketne datoteke (Gitovo obliko za skupno stiskanje več objektov), namesto da za vsakega odjemalca ustvari ločeno paketno datoteko. Odjemalec lahko prenese nekaj objektov, ki jih že ima, vendar lahko strežnik iste bajte z malo računanja posreduje številnim odjemalcem.

Ti odločitvi nam omogočata skaliranje pisanja med vejami in streženje bralnih zahtev iz predpomnilnikov. Da bi razumeli, kako se dopolnjujeta, si najprej oglejmo, kako Git shranjuje in prenaša zgodovino.

Osnove shranjevanja objektov v Gitu

Git mora ohranjati usklajenost dveh delov repozitorija: objektov in referenc.

Repozitorij Git beleži posnetke datotek in zgodovino, ki jih povezuje, v obliki grafa objektov. Objekt je lahko blob, drevo, potrditev ali oznaka. Blobi hranijo vsebino posamezne datoteke, drevesa pa povezujejo imena z blobi ali drugimi drevesi in tako tvorijo strukturo imenikov. Potrditve kažejo na korensko drevo in nič ali več nadrejenih potrditev ter tako tvorijo zgodovino repozitorija.

Objekte določa zgoščena vrednost njihove vrste in vsebine. Objekti so tudi nespremenljivi: zapis spremenjene datoteke ustvari nov blob, nespremenjene datoteke in imeniki pa lahko znova uporabijo obstoječe objekte.

Veje in oznake so reference, ki kažejo na potrditve. Potrditev sprememb v veji ustvari novo potrditev in posodobi referenco veje, da kaže nanjo.

Veja refs/heads/main kaže na potrditev C2, katere nadrejena potrditev je C1. C1 kaže na korensko drevo T1, v katerem se parser.py sklicuje na blob B1. C2 kaže na korensko drevo T2, v katerem se parser.py sklicuje na blob B2. V obeh korenskih drevesih se docs/ sklicuje na isto drevo, T3. Njegov vnos README.md kaže na blob B3, usage.md pa na blob B4.

Objekti Git se med seboj sklicujejo prek ID-jev: potrditve na drevesa, drevesa pa na druga drevesa in blobe. Veja refs/heads/main kaže na potrditev C2, katere nadrejena potrditev je C1. C1 kaže na korensko drevo T1, v katerem se parser.py sklicuje na blob B1. C2 kaže na korensko drevo T2, v katerem se parser.py sklicuje na blob B2. V obeh korenskih drevesih se docs/ sklicuje na isto drevo, T3. Njegov vnos README.md kaže na blob B3, usage.md pa na blob B4.

Git shranjuje objekte v »posamični« ali »zapakirani« obliki. Posamični objekti so shranjeni v ločenih datotekah, ki jih določa ID objekta. Zapakirani objekti so shranjeni skupaj v paketni datoteki, kar omogoča, da se podobni objekti shranijo kot razlike glede na druge objekte.

Pod $GIT_DIR/objects sta prikazani dve različici datoteke parser.py. V posamični obliki blob A vsebuje parse(), blob B pa parse() in parse_file(). Paketna datoteka hrani A v celoti, B pa predstavi kot navodila za kopiranje celotnega A in dodajanje parse_file(). Spremljajoča datoteka *.idx določa položaj obeh blobov v paketni datoteki glede na ID objekta.

Git lahko blobe shrani kot ločene datoteke ali jih zapakira skupaj, pri čemer en blob kodira kot razliko glede na drugega.

V Gitu je objekt dosegljiv iz potrditve, če ga Git lahko najde tako, da od te potrditve sledi povezavam prek njenih nadrejenih potrditev, dreves in blobov. Med prevzemom odjemalec strežniku sporoči, katere potrditve želi in katere že ima. Strežnik poišče objekte, dosegljive iz želenih potrditev, izloči tiste, za katere ve, da jih odjemalec že ima, in preostale pošlje v paketni datoteki v isti obliki, kot jo Git uporablja na disku. Popolno kloniranje se začne brez objektov, zato potrebuje vse objekte, dosegljive iz zgodovine, ki se klonira.

Potisk prenaša objekte v nasprotni smeri, tako da strežnik zaprosi za posodobitev ene ali več referenc. Odjemalec uporabi nabor referenc, ki jih strežnik oglašuje, da se izogne pošiljanju objektov, ki jih strežnik že ima. Poslani paket je zato lahko odvisen od obstoječih objektov: samo prejem paketa ne zadostuje za obnovo nove zgodovine.

Repliciranje repozitorija Git pomeni zagotavljanje razpoložljivosti objektov in referenc na več strežnikih. Preden replika lahko posreduje novo vrednost reference, mora biti sposobna zagotoviti objekte, dosegljive iz te vrednosti. Storitev mora usklajevati tudi tekmujoče posodobitve referenc, torej potiske, ki poskušajo isto vejo premakniti z istega starega vrha na različne nove potrditve. Sistemi, ki vsilijo enoten vrstni red vseh zapisov v repozitorij, lahko povzročijo, da potiski v nepovezane veje čakajo drug na drugega.

Obstoječi vzorci porazdeljenih storitev Git

Pri snovanju sistema Pando smo ocenili običajne pristope k skaliranju nadzora izvorne kode z Gitom: pristop, ki posodobitve referenc med replikami repozitorija usklajuje s trifaznim potrjevanjem (3PC, protokol za usklajevanje potrditve transakcije med več vozlišči), in pristop, ki posodobitve razvršča prek skupnega dnevnika vnaprejšnjega pisanja (WAL, zapis sprememb pred njihovo uveljavitvijo) v shrambi blobov.

Trifazno potrjevanje (3PC)

V modelirani zasnovi s trifaznim potrjevanjem storitev Git hrani popolno kopijo repozitorija na več replikah. Ko kateri koli strežnik prejme potisk, se prejeta paketna datoteka kopira in vzporedno pošlje vsem replikam. Da se potisk objavi, mora večina replik sprejeti posodobitev reference. Strežnik, ki to upravlja, je koordinator, postopek pa zahteva več krogov komunikacije med njim in vsako drugo repliko.

Pred sprejemom posodobitve reference vsaka replika zaklene svojo kopijo reference in preveri, ali še vedno kaže na potrditev, ki jo odjemalec pričakuje. Koordinator nato replikam, ki posodobitev sprejmejo, pošlje sporočilo za predhodno potrditev. Ko večina potrdi prejem tega sporočila, jim koordinator naroči, naj izvedejo posodobitev. Vsaka ažurna replika lahko nato iz svoje lokalne kopije repozitorija oglašuje novi vrh veje.

Vzdrževanje popolnih replik omogoča lokalno streženje bralnim zahtevam in zagotavlja, da odjemalci dobijo konsistenten pogled na vse reference, ne glede na izbrano repliko. Zato se reference posodabljajo ena za drugo, vsak potisk pa zahteva več krogov izmenjave sporočil za uskladitev posodobitve reference med replikami.

Zaradi čakanja na sklepčnost sta hitrost potiska odvisna tudi od zmogljivosti diskov replik in komunikacijske zakasnitve. Ko obseg pisanja narašča, morajo replike dohajati tako prihajajoče pakete kot bralni promet.

Dnevnik vnaprejšnjega pisanja (WAL)

V modelirani zasnovi na osnovi WAL storitev Git uporablja skupni manifest v shrambi blobov za določanje vrstnega reda posodobitev v celotnem repozitoriju. Ko strežnik prejme potisk, prejeto paketno datoteko naloži v shrambo blobov in pripravi posodobitev reference v svoji lokalni kopiji repozitorija. Za objavo potiska v skupni manifest v shrambi blobov doda vnos, ki beleži posodobitev reference in kaže na paketno datoteko. Strežnik Git nato manifest posodobi z operacijo primerjaj in zamenjaj. Ta manifest atomarno zamenja ali pa spremembo zavrne. Če ga je medtem posodobil drug pisec, strežnik prebere vmesne vnose, preveri, ali je njegova posodobitev reference še vedno veljavna, in poskusi znova z že naloženo paketno datoteko.

Pisci lahko paketne datoteke nalagajo vzporedno, vendar tudi potiski v nepovezane veje zahtevajo posodobitev istega manifesta. Čas celotnega kroga uspešne operacije primerjaj in zamenjaj omejuje pogostost objave novega manifesta v repozitoriju. Shramba blobov z majhno zakasnitvijo ta korak pospeši. Paketna obdelava omogoča, da en manifest hkrati posodobi več referenc, vendar je treba posodobitve postaviti v čakalno vrsto, kar poveča zakasnitev.

Shramba blobov lahko paketne datoteke prejema vzporedno, posodobitve skupnega manifesta pa še vedno tekmujejo med seboj. Samo povečanje zmogljivosti shrambe blobov tega ozkega grla ne odpravi. Vsaka replika mora še vedno prebrati in indeksirati pakete iz shrambe blobov, preden jih lahko posreduje lokalno. Če novi zapisi prihajajo hitreje, kot poteka to delo, replike zaostajajo, odjemalci pa dlje čakajo na prevzem na novo potisnjenih potrditev.

Arhitekturna zasnova sistema Pando

S tem pridemo do sistema Pando. Pando je nova arhitektura za streženje repozitorijev Git, katere cilj je podpreti veliko odjemalcev, ki potiskajo sočasno. Deluje takole:

Ponovni razmislek o omejitvah

Pred razvojem sistema Pando smo slutili, da lahko izpolnimo svoje zahteve brez kompromisov pri uporabi Gita. Morali pa smo popustiti pri nekaterih stvareh, ki jih večina sistemov Git šteje za obvezne:

  • Konsistentnost celotnega repozitorija: Za konsistenten posnetek vseh referenc v določenem trenutku je potrebno usklajevanje na ravni celotnega repozitorija. V sistemu, ki vse posodobitve referenc razvršča v skupno zaporedje, potiski v nepovezane veje tekmujejo za dostop do ene same usklajevalne točke.

  • Ločene paketne datoteke za vsakega odjemalca: Če dva odjemalca z različnima lokalnima zgodovinama prevzameta isto vejo, mora strežnik morda za vsakega ustvariti drugačen paket, da jima ne pošilja objektov, ki jih že imata. Ustvarjanje teh odgovorov porablja procesorski čas in vhodno-izhodne zmogljivosti diska, zato je sistem težko razširiti za veliko število bralcev.

Pando namesto tega uporablja manj strog model konsistentnosti (vzročno konsistentnost) za skaliranje pisanja in ponovno uporablja paketne datoteke za skaliranje branja.

Vzročna konsistentnost (CC)

Ko odjemalec potisne spremembe v vejo, Pando prejeto paketno datoteko shrani kot nespremenljiv blob v shrambo blobov. Ob vsakem premiku veje Pando v shrambo metapodatkov zapiše različico reference, ki vsebuje novi vrh veje, paket, iz katerega izvira, in vse prejšnje različice referenc, od katerih je paket odvisen. Tako shramba blobov kot shramba metapodatkov omogočata horizontalno skaliranje.

Za prevzem potrditve ali reference iz sistema Pando je treba odjemalcu poslati nabor različic referenc (paketnih datotek), s katerimi se obnovi stanje odjemalca, ki je ustvaril prevzemano različico reference. To zagotavlja šibkejšo obliko konsistentnosti na ravni celotnega repozitorija, hkrati pa omogoča serializabilnost posodobitev posameznih referenc. Odjemalci pridejo v spor le, kadar hkrati potiskajo v isto referenco. Odjemalec, ki bere več vej, ne more predpostavljati, da ta branja predstavljajo stanje celotnega repozitorija v istem trenutku.

Na levi trije zaporedni paketi glavne veje vsebujejo potrditve A, B in C; D in E; ter F. Na desni se glavna in tematska veja razcepita pri C. Glavna veja napreduje do G, tematska veja pa prek D in E do F. Najnovejši paket tematske veje vsebuje D, E in F. Črtkani obris označuje, da sta D in E tudi v prejšnjem paketu tematske veje. Modra puščica označuje odvisnost novega paketa od glavne veje pri G. Nadrejena potrditev za F v Gitu je E.

Potisk v glavno vejo doda inkrementalni paket z eno ali več potrditvami. Vsak potisk v stransko vejo znova vključuje prejšnje potrditve tematske veje, tako da lahko odjemalec z oglaševano različico glavne veje tematsko vejo prevzame v enem paketu.

To je oblika vzročne konsistentnosti. Za prevzem reference bralec vidi zgodovino pisca, ki jo je ustvaril. Med potiskom Pando nadzoruje, o kateri skupni zgodovini se dogovorita odjemalec in strežnik, ter tako poskrbi, da odjemalci ustvarjajo paketne datoteke, ki jih je lažje deliti.

Zato Pando med operacijami Git ne zagotavlja stanja vseh referenc v istem trenutku ali skupnega vrstnega reda posodobitev referenc znotraj kloniranja ali prevzema. Denimo, da se veji foo in bar posodobita med pogajanjem za popolno kloniranje. Med pogajanjem lahko en odjemalec vidi foo v različici 1 in bar v različici 2, drugi pa foo v različici 2 in bar v različici 1. V praksi uporabniki in agenti niso odvisni od globalnega vrstnega reda posodobitev referenc, temveč od vrstnega reda posodobitev posamezne reference.

Primerjava pristopov

V tej simulaciji treh modelov konsistentnosti nastavimo 1.000 odjemalcev, da vsak izbere svojo vejo in nato večkrat potisne edinstvene potrditve. Pri tem merimo skupni pretečeni čas in zakasnitev od potiska do prevzema. Slednja je pomembna metrika delovnih obremenitev, saj meri čas celotnega kroga, ki ga uporabniki in agenti potrebujejo za sodelovanje ali uporabo drugih sistemov, odvisnih od nadzora različic. Simulacija modelira strojno opremo od običajnih do vrhunskih navideznih računalnikov in sistemov za shranjevanje blobov. V tej simulaciji smo pri trifaznem potrjevanju omilili zahtevo po konsistentnosti celotnega repozitorija.

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.

Zaženite to simulacijo, ki primerja Pando z dvema drugima pristopoma. Izberite infrastrukturo od običajne do vrhunske strojne opreme, število odjemalcev in druge nastavitve ter si oglejte primerjavo.

Modelirani sistem s trifaznim potrjevanjem ima pod obremenitvijo bistveno večjo zakasnitev pisanja, ker njegovi diski dosežejo mejo zmogljivosti. Sistemi z dnevnikom vnaprejšnjega pisanja pa imajo iz istega razloga bistveno večjo zakasnitev branja, saj strežniki ne zmorejo dovolj hitro brati dnevnika. Tako pri preizkusih na našem monorepozitoriju v dejanskem okolju kot v tej simulaciji Pando podpira več tisoč odjemalcev, ki lahko sočasno potiskajo brez poslabšanja zmogljivosti pod obremenitvijo.

Pri obdelavi potiska Pando preveri veljavnost prejete paketne datoteke med pretočnim prenosom v shrambo blobov, ne da bi jo razpakiral v lokalni repozitorij. Ko odjemalec začne prevzem, Pando izbere shranjene pakete, ki jih bralec potrebuje, ta pa jih prenese. Nobenemu strežniku ni treba uveljavljati paketnih datotek v lokalnem repozitoriju ali izvajati zahtevnih operacij razpakiranja in pakiranja. Ker strežniki Pando večinoma izvajajo pogajanja in izbirajo paketne datoteke, lahko delujejo na običajni strojni opremi.

Pando se tako lahko skalira do meja zmogljivosti odjemalcev, omrežja med odjemalci in sistemom Pando ter storitve za shranjevanje blobov. Ob zadostni zmogljivosti lahko odjemalci, ki delajo na neodvisnih vejah, prenašajo pakete s polno hitrostjo svoje povezave. Za oskrbo več odjemalcev s to hitrostjo lahko dodamo strežnike Pando ter hkrati razširimo shrambo metapodatkov in shrambo blobov.

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 lokalno uredi datoteke in ustvari potrditev ter zažene git push, ki iz teh sprememb ustvari paketno datoteko za posodobitev veje. Pando prejme paket in ga pretočno prenese v shrambo blobov. Ko Pando z enoprehodnim dekodiranjem preveri veljavnost prejetega potiska, s transakcijo nad več ključi zapiše različico reference v našo shrambo metapodatkov in atomarno posodobi kazalec reference. Tako Pando zagotavlja konsistentnost po načelu »branje lastnih zapisov«. Celotna operacija pisanja zahteva O(1)O(1) branj in zapisov metapodatkov.

Ko drug odjemalec prevzame vejo, Pando na podlagi zgodovine, ki jo odjemalec že ima, izbere nabor shranjenih paketov. Ta operacija zahteva O(log⁡Δ)O(\log \Delta) branj metapodatkov, pri čemer je Δ\Delta najkrajša razdalja v različicah referenc med tem, kar odjemalec ima, in tem, kar želi. Strežnik vrne URI-je paketnih datotek, odjemalec pa jih prenese prek storitve Express, ki paketne datoteke shranjuje v regionalnih predpomnilnikih.

Ta zasnova temelji na treh osrednjih storitvah arhitekture Pando: potiskih, prevzemih in kompaktiranju.

Skaliranje potiskov

Ko odjemalec potiska spremembe, je običajni strežnik Git kot igralec pokra, ki razkrije vse karte: odjemalcu pokaže vse svoje veje. Odjemalec nato zbere potrditve, drevesa in blobe ter ustvari paketno datoteko, ki izključuje objekte, za katere ve, da jih strežnik že ima.

Oglaševanje več referenc lahko zmanjša obseg potiska. Lahko pa tudi oteži ponovno uporabo paketa, ker drugemu odjemalcu morda manjkajo objekti, ki jih je pisec izpustil.

Pando uporablja drugačen pristop. Namesto da bi razkril vse karte, odjemalcu oglašuje le izbran nabor vej. Za to sta dva razloga:

  1. Zagotavljanje konsistentnega pogleda na vse reference je potratno. Pri milijonih vej lahko že samo oglas referenc preseže 100 MiB. Za to mora vsak strežnik bodisi vzdrževati ažurno preslikavo, kar zahteva veliko dodatnega usklajevanja, bodisi izvesti poizvedbo v zbirki podatkov, katere zahtevnost narašča s številom referenc.

  2. Če odjemalcu posredujemo le podmnožico referenc, ustvari paket, ki je odvisen samo od tega, kar mu strežnik izrecno dovoli izključiti. S sledenjem odvisnostim lahko Pando ponovno uporabi paketno datoteko, ki jo odjemalec ustvari pri enem potisku, in jo posreduje kot odgovor na poznejši prevzem.

Več referenc ko strežnik Git oglašuje, več virov imajo odjemalci pri potisku za izključevanje objektov, zato lahko ustvarijo manjše pakete. Vendar mora strežnik za ponovno uporabo teh paketnih datotek znati dokazati, da imajo odjemalci, ki prevzemajo, vse njihove odvisnosti. Sicer lahko v paketni datoteki manjkajo objekti, ki jih odjemalec potrebuje.

Če pa strežnik Git ne oglašuje nobene reference, odjemalec ustvari paketno datoteko s celotno zgodovino repozitorija. Takšno paketno datoteko lahko uporabi kateri koli drug odjemalec, vendar za visoko ceno: prevzem katere koli veje dejansko postane popolno kloniranje.

Krivulja prikazuje vpliv oglaševanih referenc na velikost paketne datoteke: od celotne zgodovine, ko ni oglaševana nobena referenca, prek oglaševanja glavne veje do zgolj novih objektov, ko so oglaševane vse reference.

Oglaševanje več referenc pomeni manjše paketne datoteke. Večino zmanjšanja velikosti lahko dosežemo že z oglaševanjem ene same reference.

Ko odjemalec potisne novo razvojno vejo, Pando privzeto kot osnovo oglašuje le določeno glavno vejo. Velikost oglasa tako ostane enaka tudi ob dodajanju vej v repozitorij, vendar to prinaša kompromise. Ko inženir večkrat potisne spremembe v isto vejo ali eno razvojno vejo zgradi na drugi, mora odjemalec znova poslati vse, kar ni v glavni veji.

Zato so zaporedni potiski v vejo večji, lahko vsebujejo odvečne objekte in porabijo več odjemalčeve pasovne širine. V našem monorepozitoriju je bila mediana velikosti tako ustvarjenih paketnih datotek manjša od 20 KiB, 99. percentil pa pod 3 MiB. Za nas je bil to smiseln kompromis, saj lahko bralec, ki že ima oglaševano zgodovino glavne veje, vejo prevzame s to eno paketno datoteko.

Ko Pando izbere nabor referenc, ki jih bo oglaševal odjemalcu, sprejme potisk in posodobi shrambo metapodatkov. Pando posodobitev metapodatkov razdeli na sinhroni in asinhroni del. Sinhrona posodobitev metapodatkov zagotavlja, da lahko odjemalci takoj prevzamejo novo posodobljeno potrditev. Pri tem se zapiše nova različica reference in posodobi preslikava kazalca reference na to različico. Shranijo se novi vrh, odvisnosti, uporabljene pri pogajanju o paketni datoteki, in povezava do potisnjene paketne datoteke.

V isti transakciji se hkrati zabeležijo tudi asinhrona opravila, denimo načrtovanje kompaktiranja. Pri zelo velikih potiskih se sinhrono zapiše le podmnožica potrditev. Zato potrebujemo le konstantno število branj in zapisov v shrambo metapodatkov, ne glede na velikost potiska.

Pomembno je, da lahko strežniki Pando neodvisno sprejemajo potiske v nepovezane reference. Edini ključ, za katerega tekmujejo, je kazalec reference, ki ga varujemo z atomarno operacijo, podobno operaciji primerjaj in zamenjaj. Prepustnost pisanja lahko povečujemo z dodajanjem strežnikov Pando, zmogljivostnih enot shrambe metapodatkov ali shramb blobov.

Prevzem in kloniranje s polno hitrostjo povezave

Ko odjemalec izvede prevzem, strežniku posreduje dva nabora ID-jev potrditev, ki sta v protokolu označena kot have in want. Običajni strežnik Git izračuna razliko med množico objektov, dosegljivih iz želenih potrditev, in množico objektov, dosegljivih iz obstoječih potrditev, dobljene objekte stisne ter rezultat pošlje odjemalcu. Čeprav ima odprtokodni strežnik Git za pospešitev te operacije na voljo različne lokalne indekse, je lahko ustvarjanje teh paketnih datotek računsko zahtevno, praviloma pa so tudi slabo primerne za predpomnjenje.

Pando namesto tega uporabi preslikave potrditev, zapisane ob potiskih, da poišče odjemalčeve vnose have in want ter jih preslika v nabor različic referenc, ki vsebujejo te potrditve. Vsaka od teh različic referenc je povezana s paketno datoteko, ki jo je Pando že prejel. Strežnik na tej podlagi ugotovi, kaj odjemalec potrebuje, in izbere paketne datoteke, ki pokrijejo nabor potrebnih objektov.

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 preslika obstoječe in želene potrditve iz protokola Git v zaporedje različic referenc. Zahteva za stransko vejo vključuje tudi zgodovino do njenih odvisnosti v glavni veji.

Protokol Git pri kloniranju ali prevzemu dovoljuje pošiljanje le ene paketne datoteke v odgovoru HTTP. Vendar premalo cenjena funkcija protokola Git, packfile-uri, strežniku omogoča, da vrne povezave za ločen prenos dodatnih paketov. Pando uporablja izključno to možnost, v samem odgovoru pa pošlje prazno paketno datoteko. Za podporo tej funkciji je OpenAI v izvorni projekt prispeval več izboljšav za packfile-uri.

Sistemi OpenAI delujejo v številnih geografskih regijah, zato v vsaki vzdržujemo gruče Pando z lokalnimi predpomnilniki. Storitev za dostavo paketov Express predpomni našo shrambo blobov, ločena storitev pa predpomni shrambo metapodatkov. Ti predpomnilniki omogočajo, da Pando zahteve za ID potrditve ali posebno branje reference v celoti postreže iz predpomnjenega stanja v tej gruči. Za usmerjanje zahtev za paketne datoteke na strežnike Express uporabljamo konsistentno zgoščevanje, pri čemer delež zadetkov v predpomnilniku presega 99 %.

Ta sistem predpomnilnikov zagotavlja izjemno visoko prepustnost, ki lahko v celoti izkoristi omrežno zmogljivost med odjemalci in sistemom Pando. S temi URI-ji paketnih datotek in lokalnimi predpomnilniki smo potrdili, da operacije kloniranja in prevzema že na standardnih navideznih računalnikih ponudnikov oblaka v celoti izkoristijo omrežne povezave s hitrostjo 10 Gbps.

Kompaktiranje in čas do prvega bajta

Pri večini strežnikov Git sta ponovno pakiranje in čiščenje neuporabljenih objektov nujna za ohranjanje zmogljivosti rastočega repozitorija. Oba postopka sprostita prostor, vendar sta računsko in vhodno-izhodno zahtevna.

V hitro spreminjajočem se repozitoriju, kot je monorepozitorij OpenAI, ni izvedljivo poslati ločenega URI-ja paketne datoteke za vsak potisk od zadnjega prevzema. Odjemalec bi moral vsak dan prenesti več deset tisoč paketnih datotek, da bi posodobil svojo glavno vejo.

Pando zato v ozadju združuje pakete in tako zmanjša te dodatne stroške. Pri tem uporablja zasnovo, podobno kompaktiranju v dnevniško strukturiranih združevalnih drevesih (LSM). Vsak potisk ustvari paketno datoteko, ki jo Pando imenuje »L0« in predstavlja najmanjšo enoto podatkov, ki jo lahko prenese. Sčasoma procesi v ozadju te paketne datoteke zbirajo v pokritja, ki zajemajo zaporedja različic referenc in se od ravni L0 navzgor geometrijsko povečujejo. Danes naš monorepozitorij uporablja pokritja paketnih datotek do ravni L4.

Ta pokritja imajo dvojni namen. Ko je pokritje dovolj polno, ga prevzame proces za kompaktiranje in ustvari eno samo paketno datoteko. S prerazporeditvijo objektov v paketni datoteki se izboljša razmerje stiskanja. Hkrati pokritje pospeši pogajanje pri prevzemu, saj deluje kot preskočni seznam različic referenc. S sočasnim pregledovanjem indeksov na vseh ravneh lahko Pando v logaritemskem času izbere nabor paketov, ki jih pošlje odjemalcu. Ta pokritja paketnih datotek, ne glede na to, ali so že ponovno zapakirana, omogočajo učinkovitejše načrtovanje prevzemov ter zmanjšujejo zahteve glede prepustnosti in zakasnitve sistema Pando.

Compaction in Pando

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

Compaction in progressPublished cover

Kompaktiranje v ozadju združuje paketne datoteke iz številnih različic referenc ter omogoča učinkovitejše prevzeme z manj datotekami.

Za razliko od številnih strežnikov Git Pando paketne datoteke ohranja na vseh ravneh in jih ne zavrže ali odstrani med čiščenjem. Paketi na vseh ravneh se nenehno uporabljajo za streženje odjemalcem na podlagi vnosov have in want, ki jih ti pošljejo pri prevzemu. Pri odjemalcih, ki Pando uporabljajo kot API, pa branje poljubnega objekta iz paketne datoteke navadno zahteva manj prebranih podatkov iz L0 kot iz L4, saj je manj verjetno, da je objekt stisnjen.

Kompaktiranje v sistemu Pando bistveno zmanjša skupno količino podatkov, poslanih odjemalcem, in omogoča, da zahtevnost načrtovanja paketov narašča počasneje kot linearno glede na to, koliko odjemalec zaostaja. To je še posebej pomembno za velike in hitro spreminjajoče se repozitorije, za katere pričakujemo, da bodo postali nova stalnica razvoja programske opreme.

Razvoj sistema Pando

Pando smo razvili s pomočjo Codexa in od začetka projekta do produkcije prišli v samo 10 tednih. Jedro sistema Pando je zasnoval en inženir, implementirale so ga ekipe agentov, uvedla pa ga je peščica inženirjev. Pando je produkcijski promet začel obdelovati aprila letos in je del širšega sistema orodij za upravljanje izvorne kode, ki jih uporabljamo v OpenAI.

Pri preverjanju zasnove sistema Pando smo uporabili najrazličnejše pristope. Upoštevali smo svoja načela za gradnjo varnejših temeljev programske opreme⁠. Ker smo projekt začeli povsem na novo, smo se odločili Pando implementirati v Rustu in brez odvisnosti od podedovanih komponent. Napisali smo zbirko preizkusov skladnosti, da so agenti lahko preverjali svoje spremembe glede na standardno delovanje Gita. Razvili smo tudi ogrodja za deterministično simulacijsko testiranje, s katerimi preverjamo invariante in lastnosti sistema. Tako smo lahko odkrili in odpravili težko opazna tekmovalna stanja ter regresije pri pogajanju in načrtovanju paketov.

K sistemu so prispevali številni modeli; ko so bili na voljo novi, smo jih uporabili za izboljšanje zmogljivosti in zanesljivosti sistema Pando. Agenti so z modelom GPT‑6 Astra pisali v jezikih Lean in TLA+, da bi formalizirali »teorijo sistema Pando«, s katero smo iskali in odpravljali napake v implementaciji ter dokazovali lastnosti sistema. Več neprekinjenih sej je trajalo po več sto ur in v sistemu Pando iskalo ter odpravljalo robne primere. Agente z modeli, ki podpirajo Daybreak, uporabljamo tudi za neprekinjeno varnostno preizkušanje Gita in sistema Pando z vidika napadalca.

Uvedba sistema Pando

Pando smo sprva uvedli kot posredniški strežnik, ki je branja in pisanja posredoval našemu obstoječemu sistemu za nadzor izvorne kode. Tako smo lahko Pando in prejšnji sistem ohranjali usklajena ter promet varno preusmerjali na Pando brez izpadov.

V okviru zasnove predpomnjenja lahko Pando pred posredovanjem tudi odstrani podvojene zahteve in jih združi. Tako smo ohranjali nemoteno delovanje obstoječih orodij, medtem ko smo sisteme enega za drugim selili na Pando. Ko smo na Pando prenesli več delovnih obremenitev, so njihovi potiski polnili njegov predpomnilnik in izboljševali zanesljivost prejšnjega zaledja.

Da bi optimizirali zmogljivost pri uporabi sistema Pando, smo svojo javno distribucijo Gita začeli nameščati na računalnike razvijalcev in jo vključevati v namizno aplikacijo ChatGPT. Za postopno selitev odjemalcev na Pando smo uporabili interni ovoj ukaznovrstičnega vmesnika Git. Ustvarili smo številna druga orodja za obravnavo zahtev za spremembe in pregled kode ter API-je za avtomatizacijo. Skupaj s prometom Git smo preselili na stotine integracij, ki so bile odvisne od podedovanih API-jev, spletnih kavljev in prenosov repozitorijev.

Pri prehodu je razvijalcem in ekipam pomagal tudi sam Codex. Ustvarili smo veščine za pripravo obstoječih delovnih kopij in za usmerjanje agentov pri uporabi novega sistema. Razvijalci so zato lahko Codex preprosto prosili, naj preseli njihova razvojna okolja in storitve.

Prehod na Pando je izboljšal zanesljivost združevanja in zmanjšal mediano zakasnitve potiskov za skoraj 80 %.

Pando lahko z le peščico procesorskih jeder dostavi petabajt podatkov Git na dan. Pando je v eni uri posredoval več kot 57 terabajtov paketov Git, pri čemer je več kot 99 % teh bajtov prišlo iz lokalnega diskovnega predpomnilnika.

Ponovna uporaba paketov številnim odjemalcem omogoča prenos iste zgodovine repozitorija z malo računanja na strežnih vozliščih. Ta primer prikazuje en repozitorij, vendar je Pando zasnovan tako, da jih podpira veliko več. Ob naraščanju povpraševanja lahko dostavo podatkov in pogajanje Git skaliramo neodvisno.

Prispevki k projektu Git

Ker smo Pando zgradili na protokolu in odjemalcu Git, smo odkrili napake in priložnosti za izboljšanje zmogljivosti samega Gita.

Pando na primer pakete za ponovno uporabo posreduje neposredno odjemalcem, zato sta njihov prenos in indeksiranje postala ključna za hitrost in zanesljivost sistema. Pri tem delu so se pokazali robni primeri pri sočasnih prenosih, čiščenju po prekinjenih prenosih in nadaljevanju že dokončanih prenosov. Te poti izvajanja smo popravili, dodali preverjanja pri sprejemanju obstoječega paketa za prevzem in spremembe prispevali v izvorni projekt.

Ugotoviti smo morali tudi, kje se porabljata čas in pasovna širina, zato smo dodali sledenje prenosom packfile-uri in štetje bajtov pri ustvarjanju paketov. Tako lažje ugotovimo, ali pri počasnem prevzemu čas porabljamo za ustvarjanje paketov ali za njihov prenos.

V različici Gita podjetja OpenAI⁠(odpre se v novem oknu) lahko odjemalec neodvisne pakete prenaša in indeksira vzporedno. Dodali smo koncept »izrecno navedenih obstoječih potrditev«, da lahko strežnik trenutni vrh veje oglašuje ločeno od zgodovine, ki jo odjemalec pri potisku lahko izpusti iz svojega paketa. V sistemu Pando lahko odjemalci tako z možnostjo --force-with-lease preverijo, da se razvojna veja ni premaknila, in paket zgradijo samo glede na glavno vejo. Vsakdo s to zgodovino glavne veje lahko nato vejo prevzame z istim paketom.

Več teh izboljšav je že v izvornem projektu Git, številne druge pa v našem javnem repozitoriju. S skupnostjo Git še naprej sodelujemo, da bi bile te izboljšave uporabne tudi zunaj sistema Pando. Temu delu lahko sledite v našem javnem sledilniku popravkov⁠(odpre se v novem oknu).

Kaj sledi

S ponovnim razmislekom o uveljavljenih pristopih k skaliranju repozitorijev Git nam Pando omogoča razširjanje monorepozitorija brez spreminjanja delovnih postopkov Git, ki jih naši inženirji in agenti že uporabljajo. V našem monorepozitoriju je Pando omogočil več vzporednega dela, izboljšal zanesljivost združevanja in skrajšal čas za objavo sprememb kode. Poleg monorepozitorija raziskujemo tudi druge možnosti uporabe sistema Pando v OpenAI, vključno z delovnimi obremenitvami, pri katerih mora veliko agentov sodelovati v skupnem repozitoriju Git.

Ker inženirji in agenti ustvarjajo vse več programske opreme, bo vse več ekip zunaj OpenAI naletelo na omejitve nadzora izvorne kode, s katerimi smo se srečali mi. Trenutno raziskujemo, kako bi lahko tehnologijo, na kateri temelji Pando, delili z našimi uporabniki.

Pando temelji na dveh desetletjih izkušenj uporabnikov in upravljavcev repozitorijev Git. Pando bomo še naprej razvijali in pri tem sodelovali z odprtokodno skupnostjo Git, da bi delili izboljšave, ki koristijo širšemu krogu uporabnikov Gita.

Avtorji

OpenAI