OpenAI

Nobyembre 2, 2026

Engineering

Pagpapalawak ng Git sa OpenAI

Pagbuo ng source control na kayang suportahan ang lawak ng gawain ng mga agent habang namumuhunan sa Git ecosystem.

Nina Aaron Friel, Taylor Blau, at Ted Nyman, mga Miyembro ng Technical Staff

Naglo-load…

Sa nakalipas na taon, napakabilis ng paglaki ng aming monorepo at pagtaas ng mga pangangailangan nito. Malinaw na kailangan naming baguhin ang aming source control system para makayanan ang dami ng sabay-sabay na gawaing ginagawang posible ng Codex.

Binuo namin ang Pando, isang bagong backend para sa mga Git repository, nang hindi naaantala ang anumang engineering work o binabago ang paraan ng paggamit ng aming team sa Git. Pando na ngayon ang nagpapatakbo sa lahat ng development sa monorepo ng OpenAI. Dinisenyo ang Pando para sa dalawang ambisyosong layunin: una, kapasidad sa pagsulat na napapalawak sa pagdaragdag ng mga node, at naka-optimize para sa sabay-sabay na push sa maraming branch. At ikalawa, i-maximize ang throughput sa pagbasa, para makapag-download ang mga client nang sapat ang bilis upang lubos na magamit ang available nilang network bandwidth.

Natugunan namin ang mga layuning ito habang pinananatiling compatible sa Git protocol at client, kaya madaling naidagdag ng aming mga engineer at agent ang Pando sa kanilang kasalukuyang mga workflow. Sa production, pinababa ng Pando ang median push latency sa halos ikalimang bahagi ng dati. Nakapaghahatid na ito ngayon ng halos isang petabyte ng Git data bawat araw.

Dito, tatalakayin namin kung bakit kami bumuo ng bagong source control system at kung bakit pinili naming panatilihin ang compatibility sa Git. Ihahambing namin ang aming sistema sa mga karaniwang paraan ng pagpapalawak ng Git, at ipapaliwanag kung bakit natatangi ang kakayahan ng Pando na tugunan ang mga pangangailangan ng OpenAI sa paglaki. Tatalakayin din namin ang mga susunod na hakbang, at kung paano kami patuloy na mag-aambag sa Git para masuportahan ang lawak ng gawaing kayang gawin ng mga agent.

Bakit namin kinailangan ng bagong source control system

Binago ng Codex ang lawak at bilis ng development sa OpenAI, kaya mas marami nang gawaing kayang gawin ng mga engineer nang sabay-sabay. Ibig sabihin nito, mas maraming branch at push, mas maraming merge, at mas maraming fetch at clone mula sa mga development environment, research job, at CI.

Lalo pang nadaragdagan ang mga pangangailangang ito. Ang isang engineer ay mayroon nang ilang pagbabagong sabay-sabay na handa para sa CI, at bawat push ay maaaring magpasimula ng ilang job na nagfe-fetch ng parehong commit.

Bandang katapusan ng nakaraang taon, nagsimulang mahirapang makasabay ang mga sistemang sumusuporta sa aming monorepo. Naranasan namin ang mabagal na fetch at push, pati ang mga nabigong merge, at kung minsan ay bumababa sa 90% ang pagiging maaasahan. Naabot ba namin ang limitasyon ng Git mismo, o ng paraan ng paghahatid namin ng aming monorepo? Nagmula ang Git sa isang komunidad na nagpapalitan ng mga patch sa kernel sa pamamagitan ng email. Sa mahigit dalawang dekada, iniangkop ng mga developer ang kanilang mga tool sa Git habang nagbabago ang paraan nila ng pagbuo ng software. Nang magsimula kaming makaranas ng mga sagabal sa aming monorepo, mahalaga sa amin na maipagpatuloy ang pagbuo sa pundasyong iyon.

Nakakita kami ng paraan para mapanatili ang pamilyar na workflow habang muling binubuo ang backend para sabay-sabay na maproseso ang mga push sa iba't ibang branch at maihatid ang parehong history sa maraming reader. Dahil Git ang pundasyon ng aming source control workflow, maaari ring mapakinabangan sa labas ng OpenAI ang mga pagpapahusay sa client. Gusto naming iambag sa upstream ang mga pagpapahusay na iyon mula sa aming kopya ng Git⁠(magbubukas sa bagong window), para maibahagi sa mas malawak na komunidad.

Pagdidisenyo ng Git para sa malawakang paggamit

May dalawang mahalagang aspeto ang pagpapalawak ng source control system: maraming writer na nagpu-push sa iba't ibang branch, at maraming reader na nagfe-fetch ng parehong history. Naka-optimize ang Pando para sa pareho.

Una, magkakahiwalay na kino-coordinate ng Pando ang mga update sa bawat branch. Maaaring sabay-sabay na isagawa ang mga update sa mga branch na walang kaugnayan sa isa't isa, at nakukuha ng reader na nagfe-fetch ng branch ang history na kailangan para sa rebisyong iyon. Ang kapalit nito, maaaring makita ng reader ang ilang branch sa magkakaibang punto ng oras.

Ikalawa, muling ginagamit ng Pando ang mga packfile (format ng Git para sa magkakasamang pag-compress ng maraming object) sa iba't ibang fetch, sa halip na gumawa ng hiwalay na packfile para sa bawat client. Maaaring ma-download ng client ang ilang object na mayroon na ito, pero naihahatid ng server ang parehong mga byte sa maraming client gamit ang kaunting computation.

Dahil sa mga pasyang ito, napapalawak namin ang kapasidad sa pagsulat sa iba't ibang branch at natutugunan ang mga pagbasa mula sa mga cache. Para makita kung paano nagtutulungan ang mga ito, magsimula tayo sa paraan ng pag-iimbak at paglilipat ng Git ng history.

Panimula sa pag-iimbak ng Git object

Kailangang mapanatiling magkasabay ng Git ang dalawang bahagi ng repository: mga object at mga reference.

Itinatala ng isang Git repository ang mga snapshot ng file at ang history na nag-uugnay sa mga ito bilang graph ng mga object. Ang isang object ay maaaring blob, tree, commit, o tag. Laman ng mga blob ang nilalaman ng bawat file, at iniuugnay ng mga tree ang mga pangalan sa mga blob o iba pang tree para bumuo ng istruktura ng directory. Tumuturo ang mga commit sa isang root tree at sa wala, isa, o higit pang parent commit, na bumubuo sa history ng iyong repository.

Natutukoy ang mga object gamit ang hash ng kanilang uri at nilalaman. Hindi rin nababago ang mga object: lumilikha ng bagong blob ang pagtatala ng nabagong file, habang maaaring muling gamitin ng mga hindi nabagong file at directory ang mga kasalukuyang object.

Ang mga branch at tag ay mga reference, o “ref”, na tumuturo sa mga commit. Ang pag-commit sa isang branch ay lumilikha ng bagong commit at nag-a-update sa ref ng branch para tumuro rito.

Tumuturo ang branch na refs/heads/main sa commit C2, na may parent na C1. Tumuturo ang C1 sa root tree T1, kung saan tumutukoy ang parser.py sa blob B1. Tumuturo ang C2 sa root tree T2, kung saan tumutukoy ang parser.py sa blob B2. Sa parehong root tree, tumutukoy ang docs/ sa iisang tree, ang T3. Tumuturo ang entry nitong README.md sa blob B3, at ang usage.md sa blob B4.

Tumutukoy ang mga Git object sa isa't isa gamit ang kanilang ID: ang mga commit sa mga tree, at ang mga tree sa iba pang tree at blob. Tumuturo ang branch na refs/heads/main sa commit C2, na may parent na C1. Tumuturo ang C1 sa root tree T1, kung saan tumutukoy ang parser.py sa blob B1. Tumuturo ang C2 sa root tree T2, kung saan tumutukoy ang parser.py sa blob B2. Sa parehong root tree, tumutukoy ang docs/ sa iisang tree, ang T3. Tumuturo ang entry nitong README.md sa blob B3, at ang usage.md sa blob B4.

Iniimbak ng Git ang mga object nang “hiwa-hiwalay” o “magkakasama sa pack”. Ang mga hiwa-hiwalay na object ay iniimbak sa magkakahiwalay na file na tinutukoy gamit ang kanilang object ID. Ang mga naka-pack na object ay magkakasamang iniimbak sa isang packfile, kaya maaaring iimbak ang magkakahawig na object bilang mga delta batay sa isa't isa.

Makikita ang dalawang bersyon ng parser.py sa $GIT_DIR/objects . Bilang magkakahiwalay na object, laman ng blob A ang parse(), at laman ng blob B ang parse() at parse_file(). Iniimbak ng packfile ang buong A at kinakatawan ang B bilang mga utos na kopyahin ang buong A at idagdag ang parse_file() sa dulo. Tinutukoy ng kasamang *.idx file ang lokasyon ng dalawang blob sa packfile gamit ang object ID.

Kayang iimbak ng Git ang mga blob bilang magkakahiwalay na file o pagsamahin ang mga ito sa pack, kung saan ine-encode ang isang blob bilang delta batay sa isa pa.

Sa Git, naaabot ang isang object mula sa commit kung mahahanap ito ng Git sa pagsunod sa mga link mula sa commit na iyon papunta sa mga parent, tree, at blob nito. Sa isang fetch, sinasabi ng client sa server kung aling mga commit ang gusto nito at alin ang mayroon na ito. Hinahanap ng server ang mga object na naaabot mula sa mga gustong commit, inaalis ang mga alam nitong mayroon na ang client, at ipinapadala ang natitira bilang packfile sa parehong format na ginagamit ng Git sa disk. Nagsisimula ang buong clone nang walang anumang object, kaya kailangan nito ang lahat ng object na naaabot mula sa history na kino-clone.

Ang push ay naglilipat ng mga object sa kabilang direksyon sa pamamagitan ng paghiling sa server na i-update ang isa o higit pang ref. Ginagamit ng client ang hanay ng mga ref na ipinabatid ng server para maiwasang magpadala ng mga object na mayroon na ang server. Kaya maaaring nakadepende ang ipinapadalang pack sa mga umiiral nang object: hindi sapat ang pagtanggap sa pack lang para mabuo muli ang bagong history.

Ang pag-replicate ng Git repository ay nangangahulugang pagpapanatiling available ng mga object at reference sa maraming server. Bago maihatid ng replica ang bagong value ng reference, dapat kaya nitong ibigay ang mga object na naaabot mula sa value na iyon. Kailangan ding i-coordinate ng serbisyo ang mga nag-uunahang update sa reference, ibig sabihin, mga push na sinusubukang ilipat ang parehong branch mula sa parehong lumang tip papunta sa magkakaibang bagong commit. Sa mga sistemang nagtatakda ng iisang pagkakasunod-sunod sa lahat ng pagsulat sa repository, maaaring kailangang maghintayan ang mga push kahit sa magkakahiwalay na branch.

Mga kasalukuyang disenyo ng distributed na serbisyo ng Git

Habang dinidisenyo ang Pando, sinuri namin ang mga karaniwang paraan ng pagpapalawak ng source control gamit ang Git: ang isa ay nagko-coordinate ng mga ref update sa mga replica ng repository gamit ang three-phase commit (3PC, isang protocol para sa koordinasyon ng commit sa maraming node); ang isa naman ay nagsasaayos ng mga update gamit ang isang pinagsasaluhang write-ahead log (WAL, talaan ng mga pagbabagong isinusulat bago ilapat) sa blob storage.

Three-phase commit (3PC)

Sa disenyong three-phase commit na ginaya namin, nagpapanatili ang serbisyo ng Git ng kumpletong kopya ng repository sa ilang replica. Kapag nakatanggap ng push ang alinmang server, kinokopya ang papasok na packfile at sabay-sabay na ipinapadala sa lahat ng replica. Para mailathala ang push, kailangang tanggapin ng karamihan ng mga replica ang ref update. Ang server na namamahala rito ang coordinator, at kailangan ng ilang round ng komunikasyon sa pagitan nito at ng bawat iba pang replica.

Para tanggapin ang reference update, nila-lock ng bawat replica ang kopya nito ng ref at tinitingnan kung nakaturo pa rin ito sa commit na inaasahan ng client. Pagkatapos, nagpapadala ang coordinator ng pre-commit na mensahe sa mga replica na tumanggap. Kapag kinumpirma na ng karamihan ang pre-commit na mensahe, inuutusan sila ng coordinator na isagawa ang update. Maaari nang ipabatid ng alinmang napapanahong replica ang bagong tip ng branch mula sa lokal nitong kopya ng repository.

Dahil kumpleto ang mga replica, maaaring tugunan ang mga pagbasa nang lokal, at natitiyak na magkakatugma ang nakikitang estado ng lahat ng reference ng mga client, alinmang replica ang piliin nila. Dahil dito, paisa-isang ina-update ang mga reference, at kailangan ng bawat push ng ilang round trip para i-coordinate ang ref update sa mga replica.

Dahil kailangang maghintay ng quorum, nakaaapekto sa proseso ng push ang performance ng disk ng replica at latency ng komunikasyon. Habang dumarami ang pagsulat, kailangang makasabay ang mga replica sa mga papasok na pack pati sa traffic ng pagbasa.

Write-ahead log (WAL)

Sa disenyong batay sa WAL na ginaya namin, gumagamit ang serbisyo ng Git ng pinagsasaluhang manifest sa blob storage para itakda ang pagkakasunod-sunod ng mga update sa buong repository. Kapag nakatanggap ng push ang server, ina-upload nito ang papasok na packfile sa blob store at inihahanda ang ref update sa lokal nitong kopya ng repository. Para mailathala ang push, nagdaragdag ito ng entry sa pinagsasaluhang manifest sa blob store na nagtatala ng ref update at tumuturo sa packfile. Pagkatapos, ina-update ng Git server ang manifest gamit ang operasyong compare-and-swap. Atomiko nitong pinapalitan ang manifest, o kaya ay tinatanggihan ang pagbabago. Kung na-update ito ng ibang writer habang ginagawa iyon, binabasa ng server ang mga naisingit na entry, tinitingnan kung valid pa ang ref update nito, at sumusubok muli gamit ang packfile na na-upload na nito.

Maaaring sabay-sabay na i-upload ng mga writer ang kanilang mga packfile, pero kahit ang mga push sa magkakahiwalay na branch ay kailangang mag-update sa parehong manifest. Nililimitahan ng round-trip time ng matagumpay na compare-and-swap kung gaano kadalas makakapaglathala ang repository ng bagong manifest. Napapabilis ang hakbang na ito ng blob store na mababa ang latency. Sa pagba-batch, kayang i-update ng isang manifest ang ilang ref nang sabay-sabay, pero kapalit nito ang pagpila ng mga update at pagtaas ng latency.

Kayang tumanggap ng blob store ng mga packfile nang sabay-sabay, pero nag-uunahan pa rin ang mga update sa pinagsasaluhang manifest. Hindi nalulutas ang sagabal na iyon sa pagdagdag lang ng kapasidad ng blob store. Kailangan pa ring basahin at i-index ng bawat replica ang mga pack mula sa blob store bago nito maihatid ang mga ito nang lokal. Kung mas mabilis ang mga papasok na pagsulat kaysa sa gawaing iyon, nahuhuli ang mga replica at mas matagal na naghihintay ang mga client bago ma-fetch ang mga bagong na-push na commit.

Pagdidisenyo ng arkitektura ng Pando

Dito pumapasok ang Pando. Ang Pando ay isang bagong arkitektura para sa paghahatid ng mga Git repository, na layuning suportahan ang maraming client na sabay-sabay na nagpu-push. Ganito ito gumagana:

Muling pag-iisip sa mga limitasyon

Bago namin binuo ang Pando, may kutob kaming matutugunan ang aming mga pangangailangan nang hindi isinasakripisyo ang Git. Pero kinailangan naming luwagan ang ilang bagay na itinuturing na kailangan sa karamihan ng Git system:

  • Consistency sa buong repository: Kailangan ng koordinasyon sa buong repository para makapagbigay ng magkakatugmang snapshot ng lahat ng ref sa iisang punto ng oras. Sa sistemang nagtatakda ng iisang pagkakasunod-sunod para sa lahat ng ref update, nag-uunahan sa iisang coordination point ang mga push sa magkakahiwalay na branch.

  • Natatanging packfile para sa bawat client: Kung magkaiba ang lokal na history ng dalawang client na nagfe-fetch ng parehong branch, maaaring kailanganing gumawa ng server ng magkahiwalay na pack para hindi magpadala ng mga object na mayroon na ang bawat isa. Gumagamit ng CPU at disk I/O ang paggawa ng mga tugong ito, kaya mahirap tugunan ang napakaraming reader.

Sa halip, gumagamit ang Pando ng mas maluwag na consistency model (causal consistency) para mapalawak ang kapasidad sa pagsulat, at muling gumagamit ng mga packfile para mapalawak ang kapasidad sa pagbasa.

Causal consistency (CC)

Kapag nag-push ang isang client sa branch, iniimbak ng Pando ang papasok na packfile bilang blob na hindi nababago sa isang blob store. Sa bawat pag-usad ng branch, nagtatala ang Pando sa metadata store ng bersyon ng ref na naglalaman ng bagong tip, pack na pinagmulan nito, at anumang naunang bersyon ng ref na kailangan ng pack. Parehong napapalawak ang blob store at metadata store sa pamamagitan ng pagdaragdag ng mga node.

Para mag-fetch ng commit o reference mula sa Pando, kailangang muling ilapat sa client ang isang hanay ng mga bersyon ng ref (mga packfile), upang mabuo muli ang estado ng client na lumikha sa bersyon ng ref na kinukuha. Nagbibigay ito ng mas mahinang consistency sa buong repository, pero tinitiyak ang serializability ng mga update sa bawat ref. Nagkakasalungatan lang ang mga client kapag sabay silang nagpu-push sa parehong ref. Hindi maaaring ipagpalagay ng client na nagbabasa ng ilang branch na ang mga nabasa nito ay kumakatawan sa iisang punto ng oras sa buong repository.

Sa kaliwa, laman ng tatlong magkakasunod na trunk pack ang mga commit na A, B at C; D at E; at F. Sa kanan, naghihiwalay ang trunk at topic sa C. Umuusad ang trunk sa G, habang umuusad ang topic sa D, E, at F. Laman ng pinakabagong topic pack ang D, E, at F. Ipinapakita ng putol-putol na balangkas na kasama rin ang D at E sa naunang topic pack. Ipinapakita ng asul na arrow ang dependency ng bagong pack sa trunk sa G. Ang Git parent ng F ay E.

Ang push sa trunk ay nagdaragdag ng incremental pack na naglalaman ng isa o higit pang commit. Muling isinasama ng bawat push sa side branch ang mga naunang commit ng topic, kaya kayang i-fetch ng client na may ipinabatid na bersyon ng trunk ang topic sa iisang pack.

Isa itong anyo ng causal consistency. Para mag-fetch ng reference, nakikita ng reader ang history ng writer na gumawa nito. Sa isang push, kinokontrol ng Pando kung aling magkakaparehong history ang pagkakasunduan ng client at server, para makagawa ang mga client ng mga packfile na mas madaling magamit ng iba.

Dahil dito, hindi nagbibigay ang Pando ng estado ng lahat ng reference sa iisang punto ng oras habang nagsasagawa ng mga operasyon sa Git, o ng pagkakasunod-sunod ng mga ref update sa loob ng clone o fetch. Halimbawa, ipagpalagay na parehong na-update ang mga branch na foo at bar habang nagne-negotiate ng buong clone. Habang nagne-negotiate, maaaring makita ng isang client ang foo sa bersyon 1 at bar sa bersyon 2, habang nakikita ng isa pang client ang foo sa bersyon 2 at bar sa bersyon 1. Sa aktuwal na paggamit, hindi umaasa ang mga user at agent sa pangkalahatang pagkakasunod-sunod ng mga ref update, kundi sa pagkakasunod-sunod ng mga update sa iisang reference.

Paghahambing ng mga pamamaraan

Sa simulation na ito ng tatlong consistency model, nag-configure kami ng 1,000 client para bawat isa ay pumili ng magkakaibang branch at paulit-ulit na mag-push ng mga natatanging commit. Sinusukat namin ang kabuuang aktuwal na tagal at latency mula push hanggang fetch. Mahalagang sukatan ang huli para sa mga workload, dahil sinusukat nito ang round-trip time para makipagtulungan ang mga user at agent o gumamit ng iba pang sistemang umaasa sa version control. Ginagaya ng simulation ang hardware mula sa mga karaniwan hanggang sa mga premium na virtual machine at blob storage system. Sa simulation na ito, niluwagan namin ang consistency sa buong repository para sa three-phase commit.

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.

Patakbuhin ang simulation na ito para ihambing ang Pando sa dalawa pang pamamaraan. Pumili ng imprastraktura mula sa karaniwan hanggang sa premium na hardware, bilang ng mga client, at iba pang setting para makita ang paghahambing.

Sa ginayang three-phase commit system, mas malaki ang pagtaas ng write latency kapag mabigat ang load dahil naaabot ang kapasidad ng mga disk. Samantala, mas malaki ang pagtaas ng read latency sa mga write-ahead log system sa parehong dahilan, habang nahihirapan ang mga server na makasabay sa pagbasa ng journal. Sa aktuwal na pagsubok sa aming monorepo at sa simulation na ito, sinusuportahan ng Pando ang libo-libong client na sabay-sabay na nagpu-push nang hindi naaapektuhan ang performance kahit mabigat ang load.

Sa pagproseso ng push, bina-validate ng Pando ang papasok na packfile habang ini-stream ito sa blob storage, nang hindi ito ina-unpack sa lokal na repository. Kapag nagsimula ng fetch ang client, pinipili ng Pando ang mga nakaimbak na pack na kailangan ng reader, at dina-download ng reader ang mga ito. Walang server na kailangang maglapat ng mga packfile sa lokal na repository o magsagawa ng mga operasyong unpack at packing na malakas gumamit ng resource. Dahil negotiation at pagpili ng packfile ang pangunahing ginagawa ng mga server na ito, maaaring tumakbo ang mga Pando server sa karaniwang hardware.

Dahil dito, kayang palawakin ang Pando hanggang sa limitasyon ng mga client, ng network sa pagitan ng mga client at Pando, at ng blob storage service. Kung sapat ang kapasidad, makakapaglipat ng mga pack ang bawat client na nagtatrabaho sa magkakahiwalay na branch sa pinakamabilis na kayang suportahan ng sarili nitong koneksyon. Para masuportahan ang mas maraming client sa ganoong bilis, maaari kaming magdagdag ng mga Pando server at kasabay na palawakin ang metadata store at blob store.

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

Nag-e-edit ng mga file at gumagawa ng lokal na commit ang isang agent, saka nagpapatakbo ng git push, na lumilikha ng packfile ng mga pagbabagong iyon para i-update ang branch. Tinatanggap ng Pando ang pack at ini-stream ito sa blob storage. Matapos i-validate ang papasok na push sa isang pasada ng pag-decode, gumagamit ang Pando ng multi-key transaction para magtala ng bersyon ng ref sa aming metadata store at atomikong i-update ang ref pointer. Dahil dito, natitiyak ng Pando ang consistency kung saan “nababasa mo ang sarili mong isinulat”. Nangangailangan ang buong operasyong ito ng pagsulat ng O(1)O(1) na pagbasa at pagsulat ng metadata.

Kapag nag-fetch ng branch ang isa pang client, pumipili ang Pando ng isang hanay ng mga nakaimbak na pack batay sa history na mayroon na ang client. Nangangailangan ang operasyong ito ng O(log⁡Δ)O(\log \Delta) na pagbasa ng metadata, kung saan ang Δ\Delta ang pinakamaikling distansya sa mga bersyon ng ref sa pagitan ng mayroon na ang client at ng gusto nitong makuha. Nagbabalik ang server ng mga URI ng packfile, at dina-download ng client ang mga ito sa pamamagitan ng Express, isang serbisyong nag-iimbak ng mga packfile sa mga panrehiyong cache.

Umaasa ang disenyong ito sa tatlong pangunahing serbisyo ng arkitektura ng Pando: push, fetch, at compaction.

Pagpapalawak ng kapasidad sa push

Kapag nag-push ang client, parang manlalaro ng poker na inilalantad ang lahat ng baraha ang karaniwang Git server: ipinapakita nito sa client ang bawat branch na mayroon ito. Pagkatapos, tinitipon ng client ang mga commit, tree, at blob para gumawa ng packfile na hindi kasama ang mga object na alam nitong mayroon na ang server.

Maaaring mapaliit ang push kapag mas maraming ref ang ipinababatid. Pero maaari rin nitong pahirapang gamitin muli ang pack, dahil maaaring wala sa ibang client ang mga object na hindi isinama ng writer.

Iba ang pamamaraan ng Pando. Sa halip na ilantad ang lahat ng baraha nito, piling mga branch lang ang ipinababatid nito sa client. Ginagawa ito sa dalawang dahilan:

  1. Malaki ang gastos sa pagbibigay ng magkakatugmang view ng bawat ref. Kung milyon-milyon ang branch, maaaring lumampas sa 100 MiB ang impormasyon sa ipinababatid na mga ref pa lang. Para maibigay ito, kailangang magpanatili ang bawat server ng napapanahong mapping, na may malaking dagdag na gastos sa koordinasyon, o magsagawa ng database query na lumalaki kasabay ng dami ng ref.

  2. Kapag bahagi lang ng mga ref ang ibinigay sa client, gagawa ito ng pack na nakadepende lang sa mga bagay na tahasang pinapayagan ng server na hindi isama ng client. Sa pagsubaybay sa mga dependency, nagagamit muli ng Pando ang packfile na ginawa ng client sa isang push bilang tugon sa susunod na fetch.

Habang mas maraming ref ang ipinababatid ng Git server, mas maraming pinagmumulan ang magagamit ng mga client na nagpu-push para tukuyin ang mga object na hindi isasama, kaya mas maliliit na pack ang nagagawa nila. Pero para muling magamit ang mga packfile na ito, kailangang mapatunayan ng server na mayroon ang mga client na nagfe-fetch ng lahat ng dependency. Kung hindi, maaaring may mga object na kailangan ng client pero wala sa packfile.

Sa kabilang banda, kung walang anumang ref na ipinababatid ang Git server, gagawa ang client ng packfile na naglalaman ng buong history ng repository. Magagamit ng alinmang ibang client ang packfile na iyon, pero may malaking kapalit: halos katumbas na ng buong clone ang pag-fetch ng anumang branch.

Kurba na nagpapakita ng epekto ng mga ipinabatid na reference sa laki ng packfile: buong history kapag walang ipinabatid, mas maliit kapag trunk ang ipinabatid, at mga bagong object lang kapag lahat ng reference ang ipinabatid.

Mas maliliit na packfile ang nagagawa kapag mas maraming ref ang ipinababatid. Maaaring makamit ang malaking bahagi ng pagbawas sa laki sa pagpapabatid ng kahit isa lang.

Bilang default, kapag nag-push ang client ng bagong feature branch, isang itinalagang trunk branch lang ang ipinababatid ng Pando bilang base. Napapanatili nitong pareho ang laki ng ipinababatid na impormasyon kahit dumarami ang branch sa repository, pero may mga kapalit ito. Kapag maraming beses na nag-push ang engineer sa iisang branch, o nagpatong ng isang feature branch sa isa pa, kailangang ipadala ulit ng client ang lahat ng nasa labas ng trunk.

Dahil dito, mas lumalaki ang magkakasunod na push sa branch, maaaring may mga paulit-ulit na object, at nadaragdagan ang nagagamit na bandwidth ng client. Sa aming monorepo, wala pang 20 KiB ang median na laki ng packfile na ginawa sa ganitong paraan, at wala pang 3 MiB ang ika-99 na percentile. Para sa amin, makatuwiran ang kapalit na ito dahil kayang i-fetch ng reader na mayroon na ng ipinabatid na trunk history ang branch gamit ang iisang packfile na iyon.

Matapos piliin ang hanay ng mga ref na ipababatid sa client, tinatanggap ng Pando ang push at ina-update ang metadata store nito. Hinahati ng Pando ang metadata update sa mga bahaging synchronous at asynchronous. Tinitiyak ng synchronous na metadata update na agad na mafe-fetch ng mga client ang bagong na-update na commit. Nagsusulat ito ng bagong bersyon ng ref at ina-update ang mapping ng ref pointer patungo sa bagong bersyon. Iniimbak nito ang bagong tip, mga dependency na ginamit sa negotiation ng packfile, at link sa packfile na na-push.

Kasabay nito, itinatala sa parehong transaksyon ang asynchronous na gawain, gaya ng pag-iskedyul ng compaction. Para sa napakalalaking push, bahagi lang ng mga commit ang isinusulat nang synchronous. Dahil dito, pare-parehong bilang lang ng pagbasa at pagsulat sa metadata store ang kailangan, gaano man kalaki ang push.

Mahalaga ring kayang tumanggap ng mga Pando server ng mga push sa magkakahiwalay na ref nang hindi umaasa sa isa't isa. Ang ref pointer lang ang key na pinag-aagawan, at pinoprotektahan namin ito gamit ang atomikong operasyong katulad ng compare-and-swap. Mapapataas namin ang throughput sa pagsulat sa pamamagitan ng pagdaragdag ng mga Pando server, scaling unit ng metadata store, o blob store.

Fetch at clone na kasingbilis ng kapasidad ng network

Kapag nagsasagawa ng fetch ang client, nagbibigay ito sa server ng dalawang hanay ng commit ID, na kilala sa protocol bilang mga have at want. Sa karaniwang Git server, kinukuwenta ng server ang pagkakaiba ng hanay ng mga object na naaabot mula sa mga have at sa mga want, kino-compress ang mga ito, at ipinapadala ang resulta sa client. Bagama't may iba't ibang lokal na index ang open-source na Git server para pabilisin ang operasyong ito, maaaring malaki ang computation na kailangan sa paggawa ng mga packfile na ito at karaniwan ay mahirap silang i-cache.

Sa halip, ginagamit ng Pando ang mga commit mapping na naitala sa mga push para hanapin ang mga have at want ng client at itugma ang mga ito sa isang hanay ng mga bersyon ng ref na naglalaman ng mga commit na iyon. Bawat bersyon ng ref na iyon ay naka-link sa isang packfile na dati nang natanggap ng Pando. Ginagamit ito ng server para matukoy ang kailangan ng client, at pinipili ang mga packfile na sumasaklaw sa hanay ng mga object na kailangan nito.

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.

Itinutugma ng Pando ang mga have at want ng Git protocol sa isang sunod-sunod na hanay ng mga bersyon ng ref. Kapag hinihingi ang isang side branch, kasama ring hinihingi ang history hanggang sa mga dependency nito sa trunk.

Iisang packfile lang ang pinapayagang ipadala ng Git protocol sa HTTP response para sa clone o fetch. Pero may isang hindi gaanong napapansing feature ng Git protocol, ang packfile-uri, na nagpapahintulot sa server na magbalik ng mga link para hiwalay na i-download ang mga karagdagang pack. Ito lang ang ginagamit ng Pando, at nagpapadala ito ng walang-lamang packfile na inline. Para suportahan ang feature na ito, nag-ambag ang OpenAI sa upstream ng ilang pagpapahusay sa packfile-uri.

Gumagana ang mga sistema ng OpenAI sa maraming rehiyon sa mundo, kaya nagpapanatili kami ng mga Pando cluster na may lokal na cache sa bawat rehiyon. Kina-cache ng Express, isang serbisyo sa paghahatid ng pack, ang aming blob store, at hiwalay na serbisyo ang nagka-cache sa metadata store. Dahil sa mga cache na ito, kayang tugunan ng Pando ang mga request para sa commit ID o espesyal na pagbasa ng ref gamit lang ang naka-cache na estado sa cluster na iyon. Gumagamit kami ng consistent hashing para iruta ang mga request ng packfile sa mga Express server, na may mahigit 99% na cache hit rate.

Nagbibigay ang sistemang ito ng mga cache ng napakataas na throughput, na kayang sagarin ang kapasidad ng network sa pagitan ng mga client at Pando. Gamit ang mga URI ng packfile at lokal na cache na ito, napatunayan naming kayang sagarin ng mga clone at fetch ang 10 Gbps na network link, kahit mga karaniwang virtual machine lang ng cloud provider ang ginagamit.

Compaction at oras bago matanggap ang unang byte

Para sa karamihan ng Git server, kailangan ang repacking at garbage collection para mapanatili ang performance ng lumalaking repository. Parehong nagbabakante ng espasyo ang mga hakbang na ito, pero malakas gumamit ng compute at I/O.

Sa mabilis magbagong repository gaya ng monorepo ng OpenAI, hindi praktikal na magpadala ng hiwalay na URI ng packfile para sa bawat push na naganap mula noong huling fetch. Araw-araw, kailangang mag-download ng client ng sampu-sampung libong packfile para ma-update ang trunk nito.

Sa halip, pinagsasama ng Pando ang mga pack sa background para mabawasan ang dagdag na gawaing ito, gamit ang disenyong katulad ng compaction sa mga log-structured merge-tree. Bawat push ay lumilikha ng tinatawag ng Pando na “L0” packfile, na kumakatawan sa pinakamaliit na yunit ng data na kayang ipadala ng Pando. Sa paglipas ng panahon, tinitipon ng mga background worker ang mga packfile na ito sa mga cover na sumasaklaw sa sunod-sunod na bersyon ng ref, na lumalaki nang geometriko mula sa L0. Sa ngayon, gumagamit ang aming monorepo ng mga packfile cover hanggang L4.

Dalawa ang layunin ng mga cover na ito. Kapag sapat na ang laman ng cover, kukunin ito ng isang compaction worker para gumawa ng iisang packfile. Pinapahusay nito ang compression ratio sa pamamagitan ng muling pag-aayos ng mga object sa loob ng packfile. Pinapahusay din nito ang performance ng fetch negotiation sa pamamagitan ng paggana bilang skip list sa mga bersyon ng ref. Sa sabay-sabay na pag-scan sa mga index sa bawat antas, nakakapili ang Pando ng hanay ng mga pack na ipapadala sa client sa logarithmic na oras. Na-repack man o hindi, nagbibigay ang mga packfile cover na ito ng mas episyenteng paraan ng pagpaplano ng fetch, at binabawasan ang mga kinakailangan sa throughput at latency para sa Pando.

Compaction in Pando

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

Compaction in progressPublished cover

Pinagsasama ng compaction sa background ang mga packfile mula sa maraming bersyon ng ref, kaya mas episyente ang mga fetch gamit ang mas kaunting file.

Di-tulad ng maraming Git server, pinananatili ang mga packfile sa bawat antas at hindi itinatapon o inaalis sa garbage collection. Patuloy na ginagamit ang mga pack sa bawat antas para pagsilbihan ang mga client, batay sa mga have at want na hinihiling nila sa fetch. At para sa mga client na gumagamit sa Pando bilang API, ang pagbasa ng anumang object mula sa packfile ay karaniwang nangangailangan ng mas kaunting data mula sa L0 kaysa sa L4, dahil mas malamang na hindi ito naka-compress.

Malaki ang nababawas ng compaction sa Pando sa kabuuang laki ng data na ipinapadala sa mga client. Pinapahintulutan din nitong lumaki nang sub-linear ang pagpaplano ng pack kaugnay ng kung gaano kalayo ang pagkahuli ng client. Lalo itong mahalaga para sa malalaki at mabilis magbagong repository, na inaasahan naming magiging karaniwan na sa software development.

Pagbuo ng Pando

Binuo namin ang Pando gamit ang Codex, mula sa pagsisimula hanggang sa production, sa loob lang ng 10 linggo. Iisang engineer ang nagdisenyo sa pangunahing bahagi ng Pando, mga team ng agent ang nag-implement nito, at iilang engineer ang nag-deploy. Nagsimulang humawak ng production traffic ang Pando noong Abril ngayong taon, at bahagi ito ng mas malaking sistema ng mga tool sa pamamahala ng source control na ginagamit sa OpenAI.

Gumamit kami ng maraming iba't ibang pamamaraan sa pag-validate ng disenyo ng Pando. Inilapat namin ang aming mga prinsipyo para sa pagbuo ng mas ligtas na mga pundasyon ng software⁠. Dahil bagong proyekto ito na sinimulan mula sa wala, pinili naming i-implement ang Pando sa Rust at nang walang mga legacy dependency. Gumawa kami ng conformance test suite para masubukan ng mga agent kung tumutugma ang kanilang mga pagbabago sa karaniwang paggana ng Git. Bumuo rin kami ng mga harness para sa deterministic simulation testing upang masuri ang mga invariant at katangian ng sistema. Nakatulong ang mga ito na makita at mabawasan ang mga banayad na race condition at regression sa negotiation at pagpaplano ng pack.

Maraming modelo ang nag-ambag sa sistemang ito; habang nagiging available ang mga bagong modelo, ginamit namin ang mga ito para pahusayin ang performance at pagiging maaasahan ng Pando. Gamit ang GPT‑6 Astra, nagsulat ang mga agent sa Lean at TLA+ para pormal na ilarawan ang isang “teorya ng Pando” na ginamit namin para hanapin at ayusin ang mga bug sa implementasyon, at patunayan ang mga katangian ng sistema. Maraming tuloy-tuloy na session ang tumakbo nang daan-daang oras para hanapin at ayusin ang mga edge case sa Pando. Nagpapatakbo rin kami ng mga agent buong araw at gabi para magsagawa ng red-team testing sa Git at Pando gamit ang mga modelong may kakayahang Daybreak.

Pag-deploy ng Pando

Una naming inilunsad ang Pando bilang read-through at write-through na proxy sa aming kasalukuyang source control system. Dahil dito, napanatili naming magkasabay ang Pando at ang dating sistema, kaya ligtas naming nailipat ang traffic sa Pando nang walang downtime.

Bilang bahagi ng disenyo nito sa caching, kaya ring alisin ng Pando ang mga dobleng request at pagsamahin ang mga request bago ipasa sa proxy. Nakatulong ito para manatiling maayos ang aming mga kasalukuyang tool habang paisa-isa naming inililipat ang mga sistema sa Pando. Habang mas maraming workload ang inililipat namin sa Pando, napupuno ng mga push ng mga ito ang cache ng Pando at nagiging mas maaasahan ang dati naming backend.

Para ma-optimize ang performance kapag gumagamit ng Pando, sinimulan naming ipamahagi ang aming pampublikong distribusyon ng Git sa mga computer ng developer at sa loob ng ChatGPT desktop app. Gumamit kami ng panloob na wrapper sa Git CLI para paunti-unting ilipat ang mga client sa Pando. Gumawa kami ng marami pang tool para sa paghawak ng mga change request, code review, at mga API para sa automation. Kasabay ng Git traffic, inilipat namin ang daan-daang integration na umaasa sa mga legacy API, webhook, at pag-download ng repository.

Mismong Codex ang tumulong sa mga developer at team sa paglipat. Gumawa kami ng mga skill para ihanda ang mga kasalukuyang checkout at gabayan ang mga agent sa paggamit ng bagong sistema. Dahil dito, maaari na lang hilingin ng mga developer sa Codex na ilipat ang kanilang mga development environment at serbisyo.

Naging mas maaasahan ang mga merge matapos lumipat sa Pando, at bumaba nang halos 80% ang median push latency.

Kayang maghatid ng Pando ng isang petabyte ng Git data bawat araw gamit lang ang iilang CPU core. Nakapaghatid na ang Pando ng mahigit 57 terabyte ng Git pack sa loob ng isang oras, na mahigit 99% ng mga byte ay mula sa lokal na disk cache.

Sa muling paggamit ng mga pack, maraming client ang nakakapag-download ng parehong repository history gamit ang kaunting computation sa mga node na naghahatid nito. Iisang repository ang ipinapakita ng halimbawang ito, pero dinisenyo ang Pando para suportahan ang marami pa. Habang tumataas ang demand, maaari naming palawakin nang magkahiwalay ang kapasidad sa paghahatid ng data at sa Git negotiation.

Pag-aambag pabalik sa proyektong Git

Ang pagbuo ng Pando batay sa Git protocol at client ay nakatulong sa amin na matukoy ang mga bug at pagkakataong pahusayin ang performance ng Git mismo.

Halimbawa, direktang naghahatid ang Pando sa mga client ng mga pack na magagamit muli, kaya naging mahalagang bahagi ng pagpapabilis at pagpapatatag ng sistema ang pag-download at pag-index sa mga pack na iyon. Natuklasan sa gawaing iyon ang mga edge case sa sabay-sabay na pag-download, paglilinis matapos maputol ang paglilipat, at pagpapatuloy ng mga download na tapos na pala. Inayos namin ang mga bahaging iyon, nagdagdag ng mga validation sa pagtanggap ng kasalukuyang pack para sa fetch, at iniambag ang gawaing iyon pabalik sa upstream.

Kailangan din naming makita kung saan napupunta ang oras at bandwidth, kaya nagdagdag kami ng tracing para sa mga download ng packfile-uri at bilang ng byte sa paggawa ng pack. Mas madali nang matukoy kung ang mabagal na fetch ay nauubos ang oras sa paggawa o paglilipat ng mga pack.

Sa Git build ng OpenAI⁠(magbubukas sa bagong window), kayang sabay-sabay na i-download at i-index ng client ang mga pack na hindi nakadepende sa isa't isa. Idinagdag namin ang konsepto ng “mga tahasang have” para maipabatid ng server ang kasalukuyang tip ng branch nang hiwalay sa history na maaaring hindi isama sa pack ng client na nagpu-push. Sa Pando, pinapahintulutan nito ang mga client na gamitin ang --force-with-lease para tingnan kung hindi pa umusad ang feature branch at buuin ang pack nito batay lang sa trunk. Pagkatapos, maaaring i-fetch ng sinumang may trunk history na iyon ang branch gamit ang parehong pack.

Nasa upstream Git na ang ilan sa mga pagpapahusay na ito, at marami pang iba ang nasa aming pampublikong repository. Patuloy kaming nakikipagtulungan sa komunidad ng Git para mapakinabangan ang mga pagpapahusay na ito maging sa labas ng Pando. Maaari mong subaybayan ang gawaing iyon sa aming pampublikong patch tracker⁠(magbubukas sa bagong window).

Ano ang susunod

Sa muling pag-iisip sa mga nakasanayang paraan ng pagpapalawak ng mga Git repository, pinapalaki ng Pando ang kapasidad ng aming monorepo nang hindi binabago ang mga Git workflow na ginagamit na ng aming mga engineer at agent. Sa aming monorepo, nagbigay-daan ang Pando sa mas maraming sabay-sabay na gawain, mas maaasahang merge, at mas maikling oras para mailathala ang mga pagbabago sa code. Bukod sa aming monorepo, pinag-aaralan din namin ang iba pang gamit ng Pando sa OpenAI, kabilang ang mga workload kung saan kailangang magtulungan ang napakaraming agent sa iisang Git repository.

Habang mas maraming software ang ginagawa ng mga engineer at agent, mas maraming team sa labas ng OpenAI ang makakaranas ng mga limitasyong naranasan namin sa source control. Kasalukuyan naming pinag-aaralan kung paano maibabahagi sa aming mga user ang teknolohiyang nasa likod ng Pando.

Nakabatay ang Pando sa dalawang dekadang karanasan ng mga user at operator ng mga Git repository. Patuloy naming pauunlarin ang Pando habang nakikipagtulungan sa open-source na komunidad ng Git para ibahagi ang mga pagpapahusay na makikinabang ang mas maraming user ng Git.

May-akda

OpenAI