Škálovanie Gitu v OpenAI
Budujeme správu zdrojového kódu pre rozsah práce agentov a zároveň investujeme do ekosystému Gitu.
Za uplynulý rok rástla veľkosť nášho monorepozitára aj nároky naň ohromujúcim tempom. Bolo jasné, že musíme zásadne prepracovať systém správy zdrojového kódu, aby zvládol rozsah súbežnej práce, ktorý umožňuje Codex.
Vytvorili sme Pando, nový backend pre repozitáre Git, bez prerušenia vývojárskej práce či zmeny spôsobu, akým náš tím používa Git. Pando dnes zabezpečuje celý vývoj v monorepozitári OpenAI. Pando je navrhnuté tak, aby splnilo dva ambiciózne ciele: po prvé, horizontálne škálovateľné zápisy optimalizované na súbežné operácie push do mnohých vetiev. A po druhé, maximálnu priepustnosť čítania, aby klienti mohli sťahovať dáta dostatočne rýchlo na plné využitie dostupnej šírky pásma siete.
Tieto ciele sme splnili pri zachovaní kompatibility s protokolom a klientom Git, takže naši inžinieri aj agenti mohli Pando bez problémov začleniť do existujúcich pracovných postupov. V produkčnej prevádzke Pando znížilo medián latencie operácie push na takmer pätinu. Teraz denne poskytuje takmer petabajt dát Gitu.
V tomto článku vysvetlíme, čo nás viedlo k vytvoreniu nového systému správy zdrojového kódu a prečo sme sa rozhodli zachovať kompatibilitu s Gitom. Porovnáme náš systém s bežnými prístupmi ku škálovaniu Gitu a opíšeme, prečo Pando dokáže jedinečným spôsobom naplniť potreby OpenAI v oblasti škálovateľnosti. Priblížime aj ďalšie plány a to, ako budeme naďalej prispievať do Gitu, aby podporoval prácu v rozsahu, aký prinášajú agenti.
Codex zmenil rozsah aj tempo vývoja v OpenAI a umožnil inžinierom vykonávať oveľa viac práce paralelne. To znamená viac vetiev a operácií push, viac zlučovaní aj viac operácií fetch a clone z vývojových prostredí, výskumných úloh a CI.
Tieto nároky sa navzájom násobia. Jeden inžinier má teraz naraz pripravených niekoľko zmien pre CI a každá operácia push môže spustiť viacero úloh, ktoré načítavajú ten istý commit.
Približne od konca minulého roka systémy zabezpečujúce náš monorepozitár začali mať problém držať krok. Trápili nás pomalé operácie fetch a push aj zlyhania zlučovania, pričom spoľahlivosť občas klesala pod 90 %. Narazili sme na limit samotného Gitu alebo spôsobu, akým sme prevádzkovali náš monorepozitár? Git vznikol v komunite, ktorá si e-mailom vymieňala záplaty jadra. Už viac než dve desaťročia vývojári prispôsobujú svoje nástroje okolo Gitu tomu, ako sa mení spôsob tvorby softvéru. Keď sme v našom monorepozitári začali narážať na problémy, záležalo nám na tom, aby sme na tejto práci mohli ďalej stavať.
Videli sme možnosť zachovať známy pracovný postup a zároveň prebudovať backend tak, aby paralelne spracúval operácie push do rôznych vetiev a súčasne poskytoval rovnakú históriu mnohým čítajúcim klientom. Keďže sme správu zdrojového kódu postavili na Gite, vylepšenia klienta mohli byť užitočné aj mimo OpenAI. Chceli sme tieto vylepšenia z našej kópie Gitu(otvorí sa v novom okne) začleniť do hlavného projektu a sprístupniť ich širšej komunite.
Škálovanie systému správy zdrojového kódu má dva základné rozmery: mnoho zapisujúcich klientov odosiela zmeny do rôznych vetiev a mnoho čítajúcich klientov načítava tú istú históriu. Pando optimalizuje oba.
Po prvé, Pando koordinuje aktualizácie každej vetvy nezávisle. Aktualizácie nesúvisiacich vetiev môžu prebiehať paralelne a klient načítavajúci vetvu získa históriu potrebnú pre danú revíziu. Kompromisom je, že klient čítajúci viacero vetiev ich môže vidieť v stavoch z rôznych okamihov.
Po druhé, Pando pri operáciách fetch opakovane využíva súbory pack (formát Gitu na spoločnú kompresiu viacerých objektov) namiesto vytvárania osobitného súboru pack pre každého klienta. Klient si môže stiahnuť aj niektoré objekty, ktoré už má, no server dokáže poskytovať tie isté bajty mnohým klientom s minimálnymi výpočtovými nárokmi.
Tieto rozhodnutia nám umožňujú škálovať zápisy naprieč vetvami a obsluhovať čítania z vyrovnávacích pamätí. Aby bolo jasné, ako to do seba zapadá, začnime tým, ako Git ukladá a prenáša históriu.
Git musí udržiavať synchronizované dve časti repozitára: objekty a referencie.
Repozitár Git zaznamenáva snímky súborov a históriu, ktorá ich spája, ako graf objektov. Objekt môže byť blob, strom, commit alebo značka. Bloby uchovávajú obsah jednotlivých súborov a stromy priraďujú názvy k blobom alebo iným stromom, čím vytvárajú adresárovú štruktúru. Commity odkazujú na koreňový strom a nula alebo viac rodičovských commitov, čím tvoria históriu repozitára.
Objekty sa identifikujú hašom svojho typu a obsahu. Objekty sú tiež nemenné: pri zaznamenaní zmeneného súboru vznikne nový blob, zatiaľ čo nezmenené súbory a adresáre môžu opätovne použiť existujúce objekty.
Vetvy a značky sú referencie, označované aj ako „refs“, ktoré odkazujú na commity. Pri commitovaní do vetvy vznikne nový commit a referencia vetvy sa aktualizuje tak, aby naň odkazovala.
Objekty Gitu na seba odkazujú pomocou svojich ID: commity na stromy a stromy na ďalšie stromy a bloby. Vetva refs/heads/main odkazuje na commit C2, ktorého rodičovským commitom je C1. C1 odkazuje na koreňový strom T1, v ktorom parser.py odkazuje na blob B1. C2 odkazuje na koreňový strom T2, v ktorom parser.py odkazuje na blob B2. V oboch koreňových stromoch docs/ odkazuje na ten istý strom T3. Jeho položka README.md odkazuje na blob B3 a usage.md na blob B4.
Git ukladá objekty buď vo „voľnom“, alebo v „zabalenom“ stave. Voľné objekty sú uložené v samostatných súboroch identifikovaných ich ID objektu. Zabalené objekty sú uložené spoločne v súbore pack, čo umožňuje ukladať podobné objekty ako vzájomné rozdiely.
Git môže ukladať bloby ako samostatné súbory alebo ich baliť spoločne, pričom jeden blob zakóduje ako rozdiel oproti druhému.
V Gite je objekt dosiahnuteľný z commitu, ak ho Git dokáže nájsť sledovaním odkazov z daného commitu cez jeho rodičovské commity, stromy a bloby. Počas operácie fetch klient oznámi serveru, ktoré commity požaduje a ktoré už má. Server nájde objekty dosiahnuteľné z požadovaných commitov, vynechá tie, o ktorých vie, že ich klient už má, a zvyšok odošle ako súbor pack v rovnakom formáte, aký Git používa na disku. Úplné klonovanie začína bez akýchkoľvek objektov, preto potrebuje všetky objekty dosiahnuteľné z klonovanej histórie.
Operácia push prenáša objekty opačným smerom a žiada server o aktualizáciu jednej alebo viacerých referencií. Klient využíva množinu referencií oznámených serverom, aby neposielal objekty, ktoré server už má. Odosielaný balík preto môže závisieť od existujúcich objektov: samotné prijatie balíka nestačí na obnovenie novej histórie.
Replikácia repozitára Git znamená udržiavať objekty aj referencie dostupné na viacerých serveroch. Skôr než replika môže poskytovať novú hodnotu referencie, musí dokázať dodať objekty dosiahnuteľné z tejto hodnoty. Služba musí tiež koordinovať súperiace aktualizácie referencií, teda operácie push, ktoré sa snažia posunúť tú istú vetvu z rovnakého pôvodného koncového commitu na rôzne nové commity. V systémoch, ktoré zavádzajú jediné poradie všetkých zápisov do repozitára, môžu operácie push do nesúvisiacich vetiev musieť čakať jedna na druhú.
Pri navrhovaní systému Pando sme vyhodnotili bežné prístupy ku škálovaniu správy zdrojového kódu pomocou Gitu: jeden koordinuje aktualizácie referencií medzi replikami repozitára pomocou trojfázového potvrdenia (3PC, protokol na koordináciu potvrdenia transakcie naprieč viacerými uzlami), druhý určuje poradie aktualizácií cez zdieľaný denník predbežného zápisu (WAL, záznam zmien zapísaný pred ich aplikovaním) v úložisku blobov.
V nami modelovanom návrhu s trojfázovým potvrdením uchováva služba Git úplnú kópiu repozitára na viacerých replikách. Keď ľubovoľný server prijme operáciu push, prichádzajúci súbor pack sa skopíruje a paralelne odošle všetkým replikám. Na zverejnenie zmien z operácie push musí aktualizáciu referencie prijať väčšina replík. Server, ktorý to riadi, je koordinátor. Tento proces si vyžaduje viacero kôl komunikácie medzi koordinátorom a každou ďalšou replikou.
Aby replika prijala aktualizáciu referencie, uzamkne svoju kópiu referencie a overí, že stále odkazuje na commit, ktorý klient očakáva. Koordinátor potom pošle replikám, ktoré aktualizáciu prijali, správu o predbežnom potvrdení. Keď väčšina potvrdí prijatie tejto správy, koordinátor im dá pokyn vykonať aktualizáciu. Každá aktuálna replika potom môže zo svojej lokálnej kópie repozitára oznamovať nový koncový commit vetvy.
Udržiavanie úplných replík umožňuje obsluhovať čítania lokálne a zaručuje, že klienti získajú konzistentný pohľad na všetky referencie bez ohľadu na vybranú repliku. Referencie sa preto aktualizujú po jednej a každá operácia push vyžaduje viacero komunikačných cyklov na koordináciu aktualizácie referencie medzi replikami.
Pre čakanie na kvórum ovplyvňuje trvanie operácie push aj výkon diskov replík a latencia komunikácie. S rastúcim objemom zápisov musia repliky stíhať spracúvať prichádzajúce balíky aj požiadavky na čítanie.
V nami modelovanom návrhu založenom na WAL služba Git používa zdieľaný manifest v úložisku blobov na určovanie poradia aktualizácií v celom repozitári. Keď server prijme operáciu push, nahrá prichádzajúci súbor pack do úložiska blobov a pripraví aktualizáciu referencie vo svojej lokálnej kópii repozitára. Na zverejnenie zmien pridá do zdieľaného manifestu v úložisku blobov položku, ktorá zaznamenáva aktualizáciu referencie a odkazuje na súbor pack. Server Git potom aktualizuje manifest pomocou operácie compare-and-swap. Tá buď atómovo nahradí manifest, alebo zmenu odmietne. Ak ho medzitým aktualizoval iný zapisujúci klient, server načíta medzičasom pridané položky, overí, že jeho aktualizácia referencie je stále platná, a pokúsi sa o ňu znova s už nahraným súborom pack.
Zapisujúci klienti môžu nahrávať súbory pack paralelne, no aj operácie push do nesúvisiacich vetiev vyžadujú aktualizáciu toho istého manifestu. Čas komunikačného cyklu úspešnej operácie compare-and-swap obmedzuje, ako často môže repozitár zverejniť nový manifest. Úložisko blobov s nízkou latenciou tento krok zrýchľuje. Dávkové spracovanie umožňuje jedným manifestom aktualizovať viacero referencií naraz, no za cenu radenia aktualizácií do frontu a zvýšenia latencie.
Úložisko blobov môže prijímať súbory pack paralelne, ale aktualizácie zdieľaného manifestu stále navzájom súperia. Samotné zvýšenie kapacity úložiska blobov toto úzke miesto neodstráni. Každá replika stále musí načítať a indexovať balíky z úložiska blobov, kým ich môže poskytovať lokálne. Ak prichádzajúce zápisy predbiehajú túto prácu, repliky zaostávajú a klienti dlhšie čakajú na načítanie novo odoslaných commitov.
Tým sa dostávame k systému Pando. Pando je nová architektúra na obsluhu repozitárov Git, ktorej cieľom je škálovať na veľký počet klientov súčasne vykonávajúcich push. Funguje takto:
Pred vývojom systému Pando sme tušili, že naše požiadavky dokážeme splniť bez kompromisov v používaní Gitu. Museli sme však ustúpiť od niektorých predpokladov, ktoré väčšina systémov založených na Gite považuje za požiadavky:
Konzistencia v celom repozitári: Poskytnutie konzistentnej snímky všetkých referencií k určitému okamihu si vyžaduje koordináciu naprieč repozitárom. V systéme, ktorý určuje spoločné poradie všetkých aktualizácií referencií, operácie push do nesúvisiacich vetiev súperia o jediný koordinačný bod.
Jedinečné súbory pack pre každého klienta: Ak dvaja klienti s odlišnou lokálnou históriou načítavajú rovnakú vetvu, server môže potrebovať vytvoriť pre každého iný balík, aby neposielal objekty, ktoré už majú. Vytváranie týchto odpovedí spotrebúva procesorový výkon a diskové I/O, čo sťažuje škálovanie na veľký počet čítajúcich klientov.
Pando namiesto toho používa voľnejší model konzistencie (kauzálnu konzistenciu) na škálovanie zápisov a opakovane využíva súbory pack na škálovanie čítaní.
Keď klient vykoná push do vetvy, Pando uloží prichádzajúci súbor pack ako nemenný blob do úložiska blobov. Pri každom posune vetvy Pando zaznamená do úložiska metadát verziu referencie obsahujúcu nový koncový commit, balík, ktorý ho dodal, a všetky skoršie verzie referencií, od ktorých balík závisí. Úložisko blobov aj úložisko metadát sú horizontálne škálovateľné.
Načítanie commitu alebo referencie zo systému Pando vyžaduje, aby sa klientovi prehrala množina verzií referencií (súborov pack), čím sa obnoví stav klienta, ktorý vytvoril načítavanú verziu referencie. Tým sa zabezpečí slabšia forma konzistencie v celom repozitári, no zároveň serializovateľnosť aktualizácií jednotlivých referencií. Konflikty medzi klientmi vznikajú iba vtedy, keď súčasne vykonávajú push do rovnakej referencie. Klient čítajúci viacero vetiev nemôže predpokladať, že tieto čítania zachytávajú stav celého repozitára v jednom okamihu.
Operácia push do hlavnej vetvy pridá prírastkový balík obsahujúci jeden alebo viac commitov. Každá operácia push do vedľajšej vetvy znova zahrnie aj jej predchádzajúce commity, takže klient s oznámenou verziou hlavnej vetvy môže vedľajšiu vetvu načítať v jednom balíku.
Ide o formu kauzálnej konzistencie. Pri načítaní referencie čítajúci klient vidí históriu zapisujúceho klienta, ktorý ju vytvoril. Počas operácie push Pando riadi, na akej spoločnej histórii sa klient a server dohodnú, čím zabezpečuje, aby klienti vytvárali ľahšie zdieľateľné súbory pack.
Pando preto pri operáciách Gitu neposkytuje stav všetkých referencií k jednému okamihu ani neurčuje poradie aktualizácií referencií v rámci operácie clone alebo fetch. Predpokladajme napríklad, že počas vyjednávania úplného klonovania sa aktualizujú vetvy foo aj bar. Počas vyjednávania môže jeden klient vidieť foo vo verzii 1 a bar vo verzii 2, zatiaľ čo iný klient vidí foo vo verzii 2 a bar vo verzii 1. V praxi používatelia ani agenti nezávisia od globálneho poradia aktualizácií referencií, ale od poradia aktualizácií jednej referencie.
V tejto simulácii troch modelov konzistencie nastavujeme 1 000 klientov tak, aby si každý vybral inú vetvu a potom do nej opakovane odosielal jedinečné commity. Meriame celkový uplynutý čas a latenciu od operácie push po fetch. Druhá metrika je pri pracovných záťažiach dôležitá, pretože meria čas celého cyklu, ktorý používatelia a agenti potrebujú pri spolupráci alebo pri používaní iných systémov závislých od správy verzií. Simulácia modeluje hardvér od bežných až po prémiové virtuálne počítače a systémy úložiska blobov. V tejto simulácii sme pri trojfázovom potvrdení zmiernili požiadavku na konzistenciu v celom repozitári.
Simulating Git Server Implementations
Follow each writer through repeated pushes, and separately measure when an independent reader can check out each commit.
Spustite túto simuláciu, ktorá porovnáva Pando s dvoma ďalšími prístupmi. Vyberte infraštruktúru od bežného po prémiový hardvér, počet klientov a ďalšie nastavenia a pozrite si porovnanie.
Modelovaný systém s trojfázovým potvrdením vykazuje pri záťaži výrazne vyššiu latenciu zápisu, keď jeho disky dosiahnu limit výkonu. Systémy s denníkom predbežného zápisu z rovnakého dôvodu vykazujú výrazne vyššiu latenciu čítania, pretože servery nestíhajú čítať z denníka. V reálnych testoch na našom monorepozitári aj v tejto simulácii Pando podporuje mnoho tisíc klientov, ktoré dokážu súčasne vykonávať push bez zhoršenia výkonu pri záťaži.
Pri spracovaní operácie push Pando overuje prichádzajúci súbor pack počas jeho priebežného prenosu do úložiska blobov bez toho, aby ho rozbaľovalo do lokálneho repozitára. Keď klient spustí fetch, Pando vyberie uložené balíky, ktoré potrebuje, a klient si ich stiahne. Žiadny server nemusí aplikovať súbory pack na lokálny repozitár ani vykonávať náročné operácie rozbaľovania a balenia. Keďže servery Pando predovšetkým vyjednávajú a vyberajú súbory pack, môžu bežať na bežnom hardvéri.
Pando tak môže škálovať až po limity klientov, siete medzi klientmi a systémom Pando a služby úložiska blobov. Pri dostatočnej kapacite môže každý klient pracujúci na nezávislej vetve prenášať balíky tak rýchlo, ako mu to umožňuje jeho pripojenie. Ak chceme touto rýchlosťou obslúžiť viac klientov, môžeme pridať servery Pando a spolu s nimi škálovať úložisko metadát aj úložisko 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.
Agent lokálne upraví súbory, vytvorí commit a spustí git push, čím vznikne súbor pack s týmito zmenami na aktualizáciu vetvy. Pando prijme balík a priebežne ho prenesie do úložiska blobov. Po overení prichádzajúcej operácie push dekódovaním v jednom priechode Pando pomocou transakcie nad viacerými kľúčmi zaznamená verziu referencie do nášho úložiska metadát a atómovo aktualizuje ukazovateľ referencie. Pando tak dokáže zabezpečiť konzistenciu typu „čítanie vlastných zápisov“. Celá táto operácia zápisu vyžaduje čítaní a zápisov metadát.
Keď vetvu načíta iný klient, Pando vyberie množinu uložených balíkov na základe histórie, ktorú klient už má. Táto operácia vyžaduje čítaní metadát, pričom je najkratšia vzdialenosť vo verziách referencií medzi tým, čo klient má, a tým, čo požaduje. Server vráti URI súborov pack a klient si ich stiahne prostredníctvom služby Express, ktorá ich ukladá do regionálnych vyrovnávacích pamätí.
Tento návrh stojí na troch kľúčových službách architektúry Pando: operáciách push, operáciách fetch a kompakcii.
Pri operácii push sa bežný server Git správa ako hráč pokru, ktorý odkryje všetky karty: klientovi ukáže každú vetvu, ktorú má. Klient potom zhromaždí commity, stromy a bloby a vytvorí súbor pack, ktorý vynecháva objekty, o ktorých vie, že ich server už má.
Oznámenie väčšieho počtu referencií môže zmenšiť objem dát operácie push. Zároveň však môže sťažiť opätovné použitie balíka, pretože inému klientovi môžu chýbať objekty, ktoré zapisujúci klient vynechal.
Pando volí iný prístup. Namiesto odkrytia všetkých kariet oznamuje klientovi iba vybranú množinu vetiev. Má to dva dôvody:
Poskytovať konzistentný pohľad na každú referenciu je nákladné. Pri miliónoch vetiev môže samotné oznámenie referencií presiahnuť 100 MiB. Každý server si preto musí buď udržiavať aktuálne mapovanie, čo znamená značnú réžiu koordinácie, alebo vykonať databázový dopyt, ktorého náročnosť rastie s počtom referencií.
Ak klientovi poskytneme iba podmnožinu referencií, vytvorí balík závislý len od toho, čo mu server výslovne dovolil vynechať. Sledovanie závislostí umožňuje systému Pando opätovne použiť súbor pack vytvorený klientom pri jednej operácii push ako odpoveď na neskoršiu operáciu fetch.
Čím viac referencií server Git oznamuje, tým viac zdrojov majú odosielajúci klienti na vynechanie objektov, a teda môžu vytvárať menšie balíky. Opätovné použitie týchto súborov pack však vyžaduje, aby server vedel overiť, že načítavajúci klienti majú všetky závislosti. Inak môže súbor pack vynechať objekty, ktoré klient potrebuje.
Na druhej strane, ak server Git neoznámi žiadnu referenciu, klient vytvorí súbor pack obsahujúci celú históriu repozitára. Takýto súbor pack môže použiť ktorýkoľvek iný klient, no za vysokú cenu: načítanie ľubovoľnej vetvy sa prakticky zmení na úplné klonovanie.
Oznámenie väčšieho počtu referencií vedie k menším súborom pack. Väčšinu zmenšenia možno dosiahnuť oznámením jedinej referencie.
Keď klient odosiela novú vývojovú vetvu, Pando predvolene oznamuje ako základ iba určenú hlavnú vetvu. Veľkosť oznámenia tak zostáva rovnaká aj pri pribúdaní vetiev v repozitári, no prináša to kompromisy. Keď inžinier opakovane odosiela zmeny do jednej vetvy alebo vytvorí jednu vývojovú vetvu nad druhou, klient musí znova odoslať všetko mimo hlavnej vetvy.
Následné operácie push do vetvy sú preto väčšie, môžu obsahovať nadbytočné objekty a spotrebúvajú dodatočnú šírku pásma klienta. V našom monorepozitári bol medián veľkosti takto vytvorených súborov pack pod 20 KiB a 99. percentil pod 3 MiB. Pre nás to bol rozumný kompromis, pretože klient, ktorý už má oznámenú históriu hlavnej vetvy, môže danú vetvu načítať pomocou jediného súboru pack.
Po výbere množiny referencií, ktoré oznámi klientovi, Pando prijme operáciu push a aktualizuje úložisko metadát. Pando rozdeľuje aktualizáciu metadát na synchrónnu a asynchrónnu časť. Synchrónna aktualizácia metadát zaručuje, že klienti môžu okamžite načítať novo aktualizovaný commit. Zapíše sa nová verzia referencie a mapovanie ukazovateľa referencie sa nastaví na túto verziu. Uloží sa nový koncový commit, závislosti použité pri vyjednávaní súboru pack a odkaz na odoslaný súbor pack.
V tej istej transakcii sa zároveň zaznamenajú asynchrónne úlohy, napríklad naplánovanie kompakcie. Pri veľmi veľkých operáciách push sa synchrónne zapisuje iba podmnožina commitov. Vďaka tomu potrebujeme iba konštantný počet čítaní a zápisov do úložiska metadát bez ohľadu na veľkosť operácie push.
Dôležité je, že servery Pando môžu nezávisle prijímať operácie push do nesúvisiacich referencií. Jediným kľúčom, o ktorý sa súperí, je ukazovateľ referencie. Ten chránime atómovou operáciou podobnou compare-and-swap. Priepustnosť zápisu môžeme zvyšovať pridaním serverov Pando, škálovacích jednotiek úložiska metadát alebo úložísk blobov.
Pri operácii fetch klient poskytne serveru dve množiny ID commitov, v protokole označované ako have a want. Bežný server Git vypočíta množinový rozdiel objektov dosiahnuteľných z požadovaných commitov want a objektov dosiahnuteľných z už dostupných commitov have, skomprimuje ho a odošle výsledok klientovi. Hoci open-source server Git má lokálne rôzne indexy na zrýchlenie tejto operácie, vytváranie týchto súborov pack môže byť výpočtovo náročné a vo všeobecnosti sa zle ukladajú do vyrovnávacej pamäte.
Pando namiesto toho využíva mapovania commitov zaznamenané pri operáciách push na vyhľadanie klientových údajov have a want a priradí im množinu verzií referencií obsahujúcich tieto commity. Každá z týchto verzií referencií odkazuje na súbor pack, ktorý Pando už prijalo. Server na tomto základe určí, čo klient potrebuje, a vyberie súbory pack pokrývajúce požadovanú množinu 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.
Fetch 27 trunk packs.
Select a branch to include its trunk dependencies.
Pando mapuje údaje have a want protokolu Git na postupnosť verzií referencií. Požiadavka na vedľajšiu vetvu zahŕňa aj históriu až po jej závislosti od hlavnej vetvy.
Protokol Git umožňuje pri operácii clone alebo fetch odoslať v odpovedi HTTP iba jeden súbor pack. Málo docenená funkcia protokolu Git packfile-uri však serveru umožňuje vrátiť odkazy na samostatné stiahnutie ďalších balíkov. Pando používa výhradne tento mechanizmus a priamo v odpovedi prenáša prázdny súbor pack. Na podporu tejto funkcie OpenAI prispelo do hlavného projektu viacerými vylepšeniami packfile-uri.
Systémy OpenAI fungujú v mnohých geografických regiónoch, preto v každom prevádzkujeme klastre Pando s lokálnymi vyrovnávacími pamäťami. Služba na doručovanie balíkov Express poskytuje vyrovnávaciu pamäť pre úložisko blobov a samostatná služba pre úložisko metadát. Tieto vyrovnávacie pamäte umožňujú systému Pando obslúžiť požiadavky na ID commitu alebo špeciálne čítanie referencie výlučne zo stavu uloženého vo vyrovnávacej pamäti daného klastra. Na smerovanie požiadaviek na súbory pack na servery Express používame konzistentné hašovanie, pričom miera zásahov do vyrovnávacej pamäte presahuje 99 %.
Tento systém vyrovnávacích pamätí poskytuje mimoriadne vysokú priepustnosť, ktorá dokáže plne využiť sieťové spojenie medzi klientmi a systémom Pando. Pri použití týchto URI súborov pack a lokálnych vyrovnávacích pamätí sme overili, že operácie clone a fetch dokážu plne využiť 10 Gbps sieťové linky aj na štandardných virtuálnych počítačoch cloudových poskytovateľov.
Na väčšine serverov Git je na udržanie výkonu rastúceho repozitára potrebné prebaľovanie a odstraňovanie nepotrebných objektov. Oba kroky uvoľňujú priestor, no sú náročné na výpočty aj I/O.
V rýchlo sa meniacom repozitári, akým je monorepozitár OpenAI, nie je reálne posielať samostatné URI súboru pack pre každú operáciu push od poslednej operácie fetch. Klient by musel každý deň stiahnuť desaťtisíce súborov pack, aby aktualizoval svoju hlavnú vetvu.
Pando namiesto toho spája balíky na pozadí, čím znižuje túto réžiu. Používa pritom návrh podobný kompakcii v stromoch LSM. Každá operácia push vytvorí súbor pack, ktorý Pando označuje ako „L0“ a ktorý predstavuje najmenšiu jednotku dát, akú dokáže preniesť. Pracovné procesy na pozadí postupne zoskupujú tieto súbory pack do pokrytí zahŕňajúcich postupnosť verzií referencií, pričom ich rozsah od úrovne L0 geometricky rastie. Náš monorepozitár dnes používa pokrytia súborov pack až po úroveň L4.
Tieto pokrytia plnia dva účely. Keď sa pokrytie dostatočne naplní, prevezme ho pracovný proces kompakcie a vytvorí z neho jediný súbor pack. Reorganizáciou objektov v súbore pack sa tým zlepší kompresný pomer. Zároveň sa zrýchli vyjednávanie operácie fetch, pretože pokrytia fungujú ako preskakovací zoznam nad verziami referencií. Súbežným prechádzaním indexov na každej úrovni dokáže Pando vybrať množinu balíkov na odoslanie klientovi v logaritmickom čase. Tieto pokrytia súborov pack, či už prebalené, alebo nie, umožňujú efektívnejšie plánovanie operácií fetch a znižujú nároky na priepustnosť aj latenciu systému Pando.
Compaction in Pando
Small packfiles are grouped into larger covers across levels, improving compression and speeding up fetches.
Kompakcia na pozadí spája súbory pack z mnohých verzií referencií a umožňuje efektívnejšie operácie fetch s menším počtom súborov.
Na rozdiel od mnohých serverov Git sa súbory pack uchovávajú na každej úrovni a nezahadzujú sa ani neodstraňujú pri čistení nepotrebných objektov. Balíky na každej úrovni sa priebežne používajú na obsluhu klientov podľa údajov have a want, ktoré klienti uvedú pri operácii fetch. A pri klientoch, ktorí používajú Pando ako API, si čítanie ľubovoľného objektu zo súboru pack zvyčajne vyžaduje načítanie menšieho objemu dát z L0 než z L4, pretože je menej pravdepodobné, že bude skomprimovaný.
Kompakcia v systéme Pando výrazne znižuje celkový objem dát prenášaných klientom a zároveň umožňuje, aby náročnosť plánovania balíkov rástla sublineárne vzhľadom na to, ako veľmi klient zaostáva. To je obzvlášť dôležité pre veľké a rýchlo sa meniace repozitáre, ktoré sa podľa nás stanú novým štandardom vo vývoji softvéru.
Pando sme vytvorili s pomocou Codex: od začiatku projektu po produkčné nasadenie uplynulo len 10 týždňov. Jadro systému Pando navrhol jediný inžinier, implementovali ho tímy agentov a nasadila ho hŕstka inžinierov. Pando začalo obsluhovať produkčnú prevádzku v apríli tohto roka a je súčasťou širšieho systému nástrojov na správu zdrojového kódu používaných v OpenAI.
Pri overovaní návrhu systému Pando sme použili širokú škálu prístupov. Uplatnili sme naše zásady budovania bezpečnejších softvérových základov. Keďže išlo o projekt na zelenej lúke, rozhodli sme sa implementovať Pando v jazyku Rust a bez starších závislostí. Napísali sme sadu testov zhody, aby agenti mohli testovať svoje zmeny voči štandardnému správaniu Gitu. Vyvinuli sme aj harnessy na deterministické simulačné testovanie, ktoré nám umožňujú kontrolovať invarianty a vlastnosti systému. Vďaka nim sme dokázali odhaliť a zmierniť nenápadné súbehové chyby a regresie vo vyjednávaní a plánovaní balíkov.
Na tomto systéme sa podieľalo mnoho modelov. Keď boli dostupné nové modely, využívali sme ich na zlepšovanie výkonu a spoľahlivosti systému Pando. Pomocou GPT‑6 Astra agenti napísali formalizácie v jazykoch Lean a TLA+, ktoré tvorili „teóriu systému Pando“. Použili sme ju na hľadanie a opravu chýb v implementácii a na dokazovanie vlastností systému. Viaceré nepretržité relácie bežali stovky hodín a vyhľadávali a opravovali hraničné prípady v systéme Pando. Nepretržite tiež prevádzkujeme agentov, ktorí pomocou modelov so schopnosťami Daybreak vykonávajú adversariálne bezpečnostné testovanie Gitu a systému Pando.
Pando sme spočiatku nasadili ako proxy, ktoré sprostredkúvalo čítania aj zápisy do nášho existujúceho systému správy zdrojového kódu. Vďaka tomu sme mohli udržiavať Pando a predchádzajúci systém synchronizované a bezpečne presúvať prevádzku na Pando bez výpadkov.
V rámci návrhu vyrovnávacej pamäte dokáže Pando pred preposlaním požiadaviek odstrániť ich duplicity a zlúčiť ich. To nám umožnilo udržať existujúce nástroje v dobrom prevádzkovom stave, kým sme jednotlivé systémy postupne presúvali na Pando. Pri presune ďalších pracovných záťaží na Pando ich operácie push plnili vyrovnávaciu pamäť systému Pando a zvyšovali spoľahlivosť nášho predchádzajúceho backendu.
Aby sme optimalizovali výkon pri používaní systému Pando, začali sme našu verejnú distribúciu Gitu dodávať na počítače vývojárov aj v počítačovej aplikácii ChatGPT. Na postupnú migráciu klientov na Pando sme použili internú obalovú vrstvu nad rozhraním Git CLI. Vytvorili sme mnoho ďalších nástrojov na spracovanie žiadostí o zmeny, kontrolu kódu a API pre automatizácie. Spolu s prevádzkou Gitu sme migrovali stovky integrácií závislých od starších API, webhookov a sťahovania repozitárov.
Vývojárom a tímom pomáhal s prechodom samotný Codex. Vytvorili sme zručnosti na prípravu existujúcich pracovných kópií a na usmernenie agentov pri používaní nového systému. Vývojári tak mohli jednoducho požiadať Codex o migráciu svojich vývojových prostredí a služieb.
Prechod na Pando zvýšil spoľahlivosť zlučovania a znížil medián latencie operácie push o takmer 80 %.
Pando dokáže poskytovať petabajt dát Gitu denne iba s niekoľkými procesorovými jadrami. Pando poskytlo za jedinú hodinu viac než 57 terabajtov balíkov Gitu, pričom vyše 99 % týchto bajtov pochádzalo z lokálnej diskovej vyrovnávacej pamäte.
Opätovné používanie balíkov umožňuje mnohým klientom sťahovať rovnakú históriu repozitára s minimálnymi výpočtovými nárokmi na obslužné uzly. Tento príklad ukazuje jediný repozitár, ale Pando je navrhnuté na podporu omnoho väčšieho počtu. S rastúcim dopytom môžeme nezávisle škálovať doručovanie dát aj vyjednávanie protokolu Git.
Vybudovanie systému Pando na protokole a klientovi Git nám pomohlo odhaliť chyby aj príležitosti na zlepšenie výkonu samotného Gitu.
Pando napríklad poskytuje opakovane použiteľné balíky priamo klientom, takže ich sťahovanie a indexovanie sa stalo kľúčovou súčasťou zabezpečenia rýchlosti a spoľahlivosti systému. Táto práca odhalila hraničné prípady pri súbežnom sťahovaní, čistení po prerušených prenosoch a obnovovaní už dokončených sťahovaní. Opravili sme príslušné časti kódu, pridali kontroly pri prijímaní existujúceho balíka pre operáciu fetch a tieto úpravy poskytli hlavnému projektu.
Potrebovali sme tiež vidieť, na čo sa míňa čas a šírka pásma, preto sme pridali sledovanie sťahovaní cez packfile-uri a počítanie bajtov pri tvorbe balíkov. Vďaka tomu sa ľahšie určuje, či pomalú operáciu fetch zdržiava tvorba balíkov alebo ich prenos.
V zostavení Gitu od OpenAI(otvorí sa v novom okne) môže klient sťahovať a indexovať nezávislé balíky paralelne. Pridali sme koncept „explicitných údajov have“, aby server mohol oznamovať aktuálny koncový commit vetvy oddelene od histórie, ktorú môže odosielajúci klient zo svojho balíka vynechať. V systéme Pando tak môžu klienti pomocou --force-with-lease overiť, že sa vývojová vetva neposunula, a zostaviť jej balík iba voči hlavnej vetve. Ktokoľvek s touto históriou hlavnej vetvy potom môže načítať danú vetvu pomocou rovnakého balíka.
Niekoľko z týchto vylepšení je už v hlavnom projekte Git a mnohé ďalšie v našom verejnom repozitári. Naďalej spolupracujeme s komunitou Gitu, aby boli tieto vylepšenia užitočné aj mimo systému Pando. Túto prácu môžete sledovať v našom verejnom prehľade záplat(otvorí sa v novom okne).
Vďaka prehodnoteniu zaužívaných predstáv o škálovaní repozitárov Git nám Pando umožňuje škálovať náš monorepozitár bez zmeny pracovných postupov v Gite, ktoré už naši inžinieri a agenti používajú. V našom monorepozitári Pando umožnilo vyššiu mieru paralelizmu, zvýšilo spoľahlivosť zlučovania a skrátilo čas potrebný na zverejnenie zmien kódu. Okrem monorepozitára skúmame aj ďalšie možnosti využitia systému Pando v OpenAI vrátane úloh, pri ktorých musí veľký počet agentov spolupracovať na spoločnom repozitári Git.
S rastúcim množstvom softvéru, ktorý inžinieri a agenti vytvárajú, bude na limity správy zdrojového kódu, na ktoré sme narazili my, narážať čoraz viac tímov aj mimo OpenAI. V súčasnosti skúmame možnosti, ako technológiu, na ktorej Pando stojí, sprístupniť našim používateľom.
Pando stavia na dvoch desaťročiach skúseností používateľov a prevádzkovateľov repozitárov Git. Budeme pokračovať vo vývoji systému Pando a zároveň spolupracovať s open-source komunitou Gitu na zdieľaní vylepšení, ktoré prinesú úžitok širšiemu okruhu používateľov Gitu.


