OpenAI

2 November 2026

Kejuruteraan

Menskalakan Git di OpenAI

Membina kawalan sumber pada skala ejen sambil melabur dalam ekosistem Git.

Oleh Aaron Friel, Taylor Blau dan Ted Nyman, Ahli Staf Teknikal

Memuat…

Sepanjang tahun lalu, saiz monorepo kami dan tuntutan terhadapnya meningkat pada kadar yang luar biasa. Jelas bahawa kami perlu merombak sistem kawalan sumber agar dapat menampung skala kerja serentak yang dimungkinkan oleh Codex.

Kami membina Pando, sistem bahagian belakang baharu untuk repositori Git, tanpa mengganggu kerja kejuruteraan atau mengubah cara pasukan kami menggunakan Git. Pando kini mengendalikan semua pembangunan dalam monorepo OpenAI. Pando direka untuk memenuhi dua matlamat bercita-cita tinggi: pertama, penulisan yang boleh diskalakan secara mendatar, dioptimumkan untuk push serentak ke banyak cabang. Kedua, memaksimumkan daya pemprosesan bacaan supaya klien boleh memuat turun data cukup pantas untuk menggunakan sepenuhnya lebar jalur rangkaian yang tersedia.

Kami mencapai matlamat ini sambil mengekalkan keserasian dengan protokol dan klien Git, supaya jurutera dan ejen kami dapat menerapkan Pando dengan lancar dalam aliran kerja sedia ada. Dalam persekitaran pengeluaran, Pando mengurangkan kependaman median push kepada hampir satu perlima. Kini, Pando menghantar hampir satu petabait data Git sehari.

Di sini, kami akan menerangkan sebab kami membina sistem kawalan sumber baharu dan memilih untuk mengekalkan keserasian dengan Git. Kami akan membandingkan sistem kami dengan pendekatan lazim untuk menskalakan Git, serta menerangkan keupayaan unik Pando dalam memenuhi keperluan kebolehskalaan OpenAI. Kami turut membincangkan langkah seterusnya dan cara kami akan terus menyumbang kepada Git untuk menyokong kerja pada skala ejen.

Mengapa kami memerlukan sistem kawalan sumber baharu

Codex telah mengubah skala dan kepantasan pembangunan di OpenAI, membolehkan jurutera melakukan jauh lebih banyak kerja secara selari. Ini bermakna lebih banyak cabang dan push, lebih banyak penggabungan, serta lebih banyak fetch dan klon daripada persekitaran pembangunan, tugas penyelidikan dan CI.

Tuntutan ini bertambah secara berangkai. Seorang jurutera kini mempunyai beberapa perubahan yang sedia untuk CI serentak, dan setiap push boleh mencetuskan beberapa tugas untuk mengambil komit yang sama.

Sekitar penghujung tahun lalu, sistem yang menyokong monorepo kami mula sukar menampung tuntutan ini. Kami mengalami fetch dan push yang perlahan serta kegagalan penggabungan, dengan kebolehpercayaan ada kalanya jatuh di bawah 90%. Adakah kami telah mencapai had Git itu sendiri, atau had cara kami menyediakan monorepo? Git lahir daripada komuniti yang bertukar-tukar tampalan kernel melalui e-mel. Selama lebih dua dekad, pembangun menyesuaikan alat mereka berasaskan Git seiring perubahan cara mereka membina perisian. Apabila kami mula menghadapi kesukaran dalam monorepo sendiri, penting bagi kami untuk terus membina berasaskan usaha itu.

Kami melihat cara untuk mengekalkan aliran kerja yang biasa digunakan sambil membina semula sistem bahagian belakang agar dapat mengendalikan push ke cabang berbeza secara selari dan menyediakan sejarah yang sama kepada ramai pembaca serentak. Menggunakan Git sebagai asas aliran kerja kawalan sumber bermakna penambahbaikan pada klien boleh memberi manfaat di luar OpenAI. Kami mahu menyumbangkan penambahbaikan daripada salinan Git kami⁠(dibuka dalam tetingkap baru) kepada projek huluan, untuk dikongsi dengan komuniti yang lebih luas.

Kejuruteraan untuk Git berskala besar

Penskalaan sistem kawalan sumber mempunyai dua dimensi penting: ramai penulis melakukan push ke cabang berbeza, dan ramai pembaca mengambil sejarah yang sama. Pando dioptimumkan untuk kedua-duanya.

Pertama, Pando menyelaraskan kemas kini setiap cabang secara bebas. Kemas kini pada cabang yang tidak berkaitan boleh berjalan selari, dan pembaca yang mengambil cabang mendapat sejarah yang diperlukan untuk revisi itu. Namun, pembaca yang melihat beberapa cabang mungkin melihat keadaan cabang-cabang itu pada titik masa yang berbeza.

Kedua, Pando menggunakan semula fail pek (format Git untuk memampatkan berbilang objek bersama) merentas operasi fetch, bukan menjana fail pek khusus untuk setiap klien. Klien mungkin memuat turun sesetengah objek yang sudah dimiliki, tetapi pelayan boleh menghantar bait yang sama kepada ramai klien dengan pengiraan yang minimum.

Pilihan ini membolehkan kami menskalakan penulisan merentas cabang dan melayani bacaan daripada cache. Untuk memahami cara kedua-duanya saling melengkapi, mari mulakan dengan cara Git menyimpan dan memindahkan sejarah.

Asas storan objek Git

Git mesti dapat menyegerakkan dua bahagian repositori: objek dan rujukan.

Repositori Git merekodkan petikan fail dan sejarah yang menghubungkannya sebagai graf objek. Objek boleh berupa blob, pepohon, komit atau tag. Blob menyimpan kandungan satu fail, manakala pepohon mengaitkan nama dengan blob atau pepohon lain untuk membentuk struktur direktori. Komit menunjuk kepada pepohon akar dan sifar atau lebih komit induk, lalu membentuk sejarah repositori anda.

Objek dikenal pasti melalui cincangan jenis dan kandungannya. Objek juga tidak boleh diubah: merekodkan fail yang berubah mencipta blob baharu, manakala fail dan direktori yang tidak berubah boleh menggunakan semula objek sedia ada.

Cabang dan tag ialah rujukan, atau “ref”, yang menunjuk kepada komit. Melakukan komit pada cabang mencipta komit baharu dan mengemas kini ref cabang supaya menunjuk kepadanya.

Cabang refs/heads/main menunjuk kepada komit C2, yang induknya ialah C1. C1 menunjuk kepada pepohon akar T1, dengan parser.py merujuk kepada blob B1. C2 menunjuk kepada pepohon akar T2, dengan parser.py merujuk kepada blob B2. Dalam kedua-dua pepohon akar, docs/ merujuk kepada pepohon yang sama, T3. Entri README.md di dalamnya menunjuk kepada blob B3, dan usage.md menunjuk kepada blob B4.

Objek Git saling merujuk melalui ID: komit merujuk kepada pepohon, dan pepohon merujuk kepada pepohon lain serta blob. Cabang refs/heads/main menunjuk kepada komit C2, yang induknya ialah C1. C1 menunjuk kepada pepohon akar T1, dengan parser.py merujuk kepada blob B1. C2 menunjuk kepada pepohon akar T2, dengan parser.py merujuk kepada blob B2. Dalam kedua-dua pepohon akar, docs/ merujuk kepada pepohon yang sama, T3. Entri README.md di dalamnya menunjuk kepada blob B3, dan usage.md menunjuk kepada blob B4.

Git menyimpan objek dalam keadaan “berasingan” atau “dipek”. Objek berasingan disimpan dalam fail individu yang dikenal pasti melalui ID objeknya. Objek yang dipek disimpan bersama dalam fail pek, membolehkan objek serupa disimpan sebagai delta relatif antara satu sama lain.

Dua versi parser.py ditunjukkan di bawah $GIT_DIR/objects. Sebagai objek berasingan, blob A mengandungi parse(), manakala blob B mengandungi parse() dan parse_file(). Fail pek menyimpan A sepenuhnya dan mewakili B sebagai arahan untuk menyalin seluruh A serta menambah parse_file() di hujungnya. Fail *.idx yang disertakan menentukan lokasi kedua-dua blob dalam fail pek mengikut ID objek.

Git boleh menyimpan blob sebagai fail berasingan atau membungkusnya bersama, dengan mengekodkan satu blob sebagai delta relatif kepada blob lain.

Dalam Git, objek boleh dicapai daripada sesuatu komit jika Git dapat menemuinya dengan mengikuti pautan daripada komit itu melalui induk, pepohon dan blobnya. Semasa fetch, klien memberitahu pelayan komit yang dikehendaki dan yang sudah dimilikinya. Pelayan mencari objek yang boleh dicapai daripada komit yang dikehendaki, mengecualikan objek yang diketahui sudah dimiliki klien, lalu menghantar selebihnya sebagai fail pek dalam format yang sama seperti yang digunakan Git pada cakera. Klon penuh bermula tanpa sebarang objek, jadi ia memerlukan semua objek yang boleh dicapai daripada sejarah yang diklon.

Push memindahkan objek ke arah bertentangan dengan meminta pelayan mengemas kini satu atau lebih ref. Klien menggunakan set ref yang diumumkan pelayan untuk mengelakkan penghantaran objek yang sudah dimiliki pelayan. Oleh itu, pek yang dihantar boleh bergantung pada objek sedia ada: menerima pek itu sahaja tidak mencukupi untuk membina semula sejarah baharu.

Mereplikasi repositori Git bermakna memastikan objek dan rujukan tersedia merentas berbilang pelayan. Sebelum replika boleh menyediakan nilai baharu sesuatu rujukan, replika mesti dapat membekalkan objek yang boleh dicapai daripada nilai itu. Perkhidmatan ini juga perlu menyelaraskan kemas kini rujukan yang bersaing, iaitu push yang cuba menggerakkan cabang yang sama daripada hujung lama yang sama kepada komit baharu yang berbeza. Sistem yang mengenakan satu urutan bagi semua tulisan ke repositori boleh menyebabkan push ke cabang yang tidak berkaitan terpaksa menunggu satu sama lain.

Corak perkhidmatan Git teragih sedia ada

Semasa mereka bentuk Pando, kami menilai pendekatan konvensional untuk menskalakan kawalan sumber menggunakan Git: satu menyelaraskan kemas kini ref merentas replika repositori melalui komit tiga fasa (3PC, protokol untuk menyelaraskan komit merentas berbilang nod); satu lagi mengatur kemas kini melalui log tulis awal bersama (WAL, rekod perubahan yang ditulis sebelum diterapkan) dalam storan blob.

Komit tiga fasa (3PC)

Dalam reka bentuk komit tiga fasa yang kami modelkan, perkhidmatan Git menyimpan salinan lengkap repositori pada beberapa replika. Apabila mana-mana pelayan menerima push, fail pek masuk disalin dan dihantar kepada semua replika secara selari. Untuk menerbitkan push, majoriti replika mesti menerima kemas kini ref. Pelayan yang menguruskannya bertindak sebagai penyelaras, dan proses ini memerlukan beberapa pusingan komunikasi antara penyelaras dengan setiap replika lain.

Untuk menerima kemas kini rujukan, setiap replika mengunci salinan refnya dan memastikan ref itu masih menunjuk kepada komit yang dijangka klien. Penyelaras kemudian menghantar mesej prakomit kepada replika yang menerima kemas kini itu. Setelah majoriti mengakui penerimaan mesej prakomit, penyelaras mengarahkan mereka melakukan kemas kini. Mana-mana replika yang terkini kemudian boleh mengumumkan hujung baharu cabang daripada salinan setempat repositorinya.

Penyimpanan replika lengkap membolehkan bacaan dilayani secara setempat dan menjamin klien mendapat paparan konsisten bagi semua rujukan, tanpa mengira replika yang dipilih. Akibatnya, rujukan dikemas kini satu demi satu, dan setiap push memerlukan beberapa perjalanan pergi balik untuk menyelaraskan kemas kini ref antara replika.

Keperluan menunggu kuorum menjadikan prestasi cakera replika dan kependaman komunikasi sebahagian daripada laluan push. Apabila volum penulisan meningkat, replika mesti mampu menampung pek masuk serta trafik bacaan.

Log tulis awal (WAL)

Dalam reka bentuk berasaskan WAL yang kami modelkan, perkhidmatan Git menggunakan manifes bersama dalam storan blob untuk mengatur urutan kemas kini di seluruh repositori. Apabila pelayan menerima push, pelayan memuat naik fail pek masuk ke stor blob dan menyediakan kemas kini ref dalam salinan setempat repositorinya. Untuk menerbitkan push, pelayan menambah entri pada manifes bersama dalam stor blob yang merekodkan kemas kini ref dan menunjuk kepada fail pek. Pelayan Git kemudian mengemas kini manifes melalui operasi banding-dan-tukar. Operasi ini menggantikan manifes secara atomik, atau menolak perubahan itu. Jika penulis lain telah mengemas kininya sementara itu, pelayan membaca entri yang ditambah, memastikan kemas kini refnya masih sah, lalu mencuba semula menggunakan fail pek yang sudah dimuat naik.

Penulis boleh memuat naik fail pek secara selari, tetapi push ke cabang yang tidak berkaitan sekalipun perlu mengemas kini manifes yang sama. Masa pergi balik bagi operasi banding-dan-tukar yang berjaya mengehadkan kekerapan repositori boleh menerbitkan manifes baharu. Stor blob berkependaman rendah mempercepat langkah ini. Pemprosesan kelompok membolehkan satu manifes mengemas kini beberapa ref serentak, tetapi memerlukan kemas kini beratur dan meningkatkan kependaman.

Stor blob boleh menerima fail pek secara selari, tetapi kemas kini pada manifes bersama masih bersaing antara satu sama lain. Menambah kapasiti stor blob sahaja tidak menyelesaikan kesesakan itu. Setiap replika masih perlu membaca dan mengindeks pek daripada stor blob sebelum boleh menyediakannya secara setempat. Jika tulisan masuk lebih pantas daripada kerja itu, replika akan ketinggalan dan klien perlu menunggu lebih lama untuk mengambil komit yang baru dihantar melalui push.

Mereka bentuk seni bina Pando

Di sinilah Pando memainkan peranan. Pando ialah seni bina baharu untuk menyediakan repositori Git, dengan matlamat menskalakan perkhidmatan bagi ramai klien yang melakukan push serentak. Begini cara ia berfungsi:

Menilai semula kekangan

Sebelum membangunkan Pando, kami menjangka bahawa kami boleh memenuhi keperluan kami tanpa menjejaskan keserasian Git. Namun, kami perlu melonggarkan beberapa perkara yang dianggap sebagai keperluan oleh kebanyakan sistem Git:

  • Ketekalan seluruh repositori: Menyediakan petikan konsisten bagi semua ref pada satu titik masa memerlukan penyelarasan merentas repositori. Dalam sistem yang mengatur semua kemas kini ref dalam satu urutan, push ke cabang yang tidak berkaitan bersaing pada satu titik penyelarasan.

  • Fail pek unik untuk setiap klien: Jika dua klien dengan sejarah setempat yang berbeza mengambil cabang yang sama, pelayan mungkin perlu menjana pek berbeza untuk setiap klien agar tidak menghantar objek yang sudah dimiliki. Menjana respons ini menggunakan CPU dan I/O cakera, lalu menyukarkan penskalaan untuk menampung jumlah pembaca yang tinggi.

Sebaliknya, Pando menyediakan model ketekalan yang lebih longgar (ketekalan sebab-akibat) untuk menskalakan penulisan, dan menggunakan semula fail pek untuk menskalakan pembacaan.

Ketekalan sebab-akibat (CC)

Apabila klien melakukan push ke cabang, Pando menyimpan fail pek masuk sebagai blob yang tidak boleh diubah dalam stor blob. Setiap kali cabang bergerak, Pando merekodkan versi ref dalam stor metadata. Versi ini mengandungi hujung baharu cabang, pek yang membekalkannya dan mana-mana versi ref terdahulu yang menjadi kebergantungan pek itu. Stor blob dan stor metadata kedua-duanya boleh diskalakan secara mendatar.

Mengambil komit atau rujukan daripada Pando memerlukan satu set versi ref (fail pek) dimainkan semula kepada klien, untuk membina semula keadaan klien yang mencipta versi ref yang diambil. Ini menyediakan bentuk ketekalan yang lebih lemah di seluruh repositori, sambil memastikan kemas kini pada setiap ref boleh disirikan. Klien hanya berkonflik apabila melakukan push ke ref yang sama secara serentak. Klien yang membaca beberapa cabang tidak boleh menganggap bacaan itu menggambarkan satu titik masa yang sama di seluruh repositori.

Di sebelah kiri, tiga pek cabang utama berturutan mengandungi komit A, B dan C; D dan E; serta F. Di sebelah kanan, cabang utama dan cabang topik berpisah pada C. Cabang utama bergerak ke G, manakala cabang topik bergerak melalui D, E dan F. Pek cabang topik terkini mengandungi D, E dan F. Garis luar putus-putus menandakan bahawa D dan E turut ada dalam pek cabang topik terdahulu. Anak panah biru menandakan kebergantungan pek baharu pada cabang utama di G. Induk Git bagi F ialah E.

Push ke cabang utama menambah pek inkremental yang mengandungi satu atau lebih komit. Setiap push ke cabang sampingan menyertakan semula komit terdahulu cabang topik itu, supaya klien yang mempunyai versi cabang utama yang diumumkan boleh mengambil cabang topik tersebut dalam satu pek.

Ini ialah satu bentuk ketekalan sebab-akibat. Untuk mengambil rujukan, pembaca melihat sejarah penulis yang menghasilkannya. Semasa push, Pando mengawal sejarah bersama yang dipersetujui klien dan pelayan, supaya klien menghasilkan fail pek yang lebih mudah dikongsi.

Oleh itu, Pando tidak menyediakan keadaan semua rujukan pada satu titik masa semasa operasi Git, atau urutan kemas kini ref dalam operasi klon atau fetch. Sebagai contoh, anggap cabang foo dan bar kedua-duanya dikemas kini semasa rundingan klon penuh. Semasa rundingan itu, satu klien mungkin melihat foo pada versi 1 dan bar pada versi 2, manakala klien lain melihat foo pada versi 2 dan bar pada versi 1. Dalam amalan, pengguna dan ejen tidak bergantung pada urutan global kemas kini ref, sebaliknya bergantung pada urutan kemas kini satu rujukan.

Membandingkan pendekatan

Dalam simulasi tiga model ketekalan ini, kami mengkonfigurasi 1,000 klien supaya setiap satu memilih cabang berbeza, kemudian melakukan push komit unik berulang kali. Kami mengukur jumlah masa sebenar yang berlalu dan kependaman dari push hingga fetch. Metrik kedua penting untuk beban kerja kerana mengukur masa pergi balik bagi pengguna dan ejen untuk bekerjasama atau menggunakan sistem lain yang bergantung pada kawalan versi. Simulasi ini memodelkan perkakasan yang merangkumi mesin maya dan sistem storan blob daripada kelas standard hingga premium. Dalam simulasi ini, kami melonggarkan ketekalan seluruh repositori untuk komit tiga fasa.

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.

Jalankan simulasi ini untuk membandingkan Pando dengan dua pendekatan lain. Pilih infrastruktur daripada perkakasan standard hingga premium, bilangan klien dan tetapan lain untuk melihat perbandingannya.

Sistem komit tiga fasa yang dimodelkan menunjukkan kependaman tulis yang jauh lebih tinggi di bawah beban apabila cakeranya mencapai kapasiti maksimum. Sistem log tulis awal pula menunjukkan kependaman baca yang jauh lebih tinggi atas sebab yang sama, apabila pelayan sukar mengimbangi bacaan daripada jurnal. Dalam ujian sebenar pada monorepo kami dan dalam simulasi ini, Pando menyokong beribu-ribu klien yang melakukan push serentak tanpa menjejaskan prestasi di bawah beban.

Semasa mengendalikan push, Pando mengesahkan fail pek masuk sambil menstrimkannya ke storan blob, tanpa menyahpek fail itu ke repositori setempat. Apabila klien memulakan fetch, Pando memilih pek tersimpan yang diperlukan pembaca, lalu pembaca memuat turunnya. Tiada pelayan perlu menerapkan fail pek pada repositori setempat atau menjalankan operasi nyahpek dan pembungkusan yang mahal. Oleh sebab pelayan ini terutamanya menjalankan rundingan dan pemilihan fail pek, pelayan Pando boleh beroperasi pada perkakasan standard.

Ini membolehkan Pando diskalakan hingga mencapai had klien, rangkaian antara klien dengan Pando, dan perkhidmatan storan blob. Dengan kapasiti yang mencukupi, setiap klien yang bekerja pada cabang bebas boleh memindahkan pek sepantas yang dibenarkan sambungannya sendiri. Untuk melayani lebih banyak klien pada kadar itu, kami boleh menambah pelayan Pando serta menskalakan stor metadata dan stor blob seiring dengannya.

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

Ejen menyunting fail dan mencipta komit secara setempat, kemudian menjalankan git push, yang mencipta fail pek perubahan tersebut untuk mengemas kini cabang. Pando menerima pek itu dan menstrimkannya ke storan blob. Selepas mengesahkan push masuk melalui penyahkodan satu laluan, Pando menggunakan transaksi berbilang kunci untuk merekodkan versi ref dalam stor metadata kami dan mengemas kini penuding ref secara atomik. Ini membolehkan Pando menyediakan ketekalan “baca tulisan sendiri”. Keseluruhan operasi tulis ini memerlukan O(1)O(1) bacaan dan tulisan metadata.

Apabila klien lain mengambil cabang itu, Pando memilih satu set pek tersimpan berdasarkan sejarah yang sudah dimiliki klien. Operasi ini memerlukan O(log⁡Δ)O(\log \Delta) bacaan metadata, dengan Δ\Delta ialah jarak terpendek dalam bilangan versi ref antara apa yang dimiliki klien dengan apa yang dikehendakinya. Pelayan mengembalikan URI fail pek, dan klien memuat turunnya melalui Express, perkhidmatan yang menyimpan fail pek dalam cache serantau.

Reka bentuk ini bergantung pada tiga perkhidmatan teras seni bina Pando: push, fetch dan pemadatan.

Menskalakan push

Apabila klien melakukan push, pelayan Git konvensional ibarat pemain poker yang menunjukkan semua kadnya, mendedahkan setiap cabang yang dimilikinya kepada klien. Klien kemudian mengumpulkan komit, pepohon dan blob untuk mencipta fail pek yang mengecualikan objek yang diketahuinya sudah dimiliki pelayan.

Mengumumkan lebih banyak ref boleh mengecilkan saiz push. Namun, pek itu juga mungkin lebih sukar digunakan semula kerana klien lain mungkin tidak memiliki objek yang dikecualikan oleh penulis.

Pando mengambil pendekatan berbeza. Daripada menunjukkan semua kadnya, Pando hanya mengumumkan satu set cabang terpilih kepada klien. Ini dilakukan atas dua sebab:

  1. Menyediakan paparan konsisten bagi setiap ref memerlukan kos yang tinggi. Dengan jutaan cabang, pengumuman ref sahaja boleh melebihi 100 MiB. Untuk menyediakannya, setiap pelayan perlu mengekalkan pemetaan terkini dengan overhed penyelarasan yang besar, atau menjalankan pertanyaan pangkalan data yang meningkat mengikut bilangan ref.

  2. Dengan hanya menyediakan subset ref kepada klien, klien akan menjana pek yang bergantung hanya pada objek yang dibenarkan secara jelas oleh pelayan untuk dikecualikan. Penjejakan kebergantungan membolehkan Pando menggunakan semula fail pek yang dijana klien dalam satu push sebagai respons kepada fetch berikutnya.

Semakin banyak ref diumumkan oleh pelayan Git, semakin banyak sumber yang boleh digunakan klien untuk mengecualikan objek semasa push, lalu menghasilkan pek yang lebih kecil. Namun, untuk menggunakan semula fail pek ini, pelayan mesti dapat membuktikan bahawa klien yang melakukan fetch memiliki setiap kebergantungan. Jika tidak, fail pek mungkin tidak menyertakan objek yang diperlukan klien.

Sebaliknya, jika pelayan Git tidak mengumumkan sebarang ref, klien akan menjana fail pek yang mengandungi seluruh sejarah repositori. Fail pek itu boleh digunakan oleh mana-mana klien lain, tetapi kosnya tinggi kerana mengambil mana-mana cabang pada dasarnya menjadi operasi klon penuh.

Lengkung yang menunjukkan kesan rujukan yang diumumkan terhadap saiz fail pek: daripada sejarah penuh apabila tiada rujukan diumumkan, kepada pengumuman cabang utama, hingga hanya objek baharu apabila semua rujukan diumumkan.

Mengumumkan lebih banyak ref menghasilkan fail pek yang lebih kecil. Sebahagian besar pengurangan saiz boleh dicapai dengan mengumumkan satu ref sahaja.

Secara lalai, apabila klien melakukan push cabang ciri baharu, Pando hanya mengumumkan cabang utama yang ditetapkan sebagai asas. Ini mengekalkan saiz pengumuman walaupun bilangan cabang repositori bertambah, tetapi ada kekurangannya. Apabila jurutera melakukan push berulang kali ke satu cabang, atau membina satu cabang ciri di atas cabang ciri lain, klien perlu menghantar semula semua yang berada di luar cabang utama.

Ini menjadikan push cabang berturutan lebih besar kerana mungkin mengandungi objek berulang, serta menggunakan lebar jalur klien tambahan. Dalam monorepo kami, saiz median fail pek yang dihasilkan dengan cara ini kurang daripada 20 KiB, dan persentil ke-99 kurang daripada 3 MiB. Bagi kami, kompromi ini munasabah kerana pembaca yang sudah memiliki sejarah cabang utama yang diumumkan boleh mengambil cabang itu dengan satu fail pek tersebut.

Selepas memilih set ref untuk diumumkan kepada klien, Pando menerima push dan mengemas kini stor metadatanya. Pando membahagikan kemas kini metadata kepada bahagian segerak dan tak segerak. Kemas kini metadata segerak memastikan klien boleh segera mengambil komit yang baru dikemas kini. Ini menulis versi ref baharu dan mengemas kini pemetaan penuding ref kepada versi itu, yang menyimpan hujung baharu, kebergantungan yang digunakan untuk merundingkan fail pek, serta pautan ke fail pek yang dihantar melalui push.

Pada masa yang sama, kerja tak segerak, seperti penjadualan pemadatan, direkodkan dalam transaksi yang sama. Untuk push yang sangat besar, hanya subset komit ditulis secara segerak. Ini membolehkan kami memerlukan hanya bilangan bacaan dan tulisan yang tetap pada stor metadata, tanpa mengira saiz push.

Yang penting, pelayan Pando boleh menerima push ke ref yang tidak berkaitan secara bebas. Satu-satunya kunci yang diperebutkan ialah penuding ref, yang kami lindungi dengan operasi atomik seumpama banding-dan-tukar. Kami boleh meningkatkan daya pemprosesan penulisan dengan menambah pelayan Pando, unit penskalaan stor metadata atau stor blob.

Prestasi fetch dan klon pada kelajuan penuh talian

Apabila klien melakukan fetch, klien menyediakan dua set ID komit kepada pelayan, yang dikenali dalam protokol sebagai have dan want. Dalam pelayan Git konvensional, pelayan mengira perbezaan set antara objek yang boleh dicapai daripada komit yang dimiliki dengan objek yang boleh dicapai daripada komit yang dikehendaki, memampatkannya dan menghantar hasilnya kepada klien. Walaupun pelayan Git sumber terbuka mempunyai pelbagai indeks setempat untuk mempercepat operasi ini, penjanaan fail pek boleh memerlukan pengiraan yang tinggi dan lazimnya sukar dicache.

Sebaliknya, Pando menggunakan pemetaan komit yang direkodkan semasa push untuk mencari have dan want klien, lalu memadankannya dengan satu set versi ref yang mengandungi komit tersebut. Setiap versi ref itu berpaut kepada fail pek yang pernah diterima oleh Pando. Pelayan menggunakan maklumat ini untuk menentukan keperluan klien, dengan memilih fail pek yang meliputi set objek yang diperlukan.

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 memetakan komit yang dimiliki dan dikehendaki dalam protokol Git kepada urutan versi ref. Meminta cabang sampingan turut memerlukan sejarah cabang utama sehingga titik kebergantungan cabang itu.

Protokol Git hanya membenarkan satu fail pek dihantar dalam respons HTTP semasa operasi klon atau fetch. Namun, ciri protokol Git yang kurang diberi perhatian, packfile-uri, membolehkan pelayan mengembalikan pautan untuk memuat turun pek tambahan secara berasingan. Pando menggunakan kaedah ini secara eksklusif dan menghantar fail pek kosong dalam respons itu sendiri. Untuk menyokong ciri ini, OpenAI menyumbangkan beberapa penambahbaikan packfile-uri kepada projek huluan.

Sistem OpenAI beroperasi di banyak rantau geografi, jadi kami mengekalkan kluster Pando dengan cache setempat di setiap rantau. Perkhidmatan penghantaran pek, Express, mencache stor blob kami, manakala perkhidmatan berasingan mencache stor metadata. Cache ini membolehkan Pando melayani permintaan untuk ID komit atau bacaan khas ref sepenuhnya daripada keadaan yang dicache dalam kluster itu. Kami menggunakan pencincangan konsisten untuk menghalakan permintaan fail pek ke pelayan Express, dengan kadar padanan cache melebihi 99%.

Sistem cache ini menyediakan daya pemprosesan yang amat tinggi, mampu menggunakan sepenuhnya kapasiti rangkaian antara klien dengan Pando. Dengan URI fail pek dan cache setempat ini, kami telah mengesahkan bahawa operasi klon dan fetch boleh menggunakan sepenuhnya sambungan rangkaian 10 Gbps, hanya dengan mesin maya standard daripada penyedia awan.

Pemadatan dan masa hingga bait pertama

Bagi kebanyakan pelayan Git, pembungkusan semula dan pengumpulan sampah diperlukan untuk mengekalkan prestasi repositori yang semakin berkembang. Kedua-dua langkah ini membebaskan ruang, tetapi memerlukan pengiraan dan I/O yang intensif.

Dalam repositori yang berubah pantas seperti monorepo OpenAI, tidak praktikal untuk menghantar URI fail pek individu bagi setiap push sejak fetch terakhir. Setiap hari, klien perlu memuat turun puluhan ribu fail pek untuk mengemas kini cabang utamanya.

Sebaliknya, Pando menggabungkan pek di latar belakang untuk mengurangkan overhed ini, dengan reka bentuk yang menyerupai pemadatan dalam pepohon gabungan berstruktur log. Setiap push menghasilkan apa yang disebut Pando sebagai fail pek “L0”, iaitu unit data terkecil yang boleh dihantar oleh Pando. Lama-kelamaan, proses pekerja latar belakang mengumpulkan fail pek ini menjadi kumpulan liputan yang merangkumi urutan versi ref, dengan skala meningkat secara geometri daripada L0. Kini, monorepo kami menggunakan kumpulan liputan fail pek hingga tahap L4.

Kumpulan liputan ini mempunyai dua fungsi. Apabila kumpulan liputan cukup penuh, proses pekerja pemadatan akan mengambil alih pemprosesannya dan menghasilkan satu fail pek. Ini meningkatkan nisbah mampatan dengan menyusun semula objek dalam fail pek. Ia juga meningkatkan prestasi rundingan fetch dengan bertindak sebagai senarai langkau bagi versi ref. Dengan mengimbas indeks pada setiap tahap secara serentak, Pando boleh memilih satu set pek untuk dihantar kepada klien dalam masa logaritma. Kumpulan liputan fail pek ini, sama ada dibungkus semula atau tidak, membolehkan perancangan fetch yang lebih cekap, sekali gus mengurangkan keperluan daya pemprosesan dan kependaman Pando.

Compaction in Pando

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

Compaction in progressPublished cover

Pemadatan menggabungkan fail pek daripada banyak versi ref di latar belakang, membolehkan fetch yang lebih cekap dengan lebih sedikit fail.

Tidak seperti kebanyakan pelayan Git, fail pek disimpan pada setiap tahap dan tidak dibuang atau dihapuskan melalui pengumpulan sampah. Pek pada setiap tahap digunakan untuk melayani klien secara berterusan, berdasarkan have dan want yang dinyatakan dalam permintaan fetch. Bagi klien yang menggunakan Pando sebagai API, membaca objek sebarang daripada fail pek lazimnya memerlukan bacaan data yang lebih sedikit daripada L0 berbanding L4, kerana objek itu kurang berkemungkinan dimampatkan.

Pemadatan dalam Pando mengurangkan jumlah saiz pemindahan kepada klien dengan ketara, serta membolehkan perancangan pek berkembang secara sublinear berbanding sejauh mana klien ketinggalan. Ini amat penting untuk repositori besar yang berubah pantas, yang kami jangka akan menjadi kelaziman baharu dalam pembangunan perisian.

Membina Pando

Kami membina Pando dengan Codex, daripada permulaan projek hingga penggunaan dalam pengeluaran, dalam hanya 10 minggu. Teras Pando direka oleh seorang jurutera, diimplementasikan oleh pasukan ejen dan digunakan dalam operasi oleh beberapa jurutera. Pando mula melayani trafik pengeluaran pada April tahun ini, dan merupakan sebahagian daripada sistem alat pengurusan kawalan sumber yang lebih besar di OpenAI.

Kami menggunakan pelbagai pendekatan untuk mengesahkan reka bentuk Pando. Kami menerapkan prinsip kami untuk membina asas perisian yang lebih selamat⁠. Sebagai projek yang dibina dari awal, kami memilih untuk mengimplementasikan Pando dalam Rust tanpa kebergantungan legasi. Kami menulis suite ujian pematuhan supaya ejen boleh menguji perubahan mereka terhadap tingkah laku standard Git. Kami turut membangunkan harnes ujian simulasi deterministik untuk memeriksa invarian dan sifat sistem. Ini membolehkan kami mencari dan mengurangkan keadaan perlumbaan yang sukar dikesan serta regresi dalam rundingan dan perancangan pek.

Banyak model menyumbang kepada sistem ini; apabila model baharu tersedia, kami menggunakannya untuk meningkatkan prestasi dan kebolehpercayaan Pando. Dengan GPT‑6 Astra, ejen menulis kod Lean dan TLA+ untuk memformalkan “teori Pando” yang kami gunakan bagi mencari dan membaiki pepijat dalam implementasi serta membuktikan sifat sistem. Beberapa sesi berterusan berjalan selama beratus-ratus jam untuk mencari dan membaiki kes pinggir dalam Pando. Kami juga menjalankan ejen sepanjang masa untuk menguji Git dan Pando secara adversarial menggunakan model berkeupayaan Daybreak.

Melaksanakan Pando

Pada awal pelaksanaan, Pando berfungsi sebagai proksi yang meneruskan bacaan dan tulisan kepada sistem kawalan sumber sedia ada. Ini membolehkan kami memastikan Pando dan sistem lama sentiasa disegerakkan, supaya trafik boleh dipindahkan ke Pando dengan selamat tanpa masa henti.

Sebagai sebahagian daripada reka bentuk cachenya, Pando juga boleh menyahgandakan dan menggabungkan permintaan sebelum meneruskannya melalui proksi. Ini membolehkan alat sedia ada terus berfungsi dengan baik semasa kami memindahkan sistem ke Pando satu demi satu. Apabila lebih banyak beban kerja dipindahkan ke Pando, push daripadanya mengisi cache Pando dan meningkatkan kebolehpercayaan sistem bahagian belakang lama kami.

Untuk mengoptimumkan prestasi penggunaan Pando, kami mula mengedarkan versi Git awam kami ke mesin pembangun dan dalam aplikasi desktop ChatGPT. Untuk memindahkan klien ke Pando secara berperingkat, kami menggunakan pembalut dalaman bagi CLI Git. Kami mencipta banyak alat lain untuk mengendalikan permintaan perubahan dan semakan kod, serta API untuk automasi. Selain trafik Git, kami memindahkan ratusan integrasi yang bergantung pada API legasi, webhook dan muat turun repositori.

Codex sendiri membantu pembangun dan pasukan melakukan peralihan itu. Kami mencipta kemahiran untuk menyediakan salinan kerja sedia ada dan membimbing ejen menggunakan sistem baharu. Hasilnya, pembangun hanya perlu meminta Codex memindahkan persekitaran pembangunan dan perkhidmatan mereka.

Peralihan ke Pando meningkatkan kebolehpercayaan penggabungan dan mengurangkan kependaman median push hampir 80%.

Pando boleh menghantar satu petabait data Git sehari dengan hanya beberapa teras CPU. Pando pernah menghantar lebih 57 terabait pek Git dalam satu jam, dengan lebih 99% bait tersebut datang daripada cache cakera setempat.

Penggunaan semula pek membolehkan ramai klien memuat turun sejarah repositori yang sama dengan pengiraan minimum pada nod yang melayani permintaan. Contoh ini menunjukkan satu repositori, tetapi Pando direka untuk menyokong jauh lebih banyak. Apabila permintaan meningkat, kami boleh menskalakan penghantaran data dan rundingan Git secara berasingan.

Menyumbang semula kepada projek Git

Membina Pando berasaskan protokol dan klien Git membantu kami mengenal pasti pepijat dan peluang untuk meningkatkan prestasi Git sendiri.

Contohnya, Pando menghantar pek boleh guna semula terus kepada klien, jadi muat turun dan pengindeksan pek itu menjadi unsur utama untuk menjadikan sistem pantas dan boleh dipercayai. Usaha itu mendedahkan kes pinggir dalam muat turun serentak, pembersihan selepas pemindahan terganggu dan penyambungan semula muat turun yang sudah selesai. Kami membaiki laluan kod tersebut, menambah pengesahan apabila menerima pek sedia ada untuk fetch, dan menyumbangkan hasil kerja itu kepada projek huluan.

Kami juga perlu mengetahui penggunaan masa dan lebar jalur, jadi kami menambah penjejakan muat turun packfile-uri dan kiraan bait bagi penjanaan pek. Ini memudahkan kami menentukan sama ada fetch yang perlahan menghabiskan masa untuk menjana pek atau memindahkannya.

Dalam binaan Git OpenAI⁠(dibuka dalam tetingkap baru), klien boleh memuat turun dan mengindeks pek bebas secara selari. Kami menambah konsep “komit dimiliki yang dinyatakan secara eksplisit” supaya pelayan boleh mengumumkan hujung semasa cabang secara berasingan daripada sejarah yang boleh dikecualikan daripada pek oleh klien yang melakukan push. Dalam Pando, ini membolehkan klien menggunakan --force-with-lease untuk memastikan cabang ciri belum bergerak dan membina peknya berasaskan cabang utama sahaja. Sesiapa yang mempunyai sejarah cabang utama itu kemudian boleh mengambil cabang tersebut menggunakan pek yang sama.

Beberapa penambahbaikan ini sudah tersedia dalam Git huluan, dan banyak lagi dalam repositori awam kami. Kami terus bekerjasama dengan komuniti Git untuk memastikan penambahbaikan ini bermanfaat di luar Pando. Anda boleh mengikuti usaha itu melalui penjejak tampalan awam⁠(dibuka dalam tetingkap baru) kami.

Langkah seterusnya

Dengan menilai semula pandangan lazim tentang cara menskalakan repositori Git, Pando membolehkan kami menskalakan monorepo tanpa mengubah aliran kerja Git yang sudah digunakan jurutera dan ejen kami. Dalam monorepo kami, Pando membolehkan lebih banyak kerja dilakukan selari, meningkatkan kebolehpercayaan penggabungan dan memendekkan masa untuk menerbitkan perubahan kod. Selain monorepo, kami turut meneroka kegunaan lain Pando dalam OpenAI, termasuk beban kerja yang memerlukan sejumlah besar ejen bekerjasama pada repositori Git bersama.

Apabila jurutera dan ejen menghasilkan lebih banyak perisian, lebih banyak pasukan di luar OpenAI akan menghadapi had kawalan sumber yang telah kami alami. Kami sedang meneroka cara berkongsi teknologi yang mendasari Pando dengan pengguna kami.

Pando dibina berasaskan dua dekad pengalaman pengguna dan pengendali repositori Git. Kami akan terus membangunkan Pando sambil bekerjasama dengan komuniti Git sumber terbuka untuk berkongsi penambahbaikan yang bermanfaat kepada pengguna Git secara lebih meluas.

Penulis

OpenAI