OpenAI

2026 m. lapkričio 2 d.

Inžinerija

„Git“ masto didinimas OpenAI

Kuriame agentų darbo mastui pritaikytą kodo versijų valdymą ir investuojame į „Git“ ekosistemą.

Autoriai: techninės komandos nariai Aaron Friel, Taylor Blau ir Ted Nyman

Įkeliama...

Per pastaruosius metus mūsų bendros kodo saugyklos dydis ir jai tenkantys reikalavimai augo stulbinamu tempu. Buvo aišku, kad turime pertvarkyti kodo versijų valdymo sistemą, kad ji atlaikytų tokį vienu metu atliekamų darbų mastą, kokį įgalina Codex.

Sukūrėme „Pando“ – naują „Git“ saugyklų serverinę sistemą – nestabdydami inžinerinių darbų ir nekeisdami to, kaip mūsų komanda naudoja „Git“. Dabar „Pando“ aptarnauja visą kūrimo veiklą OpenAI bendroje kodo saugykloje. „Pando“ sukurta dviem ambicingiems tikslams pasiekti. Pirmasis – horizontaliai plečiamos rašymo operacijos, optimizuotos vienalaikiam siuntimui į daugelį šakų. Antrasis – kuo didesnis skaitymo pralaidumas, kad klientai galėtų atsisiųsti duomenis visu turimu tinklo pralaidumu.

Šiuos tikslus pasiekėme išlaikydami suderinamumą su „Git“ protokolu ir klientu, todėl tiek inžinieriai, tiek agentai galėjo sklandžiai įtraukti „Pando“ į esamą darbo eigą. Gamybinėje aplinkoje „Pando“ sumažino siuntimo delsos medianą beveik 5 kartus. Dabar ji per dieną pateikia beveik petabaitą „Git“ duomenų.

Čia aptarsime, kodėl kūrėme naują kodo versijų valdymo sistemą ir kodėl nusprendėme išlaikyti suderinamumą su „Git“. Palyginsime savo sistemą su įprastais „Git“ masto didinimo metodais ir paaiškinsime, kuo „Pando“ išskirtinai tinka OpenAI plėtros poreikiams. Taip pat aptarsime tolesnius planus ir tai, kaip toliau prisidėsime prie „Git“, kad ji galėtų aptarnauti agentų darbo mastą.

Kodėl mums reikėjo naujos kodo versijų valdymo sistemos

Codex pakeitė programinės įrangos kūrimo mastą ir tempą OpenAI, leisdama inžinieriams gerokai daugiau darbų atlikti lygiagrečiai. Tai reiškia daugiau šakų, siuntimų ir suliejimų, taip pat daugiau gavimo bei klonavimo operacijų iš kūrimo aplinkų, tyrimų užduočių ir nuolatinio integravimo (CI) sistemų.

Šie poreikiai vienas kitą didina. Dabar vienas inžinierius vienu metu turi kelis CI paruoštus pakeitimus, o kiekvienas siuntimas gali paleisti kelias užduotis, gaunančias tą patį įrašą.

Maždaug nuo praėjusių metų pabaigos mūsų bendrą kodo saugyklą aptarnaujančios sistemos ėmė nebespėti. Gavimo ir siuntimo operacijos lėtėjo, suliejimai nepavykdavo, o patikimumas kartais nesiekė 90 %. Ar pasiekėme pačios „Git“ ribas, ar tik būdo, kuriuo aptarnavome savo bendrą kodo saugyklą? „Git“ išaugo iš bendruomenės, kuri branduolio pataisomis keitėsi el. paštu. Jau daugiau nei du dešimtmečius, keičiantis programinės įrangos kūrimo būdams, kūrėjai pritaiko savo įrankius darbui su „Git“. Kai savo bendroje kodo saugykloje pradėjome susidurti su sunkumais, mums buvo svarbu ir toliau remtis šiuo įdirbiu.

Pamatėme galimybę išlaikyti įprastą darbo eigą ir kartu pertvarkyti serverinę sistemą taip, kad ji lygiagrečiai apdorotų siuntimus į skirtingas šakas ir vienu metu pateiktų tą pačią istoriją daugeliui skaitančių klientų. Kadangi kodo versijų valdymo darbo eigą grindėme „Git“, kliento patobulinimai galėjo būti naudingi ir už OpenAI ribų. Norėjome šiuos patobulinimus iš savo „Git“ kopijos⁠(atsidaro naujame lange) perduoti pagrindiniam projektui ir pasidalyti jais su platesne bendruomene.

„Git“ pritaikymas dideliam mastui

Didinant kodo versijų valdymo sistemos mastą, svarbūs du aspektai: daug rašančių klientų, siunčiančių į skirtingas šakas, ir daug skaitančių klientų, gaunančių tą pačią istoriją. „Pando“ optimizuota abiem atvejams.

Pirma, „Pando“ kiekvienos šakos atnaujinimus koordinuoja atskirai. Nesusijusios šakos gali būti atnaujinamos lygiagrečiai, o šaką gaunančiam klientui pateikiama tai revizijai reikalinga istorija. Kompromisas toks: kelias šakas skaitantis klientas gali matyti jų būsenas skirtingais laiko momentais.

Antra, „Pando“ skirtingoms gavimo operacijoms pakartotinai naudoja paketų failus („Git“ formatą keliems objektams kartu glaudinti), užuot kūrusi atskirą failą kiekvienam klientui. Klientas gali atsisiųsti dalį jau turimų objektų, tačiau serveris tuos pačius baitus gali pateikti daugeliui klientų, atlikdamas nedaug skaičiavimų.

Šie sprendimai leidžia didinti rašymo į skirtingas šakas mastą, o skaitymo užklausas aptarnauti iš podėlių. Kad suprastume, kaip tai dera tarpusavyje, pirmiausia pažvelkime, kaip „Git“ saugo ir perduoda istoriją.

„Git“ objektų saugojimo pagrindai

„Git“ turi palaikyti dviejų saugyklos dalių – objektų ir nuorodų – sinchroniškumą.

„Git“ saugykla failų būsenas ir jas siejančią istoriją saugo kaip objektų grafą. Objektas gali būti dvejetainis objektas, medis, pakeitimų įrašas arba žyma. Dvejetainiai objektai saugo atskirų failų turinį, o medžiai susieja vardus su dvejetainiais objektais ar kitais medžiais ir taip sudaro katalogų struktūrą. Pakeitimų įrašai nurodo šakninį medį ir nulį ar daugiau pirminių įrašų, taip sudarydami saugyklos istoriją.

Objektai identifikuojami pagal jų tipo ir turinio maišos reikšmę. Objektai taip pat yra nekintami: išsaugant pakeistą failą sukuriamas naujas dvejetainis objektas, o nepakeistiems failams ir katalogams galima pakartotinai naudoti esamus objektus.

Šakos ir žymos yra nuorodos (angl. „refs“), nurodančios pakeitimų įrašus. Užfiksuojant pakeitimus šakoje sukuriamas naujas įrašas, o šakos nuoroda atnaujinama, kad jį nurodytų.

Šaka refs/heads/main nurodo įrašą C2, kurio pirminis įrašas yra C1. C1 nurodo šakninį medį T1, kuriame parser.py nurodo dvejetainį objektą B1. C2 nurodo šakninį medį T2, kuriame parser.py nurodo dvejetainį objektą B2. Abiejuose šakniniuose medžiuose docs/ nurodo tą patį medį T3. Jo README.md elementas nurodo dvejetainį objektą B3, o usage.md – dvejetainį objektą B4.

„Git“ objektai nurodo vieni kitus pagal ID: įrašai – medžius, o medžiai – kitus medžius ir dvejetainius objektus. Šaka refs/heads/main nurodo įrašą C2, kurio pirminis įrašas yra C1. C1 nurodo šakninį medį T1, kuriame parser.py nurodo dvejetainį objektą B1. C2 nurodo šakninį medį T2, kuriame parser.py nurodo dvejetainį objektą B2. Abiejuose šakniniuose medžiuose docs/ nurodo tą patį medį T3. Jo README.md elementas nurodo dvejetainį objektą B3, o usage.md – dvejetainį objektą B4.

„Git“ saugo objektus „atskirai“ arba „supakuotus“. Nesupakuoti objektai saugomi atskiruose failuose, identifikuojamuose pagal objekto ID. Supakuoti objektai saugomi kartu paketo faile, todėl panašius objektus galima saugoti kaip jų tarpusavio skirtumus.

Kataloge $GIT_DIR/objects matomos dvi parser.py versijos. Nesupakuotas dvejetainis objektas A turi parse(), o objektas B – parse() ir parse_file(). Paketo faile A saugomas visas, o B pateikiamas kaip instrukcijos nukopijuoti visą A ir pridėti parse_file(). Pridedamas *.idx failas pagal objekto ID nurodo abiejų dvejetainių objektų vietas paketo faile.

„Git“ gali saugoti dvejetainius objektus atskiruose failuose arba supakuoti juos kartu, vieną objektą užkoduodama kaip skirtumą nuo kito.

„Git“ sistemoje objektas yra pasiekiamas iš įrašo, jei „Git“ gali jį rasti sekdama nuorodas iš to įrašo per jo pirminius įrašus, medžius ir dvejetainius objektus. Gavimo metu klientas praneša serveriui, kurių įrašų nori ir kuriuos jau turi. Serveris randa iš pageidaujamų įrašų pasiekiamus objektus, atmeta tuos, kuriuos, jo žiniomis, klientas jau turi, ir likusius siunčia paketo failu – tuo pačiu formatu, kurį „Git“ naudoja diske. Visiškas klonavimas pradedamas neturint jokių objektų, todėl reikia visų iš klonuojamos istorijos pasiekiamų objektų.

Siuntimas perduoda objektus priešinga kryptimi ir prašo serverio atnaujinti vieną ar kelias nuorodas. Klientas naudoja serverio paskelbtų nuorodų rinkinį, kad nesiųstų objektų, kuriuos serveris jau turi. Todėl siunčiamas paketas gali priklausyti nuo esamų objektų: vien jo gavimo nepakanka naujai istorijai atkurti.

Replikuoti „Git“ saugyklą reiškia užtikrinti, kad objektai ir nuorodos būtų pasiekiami keliuose serveriuose. Kad replika galėtų pateikti naują nuorodos reikšmę, ji turi gebėti pateikti iš tos reikšmės pasiekiamus objektus. Paslauga taip pat turi koordinuoti konkuruojančius nuorodų atnaujinimus, t. y. siuntimus, kurie bando tą pačią šaką iš tos pačios senos viršūnės perkelti į skirtingus naujus įrašus. Sistemose, kurios visoms saugyklos rašymo operacijoms nustato vieną bendrą tvarką, siuntimams į nesusijusias šakas gali tekti laukti vieniems kitų.

Esami paskirstytųjų „Git“ paslaugų modeliai

Projektuodami „Pando“, įvertinome įprastus „Git“ kodo versijų valdymo masto didinimo metodus: vienas koordinuoja nuorodų atnaujinimus tarp saugyklos replikų naudodamas trijų fazių patvirtinimą (3PC – patvirtinimo keliuose mazguose koordinavimo protokolą), o kitas nustato atnaujinimų tvarką per bendrą išankstinio rašymo žurnalą (WAL – pakeitimų žurnalą, pildomą prieš juos pritaikant) dvejetainių objektų saugykloje.

Trijų fazių patvirtinimas (3PC)

Mūsų modeliuotame trijų fazių patvirtinimo sprendime „Git“ paslauga keliose replikose saugo visą saugyklos kopiją. Kai bet kuris serveris gauna siuntimą, gautas paketo failas nukopijuojamas ir lygiagrečiai siunčiamas visoms replikoms. Kad siuntimo rezultatas būtų paskelbtas, dauguma replikų turi priimti nuorodos atnaujinimą. Šį procesą valdantis serveris yra koordinatorius. Jam ir kiekvienai kitai replikai reikia apsikeisti keliais pranešimų raundais.

Kad priimtų nuorodos atnaujinimą, kiekviena replika užrakina savo nuorodos kopiją ir patikrina, ar ji vis dar nurodo įrašą, kurio tikisi klientas. Tada koordinatorius siunčia išankstinio patvirtinimo pranešimą sutikusioms replikoms. Kai dauguma patvirtina gavusios šį pranešimą, koordinatorius nurodo joms atlikti atnaujinimą. Tuomet bet kuri atnaujinta replika gali paskelbti naują šakos viršūnę iš savo vietinės saugyklos kopijos.

Visos saugyklos replikos leidžia skaitymo užklausas aptarnauti vietoje ir užtikrina, kad klientai gautų nuoseklią visų nuorodų būseną, nesvarbu, kurią repliką pasirenka. Dėl to nuorodos atnaujinamos po vieną, o kiekvienam siuntimui reikia kelių apsikeitimo pranešimais ciklų, kad atnaujinimas būtų suderintas tarp replikų.

Laukiant kvorumo, siuntimo trukmė tampa priklausoma nuo replikų diskų našumo ir ryšio delsos. Augant rašymo apimčiai, replikos turi spėti apdoroti ir gaunamus paketus, ir skaitymo srautą.

Išankstinio rašymo žurnalas (WAL)

Mūsų modeliuotame WAL sprendime „Git“ paslauga naudoja bendrą manifestą dvejetainių objektų saugykloje visos saugyklos atnaujinimų tvarkai nustatyti. Gavęs siuntimą, serveris įkelia paketo failą į dvejetainių objektų saugyklą ir savo vietinėje saugyklos kopijoje paruošia nuorodos atnaujinimą. Norėdamas paskelbti siuntimo rezultatą, jis dvejetainių objektų saugykloje saugomą bendrą manifestą papildo įrašu, kuris fiksuoja nuorodos atnaujinimą ir nurodo paketo failą. Tada „Git“ serveris atnaujina manifestą palyginimo ir sukeitimo operacija. Ji arba atomiškai pakeičia manifestą, arba atmeta pakeitimą. Jei per tą laiką manifestą atnaujino kitas rašytuvas, serveris perskaito naujus įrašus, patikrina, ar jo nuorodos atnaujinimas vis dar galioja, ir bando dar kartą naudodamas jau įkeltą paketo failą.

Rašantys klientai gali įkelti paketų failus lygiagrečiai, tačiau net siunčiant į nesusijusias šakas reikia atnaujinti tą patį manifestą. Sėkmingos palyginimo ir sukeitimo operacijos užklausos bei atsakymo trukmė riboja, kaip dažnai saugykla gali paskelbti naują manifestą. Mažos delsos dvejetainių objektų saugykla leidžia šį žingsnį atlikti greitai. Grupuojant atnaujinimus, vienu manifestu galima vienu metu atnaujinti kelias nuorodas, tačiau atnaujinimai turi laukti eilėje ir delsa didėja.

Dvejetainių objektų saugykla gali priimti paketų failus lygiagrečiai, tačiau bendro manifesto atnaujinimai vis tiek konkuruoja tarpusavyje. Vien dvejetainių objektų saugyklos pajėgumo didinimas šio ribojančio veiksnio nepašalina. Kiekviena replika vis tiek turi perskaityti ir indeksuoti paketus iš dvejetainių objektų saugyklos, kad galėtų juos pateikti vietoje. Jei nauji rašymai vyksta greičiau nei šis darbas, replikos atsilieka, o klientai ilgiau laukia, kol galės gauti naujai atsiųstus įrašus.

„Pando“ architektūra

Taip prieiname prie „Pando“. „Pando“ – tai nauja „Git“ saugyklų aptarnavimo architektūra, skirta didinti mastą taip, kad daugybė klientų galėtų siųsti pakeitimus vienu metu. Štai kaip ji veikia:

Apribojimų permąstymas

Dar prieš kurdami „Pando“ nujautėme, kad galime patenkinti savo poreikius neatsisakydami „Git“, tačiau teko atsisakyti kai kurių dalykų, kuriuos dauguma „Git“ sistemų laiko būtinais:

  • Visos saugyklos nuoseklumas: norint pateikti nuoseklią visų nuorodų būseną konkrečiu laiko momentu, reikia koordinuoti veiksmus visoje saugykloje. Sistemoje, kuri visų nuorodų atnaujinimus išrikiuoja į bendrą seką, siuntimo į nesusijusias šakas operacijos konkuruoja dėl vieno koordinavimo taško.

  • Atskiri paketų failai kiekvienam klientui: jei du klientai su skirtingomis vietinėmis istorijomis gauna tą pačią šaką, serveriui gali tekti kiekvienam parengti skirtingą paketą, kad nesiųstų jau turimų objektų. Tokiems atsakymams parengti reikia procesoriaus ir disko įvesties bei išvesties išteklių, todėl sunku aptarnauti daug skaitančių klientų.

Todėl „Pando“ taiko silpnesnį nuoseklumo modelį (priežastinį nuoseklumą), kad galėtų didinti rašymo operacijų mastą, o skaitymo operacijų mastą didina pakartotinai naudodama paketų failus.

Priežastinis nuoseklumas (CC)

Kai klientas siunčia pakeitimus į šaką, „Pando“ gautą paketo failą išsaugo kaip nekintamą dvejetainį objektą dvejetainių objektų saugykloje. Kaskart pasikeitus šakos viršūnei, „Pando“ metaduomenų saugykloje išsaugo nuorodos versiją: naują viršūnę, ją pateikusį paketą ir visas ankstesnes nuorodų versijas, nuo kurių priklauso paketas. Tiek dvejetainių objektų, tiek metaduomenų saugyklas galima plėsti horizontaliai.

Norint gauti įrašą ar nuorodą iš „Pando“, klientui reikia perduoti nuorodų versijų rinkinį (paketų failus), taip atkuriant kliento, sukūrusio gaunamą nuorodos versiją, būseną. Taip užtikrinamas silpnesnis visos saugyklos nuoseklumas, tačiau atskirų nuorodų atnaujinimai išlieka serializuojami. Klientų operacijos konfliktuoja tik tada, kai jie vienu metu siunčia pakeitimus į tą pačią nuorodą. Kelias šakas skaitantis klientas negali manyti, kad gauti duomenys atspindi vieną bendrą saugyklos laiko momentą.

Kairėje trijuose iš eilės einančiuose pagrindinės šakos paketuose yra įrašai A, B ir C; D ir E; ir F. Dešinėje pagrindinė ir teminė šakos išsiskiria ties C. Pagrindinė šaka pasiekia G, o teminė – D, E ir F. Naujausiame teminės šakos pakete yra D, E ir F. Brūkšninis kontūras žymi, kad D ir E yra ir ankstesniame teminės šakos pakete. Mėlyna rodyklė žymi naujo paketo priklausomybę nuo pagrindinės šakos ties G. F pirminis „Git“ įrašas yra E.

Siunčiant į pagrindinę šaką pridedamas prieauginis paketas su vienu ar keliais pakeitimų įrašais. Kiekvieną kartą siunčiant į šalutinę šaką vėl įtraukiami ankstesni jos įrašai, todėl klientas, turintis paskelbtą pagrindinės šakos versiją, gali gauti šalutinę šaką vienu paketu.

Tai yra priežastinio nuoseklumo forma. Norėdamas gauti nuorodą, skaitantis klientas gauna ją sukūrusio rašančio kliento istoriją. Siuntimo metu „Pando“ kontroliuoja, dėl kokios bendros istorijos susitaria klientas ir serveris, taip užtikrindama, kad klientų kuriamus paketų failus būtų lengviau naudoti pakartotinai.

Todėl per „Git“ operacijas „Pando“ nepateikia visų nuorodų būsenos vienu laiko momentu ir neužtikrina bendros nuorodų atnaujinimų tvarkos klonavimo ar gavimo metu. Tarkime, derinant visiško klonavimo operaciją atnaujinamos abi šakos – foo ir bar. Derinimo metu vienas klientas gali matyti foo 1 versiją ir bar 2 versiją, o kitas – foo 2 versiją ir bar 1 versiją. Praktikoje naudotojams ir agentams bendra nuorodų atnaujinimų tvarka nesvarbi: jie remiasi vienos nuorodos atnaujinimų tvarka.

Metodų palyginimas

Šioje trijų nuoseklumo modelių simuliacijoje sukonfigūruojame 1 000 klientų: kiekvienas pasirenka atskirą šaką ir pakartotinai siunčia unikalius įrašus. Matuojame bendrą faktinę trukmę ir delsą nuo siuntimo iki gavimo. Pastarasis rodiklis svarbus darbo krūviams, nes parodo viso ciklo trukmę, kai naudotojai ir agentai bendradarbiauja arba naudoja kitas versijų valdymu paremtas sistemas. Simuliacijoje modeliuojama įvairaus lygio infrastruktūra – nuo įprastų iki aukščiausios klasės virtualiųjų mašinų ir dvejetainių objektų saugojimo sistemų. Šioje simuliacijoje trijų fazių patvirtinimo sistemai sušvelninome visos saugyklos nuoseklumo reikalavimą.

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.

Paleiskite šią simuliaciją ir palyginkite „Pando“ su dviem kitais metodais. Pasirinkite infrastruktūrą – nuo įprastos iki aukščiausios klasės aparatinės įrangos, klientų skaičių ir kitas nuostatas, kad pamatytumėte skirtumus.

Esant apkrovai, modeliuojamos trijų fazių patvirtinimo sistemos rašymo delsa gerokai išauga, nes diskai pasiekia savo našumo ribą. Išankstinio rašymo žurnalo sistemose dėl tos pačios priežasties smarkiai išauga skaitymo delsa – serveriai nebespėja skaityti žurnalo. Tiek realiomis sąlygomis atliktuose bandymuose su mūsų bendra kodo saugykla, tiek šioje simuliacijoje „Pando“ leidžia daugeliui tūkstančių klientų vienu metu siųsti pakeitimus, nesumažindama našumo esant apkrovai.

Apdorodama siuntimą, „Pando“ tikrina gaunamą paketo failą ir tuo pat metu srautu perduoda jį į dvejetainių objektų saugyklą, neišpakuodama į vietinę saugyklą. Klientui pradėjus gavimo operaciją, „Pando“ parenka jam reikalingus išsaugotus paketus, o klientas juos atsisiunčia. Nė vienam serveriui nereikia pritaikyti paketų failų vietinei saugyklai ar atlikti brangių išpakavimo ir pakavimo operacijų. Kadangi serveriai daugiausia derina operacijas ir parenka paketų failus, „Pando“ gali veikti įprastoje aparatinėje įrangoje.

Todėl „Pando“ mastą galima didinti iki klientų, tinklo tarp klientų ir „Pando“ bei dvejetainių objektų saugojimo paslaugos pajėgumo ribų. Turint pakankamai pajėgumų, nepriklausomose šakose dirbantys klientai gali perduoti paketus tokiu greičiu, kokį leidžia jų pačių ryšys. Norėdami tokiu greičiu aptarnauti daugiau klientų, galime pridėti „Pando“ serverių ir kartu plėsti metaduomenų bei dvejetainių objektų saugyklas.

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

Agentas vietoje redaguoja failus, sukuria pakeitimų įrašą ir paleidžia git push. Ši komanda sukuria pakeitimų paketo failą šakai atnaujinti. „Pando“ gauna paketą ir srautu perduoda jį į dvejetainių objektų saugyklą. Vienu dekodavimo praėjimu patikrinusi gautą siuntimą, „Pando“ kelių raktų transakcija išsaugo nuorodos versiją mūsų metaduomenų saugykloje ir atomiškai atnaujina nuorodos rodyklę. Taip „Pando“ užtikrina nuoseklumą pagal principą „perskaitai tai, ką pats įrašei“. Visai šiai rašymo operacijai reikia O(1)O(1) metaduomenų skaitymo ir rašymo operacijų.

Kai kitas klientas gauna šaką, „Pando“ parenka išsaugotų paketų rinkinį pagal kliento jau turimą istoriją. Šiai operacijai reikia O(log⁡Δ)O(\log \Delta) metaduomenų skaitymo operacijų, kur Δ\Delta – trumpiausias atstumas nuorodų versijomis tarp to, ką klientas turi, ir to, ką nori gauti. Serveris grąžina paketų failų URI, o klientas juos atsisiunčia per „Express“ paslaugą, saugančią paketų failus regioniniuose podėliuose.

Šis sprendimas remiasi trimis pagrindinėmis „Pando“ architektūros paslaugomis: siuntimo, gavimo ir tankinimo.

Siuntimo masto didinimas

Kai klientas siunčia pakeitimus, įprastas „Git“ serveris elgiasi tarsi pokerio žaidėjas, atverčiantis visas savo kortas: atskleidžia klientui visas turimas šakas. Tada klientas surenka įrašus, medžius ir dvejetainius objektus į paketo failą, į kurį neįtraukia objektų, kuriuos, jo žiniomis, serveris jau turi.

Paskelbus daugiau nuorodų, siunčiamų duomenų gali būti mažiau. Tačiau tokį paketą gali būti sunkiau panaudoti pakartotinai, nes kitam klientui gali trūkti objektų, kurių siuntėjas neįtraukė.

„Pando“ renkasi kitą kelią. Užuot atvertusi visas kortas, ji klientui paskelbia tik atrinktas šakas. Taip daroma dėl dviejų priežasčių:

  1. Pateikti nuoseklią visų nuorodų būseną kainuoja daug išteklių. Kai yra milijonai šakų, vien skelbiamų nuorodų sąrašas gali viršyti 100 MiB. Tam kiekvienas serveris turi arba palaikyti nuolat atnaujinamą atitikčių lentelę, o tai reikalauja daug koordinavimo, arba vykdyti duomenų bazės užklausą, kurios sąnaudos auga kartu su nuorodų skaičiumi.

  2. Klientui pateikus tik dalį nuorodų, jo sugeneruotas paketas priklausys tik nuo objektų, kurių serveris aiškiai leido neįtraukti. Sekdama priklausomybes, „Pando“ gali per vieną siuntimą kliento sukurtą paketo failą panaudoti pakartotinai – pateikti jį kaip atsakymą į vėlesnę gavimo užklausą.

Kuo daugiau nuorodų paskelbia „Git“ serveris, tuo daugiau šaltinių siunčiantis klientas gali naudoti spręsdamas, kurių objektų neįtraukti, ir tuo mažesnius paketus gali sukurti. Tačiau norėdamas pakartotinai naudoti šiuos paketų failus, serveris turi galėti įrodyti, kad gaunantys klientai turi visas priklausomybes. Kitaip paketo faile gali trūkti klientui reikalingų objektų.

Kita vertus, jei „Git“ serveris nepaskelbtų nė vienos nuorodos, klientas sukurtų paketo failą su visa saugyklos istorija. Tokį paketo failą galėtų naudoti bet kuris kitas klientas, tačiau kaina būtų didelė: bet kurios šakos gavimas iš esmės prilygtų visiškam klonavimui.

Kreivė rodo, kaip skelbiamos nuorodos veikia paketo failo dydį: nuo visos istorijos, kai neskelbiama nė viena nuoroda, per pagrindinės šakos skelbimą iki vien naujų objektų, kai skelbiamos visos nuorodos.

Paskelbus daugiau nuorodų, paketų failai būna mažesni. Didžiąją dalį dydžio sumažėjimo galima pasiekti paskelbus vos vieną nuorodą.

Pagal numatytąją nuostatą, kai klientas siunčia naują funkcijos šaką, „Pando“ kaip pagrindą paskelbia tik paskirtą pagrindinę šaką. Taip skelbiamo sąrašo dydis nesikeičia, nors saugykloje daugėja šakų, tačiau tenka daryti kompromisų. Kai inžinierius daug kartų siunčia į vieną šaką arba kuria vieną funkcijos šaką kitos pagrindu, klientas turi iš naujo siųsti viską, ko nėra pagrindinėje šakoje.

Todėl vėlesni šakos siuntimai būna didesni, gali turėti perteklinių objektų ir sunaudoja daugiau kliento tinklo pralaidumo. Mūsų bendroje kodo saugykloje taip sukurtų paketų failų dydžio mediana nesiekė 20 KiB, o 99-asis procentilis – 3 MiB. Mums tai buvo pagrįstas kompromisas, nes paskelbtą pagrindinės šakos istoriją jau turintis klientas gali gauti šaką tuo vienu paketo failu.

Parinkusi klientui skelbiamas nuorodas, „Pando“ priima siuntimą ir atnaujina metaduomenų saugyklą. „Pando“ padalija metaduomenų atnaujinimą į sinchroninę ir asinchroninę dalis. Sinchroninis metaduomenų atnaujinimas užtikrina, kad klientai galėtų iš karto gauti naujai atnaujintą įrašą. Sukuriama nauja nuorodos versija ir į ją nukreipiama nuorodos rodyklė. Išsaugoma nauja viršūnė, derinant paketo failą naudotos priklausomybės ir nuoroda į atsiųstą paketo failą.

Ta pačia transakcija taip pat užregistruojami asinchroniniai darbai, pavyzdžiui, tankinimo planavimas. Kai siuntimas labai didelis, sinchroniškai išsaugoma tik dalis įrašų. Taip metaduomenų saugykloje pakanka pastovaus skaitymo ir rašymo operacijų skaičiaus, nepriklausomai nuo siuntimo dydžio.

Svarbu tai, kad „Pando“ serveriai gali nepriklausomai priimti siuntimus į nesusijusias nuorodas. Vienintelis raktas, dėl kurio konkuruojama, yra nuorodos rodyklė. Ją apsaugome atomine operacija, panašia į palyginimą ir sukeitimą. Rašymo pralaidumą galime didinti pridėdami „Pando“ serverių, metaduomenų saugyklos plėtimo vienetų ar dvejetainių objektų saugyklų.

Gavimas ir klonavimas visu tinklo greičiu

Atlikdamas gavimo operaciją, klientas pateikia serveriui du įrašų ID rinkinius, protokole vadinamus have ir want. Įprastas „Git“ serveris apskaičiuoja skirtumą tarp objektų, pasiekiamų iš pageidaujamų įrašų, ir objektų, pasiekiamų iš turimų įrašų, suspaudžia trūkstamus objektus ir siunčia rezultatą klientui. Nors atvirojo kodo „Git“ serveris turi įvairių vietinių indeksų šiai operacijai paspartinti, tokių paketų failų sudarymas gali kainuoti daug skaičiavimo išteklių, o podėliuose juos paprastai sunku panaudoti pakartotinai.

„Pando“ vietoj to naudoja siuntimo metu išsaugotas įrašų atitiktis: pagal kliento have ir want rinkinius nustato nuorodų versijas, kuriose yra tie įrašai. Kiekviena iš šių nuorodų versijų susieta su anksčiau „Pando“ gautu paketo failu. Remdamasis tuo, serveris nustato, ko reikia klientui, ir parenka paketų failus, apimančius reikalingų objektų rinkinį.

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“ susieja „Git“ protokolo turimų ir pageidaujamų įrašų rinkinius su nuorodų versijų seka. Norint gauti šalutinę šaką, reikia gauti ir jos priklausomybes pagrindinėje šakoje.

„Git“ protokolas leidžia klonavimo ar gavimo HTTP atsakyme siųsti tik vieną paketo failą. Tačiau nepakankamai vertinama „Git“ protokolo funkcija packfile-uri leidžia serveriui grąžinti nuorodas papildomiems paketams atsisiųsti atskirai. „Pando“ naudoja tik šį būdą, o pačiame atsakyme perduoda tuščią paketo failą. Kad ši funkcija veiktų geriau, OpenAI pagrindiniam projektui pateikė nemažai packfile-uri patobulinimų.

OpenAI sistemos veikia daugelyje geografinių regionų, todėl kiekviename jų palaikome „Pando“ klasterius su vietiniais podėliais. Paketų pristatymo paslauga „Express“ podėlyje saugo dvejetainių objektų saugyklos duomenis, o atskira paslauga – metaduomenų saugyklos duomenis. Šie podėliai leidžia „Pando“ įrašo ID užklausas ar specialų nuorodos nuskaitymą aptarnauti vien iš tame klasteryje podėlyje saugomos būsenos. Paketų failų užklausas į „Express“ serverius nukreipiame nuosekliąja maiša, o podėlio pataikymų rodiklis viršija 99 %.

Ši podėlių sistema užtikrina itin didelį pralaidumą ir gali visiškai išnaudoti tinklo tarp klientų ir „Pando“ pajėgumą. Naudodami šiuos paketų failų URI ir vietinius podėlius patvirtinome, kad net su standartinėmis debesijos teikėjo virtualiosiomis mašinomis klonavimo ir gavimo operacijos visiškai išnaudoja 10 Gb/s tinklo jungtis.

Tankinimas ir laikas iki pirmojo baito

Daugumai „Git“ serverių būtina perpakuoti duomenis ir šalinti nebenaudojamus objektus, kad auganti saugykla išlaikytų našumą. Abu veiksmai atlaisvina vietos, tačiau reikalauja daug skaičiavimo ir įvesties bei išvesties išteklių.

Sparčiai kintančioje saugykloje, tokioje kaip OpenAI bendra kodo saugykla, nepraktiška siųsti atskirą paketo failo URI kiekvienam siuntimui, įvykusiam nuo paskutinio gavimo. Norėdamas atnaujinti savo pagrindinę šaką, klientas kasdien turėtų atsisiųsti dešimtis tūkstančių paketų failų.

Todėl „Pando“ fone sujungia paketus ir taip mažina šias papildomas sąnaudas. Šis sprendimas panašus į tankinimą žurnalo struktūros suliejimo medžiuose. Kiekvienas siuntimas sukuria paketo failą, kurį „Pando“ vadina „L0“. Tai mažiausias duomenų vienetas, kurį „Pando“ gali perduoti. Laikui bėgant foniniai procesai sujungia šiuos paketų failus į aprėpties rinkinius, apimančius nuorodų versijų seką ir geometriškai didėjančius nuo L0 lygio. Šiuo metu mūsų bendra kodo saugykla naudoja iki L4 lygio paketų failų aprėpties rinkinius.

Šie aprėpties rinkiniai atlieka dvi funkcijas. Kai aprėpties rinkinys pakankamai užsipildo, tankinimo procesas jį perima ir sukuria vieną paketo failą. Pertvarkius objektus paketo faile, pagerėja glaudinimo santykis. Be to, šie rinkiniai veikia kaip nuorodų versijų šuolių sąrašas, todėl paspartėja gavimo derinimas. Vienu metu peržiūrėdama kiekvieno lygio indeksus, „Pando“ gali per logaritminį laiką parinkti klientui siųstinų paketų rinkinį. Šie paketų failų aprėpties rinkiniai, perpakuoti ar ne, leidžia efektyviau planuoti gavimą ir mažina „Pando“ keliamus pralaidumo bei delsos reikalavimus.

Compaction in Pando

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

Compaction in progressPublished cover

Tankinant fone sujungiami daugelio nuorodų versijų paketų failai, todėl gavimo operacijos tampa efektyvesnės ir joms reikia mažiau failų.

Kitaip nei daugelyje „Git“ serverių, paketų failai išsaugomi kiekviename lygyje: jie neatmetami ir nešalinami kaip nebenaudojami objektai. Visų lygių paketai nuolat naudojami klientams aptarnauti pagal jų gavimo užklausose pateikiamus have ir want rinkinius. O klientams, naudojantiems „Pando“ kaip API, norint perskaityti bet kurį objektą iš paketo failo paprastai reikia nuskaityti mažiau duomenų iš L0 nei iš L4, nes mažesnė tikimybė, kad objektas bus suglaudintas.

„Pando“ tankinimas gerokai sumažina bendrą klientams perduodamų duomenų kiekį ir leidžia paketų planavimo sąnaudoms augti lėčiau nei tiesiškai, didėjant kliento atsilikimui. Tai ypač svarbu didelėms ir sparčiai kintančioms saugykloms, kurios, mūsų manymu, taps įprastos kuriant programinę įrangą.

„Pando“ kūrimas

Naudodami Codex, „Pando“ sukūrėme vos per 10 savaičių – nuo projekto pradžios iki gamybinės aplinkos. „Pando“ branduolį suprojektavo vienas inžinierius, įgyvendino agentų komandos, o įdiegė keli inžinieriai. „Pando“ pradėjo aptarnauti gamybinį srautą šių metų balandį. Ji yra didesnės OpenAI naudojamų kodo versijų valdymo įrankių sistemos dalis.

Tikrindami „Pando“ projektinius sprendimus taikėme įvairius metodus. Vadovavomės savo saugesnių programinės įrangos pagrindų kūrimo⁠ principais. Kadangi projektą pradėjome nuo nulio, pasirinkome „Pando“ įgyvendinti „Rust“ kalba ir be pasenusių priklausomybių. Parašėme atitikties testų rinkinį, kad agentai galėtų patikrinti, ar jų pakeitimai atitinka standartinį „Git“ veikimą. Taip pat sukūrėme deterministinių simuliacinių testų infrastruktūrą, leidžiančią tikrinti sistemos invariantus ir savybes. Ji padėjo aptikti ir suvaldyti sunkiai pastebimas lenktyniavimo sąlygas bei regresijas derinant operacijas ir planuojant paketus.

Prie šios sistemos kūrimo prisidėjo daug modelių; pasirodžius naujiems, naudojome juos „Pando“ našumui ir patikimumui gerinti. Naudodami GPT‑6 Astra, agentai rašė „Lean“ ir TLA+ kalbomis, kad formalizuotų „Pando teoriją“. Ja rėmėmės ieškodami ir taisydami įgyvendinimo klaidas bei įrodydami sistemos savybes. Kelios nepertraukiamos sesijos truko šimtus valandų – per jas buvo ieškoma ir taisoma „Pando“ ribinių atvejų. Taip pat visą parą naudojame agentus su „Daybreak“ galimybes turinčiais modeliais, kad imituodami atakas tikrintų „Git“ ir „Pando“ saugumą.

„Pando“ diegimas

Iš pradžių „Pando“ įdiegėme kaip tarpinį serverį, perduodantį skaitymo ir rašymo operacijas mūsų esamai kodo versijų valdymo sistemai. Tai leido sinchronizuoti „Pando“ su ankstesne sistema ir saugiai perkelti srautą į „Pando“ be prastovų.

„Pando“ podėlio architektūra taip pat leidžia prieš persiunčiant užklausas pašalinti jų dublikatus ir jas sujungti. Todėl esami įrankiai veikė sklandžiai, kol sistemas į „Pando“ perkėlėme po vieną. Į „Pando“ perkeliant vis daugiau darbo krūvių, jų siuntimai pildė „Pando“ podėlį ir didino ankstesnės serverinės sistemos patikimumą.

Norėdami optimizuoti darbą su „Pando“, pradėjome diegti savo viešą „Git“ distribuciją kūrėjų kompiuteriuose ir kartu su ChatGPT darbalaukio programėle. Klientus į „Pando“ perkėlėme etapais, naudodami vidinį „Git“ komandų eilutės sąsajos apvalkalą. Sukūrėme daug kitų įrankių pakeitimų užklausoms ir kodo peržiūrai tvarkyti bei API automatizavimui. Kartu su „Git“ srautu perkėlėme šimtus integracijų, kurios priklausė nuo senųjų API, žiniatinklio pranešimų kablių ir saugyklų atsisiuntimų.

Pats Codex padėjo kūrėjams ir komandoms pereiti prie naujos sistemos. Sukūrėme įgūdžius esamoms darbinėms kopijoms paruošti ir agentams mokyti naudotis naująja sistema. Todėl kūrėjams užteko paprašyti Codex perkelti jų kūrimo aplinkas ir paslaugas.

Perėjus prie „Pando“, suliejimas tapo patikimesnis, o siuntimo delsos mediana sumažėjo beveik 80 %.

„Pando“ gali pateikti petabaitą „Git“ duomenų per dieną, naudodama vos kelis procesoriaus branduolius. Per vieną valandą „Pando“ yra pateikusi daugiau nei 57 terabaitus „Git“ paketų, o daugiau nei 99 % tų baitų buvo paimti iš vietinio disko podėlio.

Pakartotinai naudojant paketus, daug klientų gali atsisiųsti tą pačią saugyklos istoriją, o aptarnaujantiems mazgams tereikia nedaug skaičiavimų. Šiame pavyzdyje rodoma viena saugykla, tačiau „Pando“ sukurta aptarnauti gerokai daugiau. Augant poreikiui, duomenų pateikimo ir „Git“ operacijų derinimo mastą galime didinti nepriklausomai.

Indėlis į „Git“ projektą

Kurdami „Pando“ „Git“ protokolo ir kliento pagrindu, aptikome klaidų ir galimybių pagerinti pačios „Git“ našumą.

Pavyzdžiui, „Pando“ tiesiogiai klientams pateikia pakartotinai naudojamus paketus, todėl jų atsisiuntimas ir indeksavimas tapo viena svarbiausių spartaus bei patikimo sistemos veikimo dalių. Šis darbas atskleidė ribinius atvejus, susijusius su vienalaikiais atsisiuntimais, išvalymu po nutrauktų perdavimų ir jau užbaigtų atsisiuntimų tęsimu. Ištaisėme šias kodo šakas, pridėjome patikrų priimant esamą paketą gavimo operacijai ir pateikėme šiuos pakeitimus pagrindiniam projektui.

Taip pat reikėjo suprasti, kur sunaudojamas laikas ir pralaidumas, todėl pridėjome packfile-uri atsisiuntimų sekimą bei paketų generavimo baitų skaitiklius. Taip tapo lengviau nustatyti, ar lėta gavimo operacija užtrunka generuodama paketus, ar juos perduodama.

OpenAI „Git“ versijoje⁠(atsidaro naujame lange) klientas gali nepriklausomus paketus atsisiųsti ir indeksuoti lygiagrečiai. Įtraukėme „aiškiai nurodytų turimų įrašų“ sąvoką, kad serveris galėtų paskelbti dabartinę šakos viršūnę atskirai nuo istorijos, kurios siunčiančiam klientui leidžiama neįtraukti į paketą. „Pando“ sistemoje tai leidžia klientams su --force-with-lease patikrinti, ar funkcijos šaka nepasikeitė, ir kurti paketą remiantis vien pagrindine šaka. Tada bet kuris klientas, turintis tą pagrindinės šakos istoriją, gali gauti šaką naudodamas tą patį paketą.

Keli iš šių patobulinimų jau įtraukti į pagrindinį „Git“ projektą, o daug kitų yra mūsų viešoje saugykloje. Toliau dirbame su „Git“ bendruomene, kad šie patobulinimai būtų naudingi ir ne vien „Pando“. Šį darbą galite stebėti mūsų viešoje pataisų sekimo sistemoje⁠(atsidaro naujame lange).

Kas toliau

Permąstę įprastus „Git“ saugyklų plėtimo principus, sukūrėme „Pando“, kuri leidžia plėsti bendrą kodo saugyklą nekeičiant „Git“ darbo eigos, jau naudojamos mūsų inžinierių ir agentų. Mūsų bendroje kodo saugykloje „Pando“ leido daugiau darbų atlikti lygiagrečiai, padidino suliejimo patikimumą ir sutrumpino kodo pakeitimų paskelbimo laiką. Be bendros kodo saugyklos, nagrinėjame ir kitus „Pando“ naudojimo būdus OpenAI viduje, įskaitant užduotis, kuriose daug agentų turi bendradarbiauti bendroje „Git“ saugykloje.

Inžinieriams ir agentams kuriant vis daugiau programinės įrangos, su mūsų pasiektomis kodo versijų valdymo ribomis susidurs ir daugiau komandų už OpenAI ribų. Šiuo metu ieškome būdų pasidalyti su naudotojais technologija, kuria paremta „Pando“.

„Pando“ remiasi dviejų dešimtmečių „Git“ saugyklų naudotojų ir administratorių patirtimi. Toliau plėtosime „Pando“ ir bendradarbiausime su atvirojo kodo „Git“ bendruomene, dalydamiesi patobulinimais, naudingais platesniam „Git“ naudotojų ratui.

Autorius

OpenAI