OpenAI

2 Kasım 2026

Mühendislik

OpenAI’da Git’i ölçeklendirmek

Git ekosistemine yatırım yaparken ajanların çalışma ölçeğine uygun kaynak kodu kontrolü geliştirmek.

Yazanlar: Teknik ekip üyeleri Aaron Friel, Taylor Blau ve Ted Nyman

Yükleniyor...

Geçtiğimiz yıl monorepomuzun boyutu ve karşılaması gereken talepler baş döndürücü bir hızla arttı. Codex’in mümkün kıldığı eş zamanlı çalışma düzeyine ulaşabilmek için kaynak kodu kontrol sistemimizi yenilememiz gerektiği açıktı.

Mühendislik çalışmalarını kesintiye uğratmadan veya ekibimizin Git’i kullanma biçimini değiştirmeden, Git depoları için yeni bir arka uç olan Pando’yu geliştirdik. OpenAI’ın monoreposundaki tüm geliştirme çalışmaları artık Pando üzerinde yürütülüyor. Pando iki iddialı hedef için tasarlandı: İlki, çok sayıda dala eş zamanlı gönderimler için optimize edilmiş, yatay ölçeklenebilir yazma işlemleri. İkincisi ise istemcilerin mevcut ağ bant genişliğinin tamamını kullanacak hızda veri indirebilmesi için okuma işlem kapasitesini en üst düzeye çıkarmak.

Git protokolü ve istemcisiyle uyumluluğu koruyarak bu hedeflere ulaştık. Böylece hem mühendislerimiz hem de ajanlarımız Pando’yu mevcut iş akışlarına sorunsuzca dâhil edebildi. Üretim ortamında Pando, medyan gönderim gecikmesini yaklaşık beşte birine indirdi. Artık günde yaklaşık bir petabayt Git verisi sunuyor.

Bu yazıda, yeni bir kaynak kodu kontrol sistemi geliştirmemizin nedenlerini ve Git ile uyumluluğu neden korumayı seçtiğimizi ele alacağız. Sistemimizi Git’i ölçeklendirmeye yönelik yaygın yaklaşımlarla karşılaştıracak, Pando’nun OpenAI’ın ölçeklenebilirlik ihtiyaçlarını karşılamada neden benzersiz bir konumda olduğunu açıklayacağız. Ayrıca sıradaki adımlarımızı ve ajanların çalışma ölçeğini desteklemek için Git’e nasıl katkıda bulunmaya devam edeceğimizi anlatacağız.

Neden yeni bir kaynak kodu kontrol sistemine ihtiyaç duyduk?

Codex, mühendislerin çok daha fazla işi paralel yürütmesini sağlayarak OpenAI’daki geliştirme çalışmalarının ölçeğini ve hızını değiştirdi. Bu da daha fazla dal, gönderim ve birleştirme; geliştirme ortamlarından, araştırma görevlerinden ve CI’dan gelen daha fazla getirme ve klonlama işlemi anlamına geliyor.

Bu talepler birbirini katlayarak büyüyor. Artık tek bir mühendisin aynı anda CI için hazır birden fazla değişikliği oluyor ve her gönderim, aynı commit’i getiren birden fazla görevi başlatabiliyor.

Geçen yılın sonlarına doğru monorepomuzu destekleyen sistemler bu tempoya yetişmekte zorlanmaya başladı. Getirme ve gönderim işlemleri yavaşladı, birleştirmeler başarısız oldu ve güvenilirlik zaman zaman %90’ın altına düştü. Git’in kendi sınırına mı, yoksa monorepomuzu sunma biçimimizin sınırına mı ulaşmıştık? Git, çekirdek yamalarını e-postayla paylaşan bir topluluktan doğdu. Yirmi yılı aşkın süredir geliştiriciler, yazılım geliştirme biçimleri değiştikçe araçlarını Git etrafında uyarlıyor. Kendi monorepomuzda sorunlar yaşamaya başladığımızda, bu birikim üzerine çalışmaya devam edebilmek bizim için önemliydi.

Farklı dallara yapılan gönderimleri paralel işlemek ve aynı geçmişi çok sayıda okuyucuya eş zamanlı sunmak için arka ucu yeniden kurarken alıştığımız iş akışını korumanın bir yolunu gördük. Kaynak kodu kontrol iş akışımızın temelinde Git’in bulunması, istemcideki iyileştirmelerin OpenAI dışında da faydalı olabileceği anlamına geliyordu. Bu iyileştirmeleri Git kopyamızdan⁠(yeni bir pencerede açılır) ana projeye aktarıp daha geniş toplulukla paylaşmak istedik.

Büyük ölçekte Git için mühendislik

Bir kaynak kodu kontrol sistemini ölçeklendirmenin iki temel boyutu vardır: Farklı dallara gönderim yapan çok sayıda yazıcı ve aynı geçmişi getiren çok sayıda okuyucu. Pando her ikisi için de optimize edilmiştir.

İlk olarak Pando, her dalın güncellemelerini bağımsız olarak koordine eder. Birbiriyle ilgisiz dalların güncellemeleri paralel ilerleyebilir ve bir dalı getiren okuyucu, ilgili revizyon için gereken geçmişi alır. Bunun karşılığında, birden fazla dala bakan bir okuyucu bu dalların farklı anlardaki durumlarını görebilir.

İkinci olarak Pando, her istemci için ayrı bir paket dosyası üretmek yerine, paket dosyalarını (Git’in birden fazla nesneyi birlikte sıkıştırdığı biçim) farklı getirme işlemlerinde yeniden kullanır. Bir istemci zaten sahip olduğu bazı nesneleri indirebilir, ancak sunucu aynı baytları çok az hesaplamayla çok sayıda istemciye sunabilir.

Bu tercihler, yazma işlemlerini dallar arasında ölçeklendirmemizi ve okuma isteklerini önbelleklerden karşılamamızı sağlar. Bunların nasıl bir araya geldiğini görmek için önce Git’in geçmişi nasıl sakladığına ve aktardığına bakalım.

Git’te nesne depolamanın temelleri

Git, bir deponun iki bileşenini eş zamanlı tutabilmelidir: nesneler ve referanslar.

Bir Git deposu, dosyaların anlık görüntülerini ve bunları birbirine bağlayan geçmişi bir nesne grafiği olarak kaydeder. Bir nesne blob, ağaç, commit veya etiket olabilir. Blob’lar tek bir dosyanın içeriğini saklar. Ağaçlar ise adları blob’larla veya diğer ağaçlarla ilişkilendirerek dizin yapısını oluşturur. Commit’ler bir kök ağaca ve sıfır ya da daha fazla ebeveyn commit’e işaret ederek deponuzun geçmişini oluşturur.

Nesneler, türlerinin ve içeriklerinin karma değeriyle tanımlanır. Nesneler aynı zamanda değiştirilemez: Değiştirilmiş bir dosyayı kaydetmek yeni bir blob oluştururken, değişmeyen dosya ve dizinler mevcut nesneleri yeniden kullanabilir.

Dallar ve etiketler, commit’lere işaret eden referanslardır; kısaca “refs” olarak adlandırılır. Bir dalda commit oluşturulduğunda yeni bir commit kaydedilir ve dalın referansı bu commit’e işaret edecek şekilde güncellenir.

refs/heads/main dalı, ebeveyni C1 olan C2 commit’ine işaret eder. C1, parser.py dosyasının B1 blob’una referans verdiği T1 kök ağacına işaret eder. C2, parser.py dosyasının B2 blob’una referans verdiği T2 kök ağacına işaret eder. Her iki kök ağaçta da docs/ aynı ağaca, T3’e referans verir. Bu ağaçtaki README.md girdisi B3 blob’una, usage.md ise B4 blob’una işaret eder.

Git nesneleri kimlikleri aracılığıyla birbirlerine referans verir: Commit’ler ağaçlara, ağaçlar da diğer ağaçlara ve blob’lara referans verir. refs/heads/main dalı, ebeveyni C1 olan C2 commit’ine işaret eder. C1, parser.py dosyasının B1 blob’una referans verdiği T1 kök ağacına işaret eder. C2, parser.py dosyasının B2 blob’una referans verdiği T2 kök ağacına işaret eder. Her iki kök ağaçta da docs/ aynı ağaca, T3’e referans verir. Bu ağaçtaki README.md girdisi B3 blob’una, usage.md ise B4 blob’una işaret eder.

Git, nesneleri “ayrı” veya “paketlenmiş” durumda saklar. Ayrı nesneler, nesne kimlikleriyle tanımlanan ayrı dosyalarda saklanır. Paketlenmiş nesneler bir paket dosyasında birlikte saklanır. Böylece benzer nesneler, birbirlerine göre farkları alınarak saklanabilir.

$GIT_DIR/objects altında parser.py dosyasının iki sürümü görünür. Ayrı nesneler olarak blob A, parse() içerirken blob B hem parse() hem de parse_file() içerir. Paket dosyası A’yı bütünüyle saklar; B’yi ise A’nın tamamını kopyalayıp sonuna parse_file() ekleyen talimatlar olarak temsil eder. Eşlik eden *.idx dosyası, nesne kimliğine göre her iki blob’un paket dosyasındaki konumunu belirler.

Git, blob’ları ayrı dosyalar olarak saklayabilir veya bir blob’u diğerine göre fark olarak kodlayıp bunları birlikte paketleyebilir.

Git, bir commit’ten başlayıp ebeveyn commit’ler, ağaçlar ve blob’lar arasındaki bağlantıları izleyerek bir nesneyi bulabiliyorsa, o nesne söz konusu commit’ten erişilebilir durumdadır. Getirme sırasında istemci, hangi commit’leri istediğini ve hangilerine zaten sahip olduğunu sunucuya bildirir. Sunucu, istenen commit’lerden erişilebilen nesneleri bulur, istemcide zaten bulunduğunu bildiklerini hariç tutar ve kalanları Git’in diskte kullandığı biçimde bir paket dosyası olarak gönderir. Tam klonlama hiçbir nesne olmadan başlar; bu nedenle klonlanan geçmişten erişilebilen tüm nesnelere ihtiyaç duyar.

Gönderim işlemi, sunucudan bir veya daha fazla referansı güncellemesini isteyerek nesneleri ters yönde aktarır. İstemci, sunucuda zaten bulunan nesneleri göndermemek için sunucunun bildirdiği referans kümesini kullanır. Bu nedenle gönderilen paket mevcut nesnelere bağımlı olabilir; yalnızca paketi almak, yeni geçmişi yeniden oluşturmak için yeterli değildir.

Bir Git deposunu çoğaltmak, hem nesneleri hem de referansları birden fazla sunucuda erişilebilir tutmak anlamına gelir. Bir replika, bir referansın yeni değerini sunabilmek için önce bu değerden erişilebilen nesneleri sağlayabilmelidir. Hizmetin ayrıca birbiriyle yarışan referans güncellemelerini, yani aynı dalı aynı eski uç commit’ten farklı yeni commit’lere taşımaya çalışan gönderimleri koordine etmesi gerekir. Bir depoya yapılan tüm yazma işlemlerini tek bir sıraya koyan sistemler, birbiriyle ilgisiz dallara yapılan gönderimlerin birbirini beklemesine neden olabilir.

Mevcut dağıtık Git hizmeti yaklaşımları

Pando’yu tasarlarken, Git ile kaynak kodu kontrolünü ölçeklendirmeye yönelik geleneksel yaklaşımları değerlendirdik. Bunlardan biri, depo replikaları arasındaki referans güncellemelerini üç aşamalı onayla (3PC, birden fazla düğümde işlemin onaylanmasını koordine eden protokol) yönetir. Diğeri ise blob depolamadaki ortak bir ön yazma günlüğü (WAL, değişikliklerin uygulanmadan önce yazıldığı kayıt) aracılığıyla güncellemeleri sıraya koyar.

Üç aşamalı onay (3PC)

Modellediğimiz üç aşamalı onay tasarımında Git hizmeti, deponun tam bir kopyasını birden fazla replikada tutar. Herhangi bir sunucu bir gönderim aldığında, gelen paket dosyası kopyalanır ve tüm replikalara paralel olarak gönderilir. Gönderimin yayımlanması için replikaların çoğunluğunun referans güncellemesini kabul etmesi gerekir. Bu süreci yöneten sunucu koordinatördür ve koordinatör ile diğer her replika arasında birden fazla iletişim turu gerekir.

Bir referans güncellemesini kabul etmek için her replika, referansın kendi kopyasını kilitler ve hâlâ istemcinin beklediği commit’e işaret ettiğini kontrol eder. Koordinatör daha sonra kabul eden replikalara bir ön onay mesajı gönderir. Çoğunluk ön onay mesajını onayladığında koordinatör, güncellemeyi gerçekleştirmelerini söyler. Ardından güncel olan herhangi bir replika, deponun yerel kopyasından dalın yeni uç commit’ini bildirebilir.

Tam replikalar tutmak, okuma isteklerini yerel olarak karşılamayı mümkün kılar ve istemcilerin hangi replikayı seçerse seçsin tüm referansların tutarlı bir görünümünü almasını garanti eder. Bunun sonucunda referanslar tek tek güncellenir ve her gönderim, referans güncellemesini replikalar arasında koordine etmek için birden fazla gidiş-dönüş gerektirir.

Yeterli sayıda replikanın onayını beklemek, gönderim süresini replikaların disk performansına ve iletişim gecikmesine bağımlı kılar. Yazma hacmi arttıkça replikalar, okuma trafiğinin yanı sıra gelen paketlere de yetişmek zorundadır.

Ön yazma günlüğü (WAL)

Modellediğimiz WAL tabanlı tasarımda Git hizmeti, depo genelindeki güncellemeleri sıraya koymak için blob depolamada ortak bir tanım dosyası kullanır. Bir sunucu gönderim aldığında, gelen paket dosyasını blob deposuna yükler ve deponun yerel kopyasında referans güncellemesini hazırlar. Gönderimi yayımlamak için blob deposunda saklanan ortak tanım dosyasına, referans güncellemesini kaydeden ve paket dosyasına işaret eden bir girdi ekler. Git sunucusu daha sonra karşılaştır-ve-değiştir işlemiyle tanım dosyasını günceller. Bu işlem tanım dosyasını atomik olarak değiştirir; koşul sağlanmazsa değişikliği reddeder. Bu arada başka bir yazıcı dosyayı güncellemişse sunucu araya giren kayıtları okur, kendi referans güncellemesinin hâlâ geçerli olduğunu kontrol eder ve daha önce yüklediği paket dosyasını kullanarak yeniden dener.

Yazıcılar paket dosyalarını paralel yükleyebilir, ancak birbiriyle ilgisiz dallara yapılan gönderimler bile aynı tanım dosyasının güncellenmesini gerektirir. Başarılı bir karşılaştır-ve-değiştir işleminin gidiş-dönüş süresi, deponun ne sıklıkla yeni bir tanım dosyası yayımlayabileceğini sınırlar. Düşük gecikmeli bir blob deposu bu adımı hızlandırır. Toplu işleme, tek bir tanım dosyasıyla aynı anda birden fazla referansın güncellenmesini sağlar; ancak güncellemelerin kuyruğa alınmasına ve gecikmenin artmasına neden olur.

Blob deposu paket dosyalarını paralel olarak alabilir, ancak ortak tanım dosyasına yapılan güncellemeler yine de birbiriyle yarışır. Yalnızca blob deposunun kapasitesini artırmak bu darboğazı çözmez. Her replikanın, paketleri yerel olarak sunabilmesi için önce blob deposundan okuyup dizinlemesi gerekir. Gelen yazma işlemleri bu işin hızını aşarsa replikalar geride kalır ve istemciler yeni gönderilen commit’leri getirmek için daha uzun süre bekler.

Pando’nun mimarisini tasarlamak

Bu da bizi Pando’ya getiriyor. Pando, aynı anda gönderim yapan çok sayıda istemciye hizmet verecek şekilde ölçeklenmeyi hedefleyen, Git depolarını sunmak için geliştirilmiş yeni bir mimaridir. İşte çalışma şekli:

Kısıtları yeniden düşünmek

Pando’yu geliştirmeden önce, Git’ten ödün vermeden ihtiyaçlarımızı karşılayabileceğimizi düşünüyorduk. Ancak çoğu Git sisteminin gereklilik olarak kabul ettiği bazı şeylerden ödün vermemiz gerekiyordu:

  • Depo genelinde tutarlılık: Tüm referansların belirli bir andaki tutarlı görünümünü sunmak, depo genelinde koordinasyon gerektirir. Tüm referans güncellemelerini tek bir sıraya koyan bir sistemde, birbiriyle ilgisiz dallara yapılan gönderimler aynı koordinasyon noktasını kullanmak için yarışır.

  • Her istemciye özel paket dosyaları: Yerel geçmişleri farklı olan iki istemci aynı dalı getirdiğinde, sunucunun istemcilerde zaten bulunan nesneleri yeniden göndermemek için her birine farklı bir paket üretmesi gerekebilir. Bu yanıtları oluşturmak CPU ve disk G/Ç kaynaklarını tüketir; bu da çok sayıda okuyucuya hizmet verecek şekilde ölçeklenmeyi zorlaştırır.

Pando ise yazma işlemlerini ölçeklendirmek için daha esnek bir tutarlılık modeli (nedensel tutarlılık) sunar, okuma işlemlerini ölçeklendirmek için de paket dosyalarını yeniden kullanır.

Nedensel tutarlılık (CC)

Bir istemci bir dala gönderim yaptığında Pando, gelen paket dosyasını blob deposunda değiştirilemez bir blob olarak saklar. Dalın ucu her değiştiğinde Pando, yeni uç commit’i, onu sağlayan paketi ve paketin bağımlı olduğu önceki referans sürümlerini içeren bir referans sürümünü meta veri deposuna kaydeder. Hem blob deposu hem de meta veri deposu yatay olarak ölçeklenebilir.

Pando’dan bir commit veya referans getirmek için bir dizi referans sürümünün (paket dosyasının) istemciye sırayla aktarılması gerekir. Böylece getirilen referans sürümünü oluşturan istemcinin durumu yeniden oluşturulur. Bu yaklaşım, depo genelinde daha zayıf bir tutarlılık sağlarken tek tek referanslara yapılan güncellemeler için serileştirilebilirlik sunar. İstemciler yalnızca aynı referansa eş zamanlı gönderim yaptıklarında çakışır. Birden fazla dalı okuyan bir istemci, bu okumaların depo genelinde aynı anı yansıttığını varsayamaz.

Solda, art arda gelen üç ana dal paketi sırasıyla A, B ve C; D ve E; F commit’lerini içerir. Sağda, ana dal ve konu dalı C’de ayrılır. Ana dal G’ye ilerlerken konu dalı D, E ve F üzerinden ilerler. Konu dalının en son paketi D, E ve F’yi içerir. Kesikli çizgili çerçeve, D ve E’nin konu dalının önceki paketinde de bulunduğunu gösterir. Mavi ok, yeni paketin ana dalın G’deki durumuna bağımlılığını gösterir. F’nin Git’teki ebeveyn commit’i E’dir.

Ana dala yapılan her gönderim, bir veya daha fazla commit içeren artımlı bir paket ekler. Yan dala yapılan her gönderim, konu dalının önceki commit’lerini de yeniden içerir. Böylece bildirilen ana dal sürümüne sahip bir istemci, konu dalını tek paketle getirebilir.

Bu, bir nedensel tutarlılık biçimidir. Bir referansı getirmek için okuyucu, o referansı üreten yazıcının geçmişini görür. Pando, gönderim sırasında istemci ile sunucunun hangi ortak geçmiş üzerinde anlaştığını kontrol ederek istemcilerin daha kolay paylaşılabilen paket dosyaları üretmesini sağlar.

Bunun sonucunda Pando, Git işlemleri sırasında tüm referansların belirli bir andaki durumunu veya bir klonlama ya da getirme işlemi içindeki referans güncellemelerinin sırasını sağlamaz. Somut bir örnek olarak, tam klonlama için yapılan uzlaşma sırasında hem foo hem de bar dalının güncellendiğini varsayalım. Uzlaşma sırasında bir istemci foo dalını sürüm 1’de ve bar dalını sürüm 2’de görürken başka bir istemci foo dalını sürüm 2’de ve bar dalını sürüm 1’de görebilir. Pratikte kullanıcılar ve ajanlar, referans güncellemelerinin genel sırasına değil, tek bir referansa yapılan güncellemelerin sırasına dayanır.

Yaklaşımların karşılaştırılması

Üç tutarlılık modelini karşılaştıran bu simülasyonda, 1.000 istemcinin her birini ayrı bir dal seçip tekrar tekrar benzersiz commit’ler gönderecek şekilde yapılandırıyoruz. Geçen toplam süreyi ve gönderimden getirmeye kadar oluşan gecikmeyi ölçüyoruz. İkinci ölçüm, kullanıcıların ve ajanların birlikte çalışırken veya sürüm kontrolüne dayanan başka sistemleri kullanırken karşılaştığı gidiş-dönüş süresini ölçtüğünden iş yükleri için önemli bir metriktir. Simülasyon, standarttan üst düzeye uzanan sanal makineleri ve blob depolama sistemlerini modeller. Bu simülasyonda, üç aşamalı onay için depo genelindeki tutarlılık koşulunu gevşettik.

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.

Pando’yu diğer iki yaklaşımla karşılaştıran bu simülasyonu çalıştırın. Yaklaşımları karşılaştırmak için standarttan üst düzeye kadar donanım seçeneklerinden altyapıyı, istemci sayısını ve diğer ayarları seçin.

Modellenen üç aşamalı onay sisteminde diskler kapasite sınırına ulaştıkça yük altında yazma gecikmesi belirgin biçimde artar. Ön yazma günlüğü sistemlerinde ise sunucular günlükteki kayıtları okumaya yetişemediğinden aynı nedenle okuma gecikmesi belirgin biçimde artar. Hem monorepomuzdaki gerçek ortam testlerinde hem de bu simülasyonda Pando, yük altında performansı etkilemeden aynı anda gönderim yapabilen binlerce istemciyi destekliyor.

Pando, bir gönderimi işlerken gelen paket dosyasını yerel bir depoya açmadan, blob depolamaya akış hâlinde aktarırken doğrular. Bir istemci getirme işlemini başlattığında Pando, okuyucunun ihtiyaç duyduğu kayıtlı paketleri seçer ve okuyucu bunları indirir. Hiçbir sunucunun paket dosyalarını yerel bir depoya uygulaması veya maliyetli paket açma ve paketleme işlemleri yapması gerekmez. Bu sunucular esas olarak uzlaşma ve paket dosyası seçimi yaptığından, Pando sunucuları standart donanımlarda çalışabilir.

Böylece Pando; istemcilerin, istemciler ile Pando arasındaki ağın ve blob depolama hizmetinin sınırlarına kadar ölçeklenebilir. Yeterli kapasite olduğunda, bağımsız dallarda çalışan her istemci kendi bağlantısının izin verdiği hızda paket aktarabilir. Bu hızda daha fazla istemciye hizmet vermek için Pando sunucuları ekleyebilir, bunlarla birlikte meta veri deposunu ve blob deposunu da ölçeklendirebiliriz.

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

Bir ajan dosyaları düzenler, yerel olarak bir commit oluşturur ve git push komutunu çalıştırır. Bu komut, bir dalı güncellemek için söz konusu değişikliklerden bir paket dosyası oluşturur. Pando paketi alır ve akış hâlinde blob depolamaya aktarır. Pando, gelen gönderimi tek geçişli kod çözmeyle doğruladıktan sonra, çok anahtarlı bir işlem kullanarak meta veri depomuza bir referans sürümü kaydeder ve referans işaretçisini atomik olarak günceller. Bu, Pando’nun “kendi yazdığını okuma” tutarlılığı sağlamasına olanak tanır. Bu yazma işleminin tamamı O(1)O(1) meta veri okuma ve yazma işlemi gerektirir.

Başka bir istemci dalı getirdiğinde Pando, istemcinin zaten sahip olduğu geçmişi kullanarak kayıtlı paketlerden bir küme seçer. Bu işlem O(log⁡Δ)O(\log \Delta) meta veri okuması gerektirir. Burada Δ\Delta, istemcinin sahip olduğu durum ile istediği durum arasındaki referans sürümü sayısı cinsinden en kısa mesafedir. Sunucu paket dosyalarının URI’lerini döndürür; istemci de bunları, paket dosyalarını bölgesel önbelleklerde saklayan Express hizmeti üzerinden indirir.

Bu tasarım, Pando mimarisinin üç temel hizmetine dayanır: gönderim, getirme ve paket birleştirme.

Gönderimleri ölçeklendirmek

Bir istemci gönderim yaptığında, geleneksel bir Git sunucusu tüm kartlarını açan bir poker oyuncusu gibi davranır ve sahip olduğu her dalı istemciye gösterir. Ardından istemci, sunucuda zaten bulunduğunu bildiği nesneleri hariç tutan bir paket dosyası oluşturmak için commit’leri, ağaçları ve blob’ları toplar.

Daha fazla referans bildirmek, gönderimin boyutunu küçültebilir. Ancak yazıcının dışarıda bıraktığı nesneler başka bir istemcide bulunmayabileceğinden, bu durum paketin yeniden kullanılmasını da zorlaştırabilir.

Pando farklı bir yaklaşım izler. Tüm elini göstermek yerine, istemciye yalnızca seçilmiş bir dal kümesini bildirir. Bunun iki nedeni vardır:

  1. Tüm referansların tutarlı bir görünümünü sunmak maliyetlidir. Milyonlarca dal olduğunda, yalnızca referans bildirimi bile 100 MiB’ı aşabilir. Bunu sağlamak için her sunucunun ya önemli bir koordinasyon yüküyle güncel bir eşleme tutması ya da maliyeti referans sayısıyla artan bir veritabanı sorgusu çalıştırması gerekir.

  2. İstemciye referansların yalnızca bir alt kümesi sunulduğunda, istemcinin ürettiği paket yalnızca sunucunun açıkça hariç tutmasına izin verdiği nesnelere bağımlı olur. Bağımlılıkların izlenmesi, Pando’nun bir istemcinin gönderim sırasında ürettiği paket dosyasını daha sonraki bir getirme isteğine yanıt olarak sunup yeniden kullanmasını sağlar.

Bir Git sunucusu daha fazla referans bildirdikçe, gönderim yapan istemciler nesneleri hariç tutmak için daha fazla kaynaktan yararlanabilir ve daha küçük paketler üretebilir. Ancak bu paket dosyalarının yeniden kullanılabilmesi için sunucunun, getirme yapan istemcilerin tüm bağımlılıklara sahip olduğunu kanıtlayabilmesi gerekir. Aksi hâlde paket dosyası, istemcinin ihtiyaç duyduğu nesneleri içermeyebilir.

Öte yandan, Git sunucusu hiçbir referans bildirmezse istemci, deponun tüm geçmişini içeren bir paket dosyası üretir. Bu paket dosyasını başka herhangi bir istemci kullanabilir. Ancak bunun önemli bir bedeli vardır: Herhangi bir dalı getirmek, fiilen tam klonlama işlemine dönüşür.

Bildirilen referansların paket dosyası boyutuna etkisini gösteren eğri: Hiç referans bildirilmediğinde tüm geçmiş, yalnızca ana dal bildirildiğinde daha az veri, tüm referanslar bildirildiğinde ise yalnızca yeni nesneler aktarılır.

Daha fazla referans bildirmek, daha küçük paket dosyaları oluşturulmasını sağlar. Boyuttaki azalmanın büyük bölümü, yalnızca tek bir referans bildirilerek sağlanabilir.

Varsayılan olarak, bir istemci yeni bir özellik dalını gönderdiğinde Pando, temel olarak yalnızca belirlenmiş bir ana dalı bildirir. Bu, depodaki dal sayısı arttıkça bildirim boyutunu sabit tutar, ancak bazı ödünler gerektirir. Bir mühendis tek bir dala birçok kez gönderim yaptığında veya bir özellik dalını başka bir özellik dalı üzerine kurduğunda, istemci ana dalın dışındaki her şeyi yeniden göndermek zorunda kalır.

Bu nedenle art arda yapılan dal gönderimleri büyür, tekrarlanan nesneler içerebilir ve istemcinin daha fazla bant genişliği kullanmasına yol açar. Monorepomuzda bu şekilde üretilen paket dosyalarının medyan boyutu 20 KiB’ın, 99. yüzdelik dilimi ise 3 MiB’ın altındaydı. Bildirilen ana dal geçmişine zaten sahip olan bir okuyucu, dalı bu tek paket dosyasıyla getirebildiğinden, bizim için bu makul bir ödünleşimdi.

Pando, istemciye bildirilecek referans kümesini seçtikten sonra gönderimi kabul eder ve meta veri deposunu günceller. Pando, meta veri güncellemesini senkron ve asenkron bölümlere ayırır. Senkron meta veri güncellemesi, istemcilerin yeni güncellenen commit’i hemen getirebilmesini sağlar. Bu işlem yeni bir referans sürümü yazar ve referans işaretçisi eşlemesini bu sürüme yönlendirir. Yeni uç commit, paket dosyası üzerinde uzlaşırken kullanılan bağımlılıklar ve gönderilen paket dosyasına bir bağlantı da kaydedilir.

Aynı zamanda, paket birleştirmeyi planlamak gibi asenkron işler de aynı işlem içinde kaydedilir. Çok büyük gönderimlerde, commit’lerin yalnızca bir alt kümesi senkron olarak yazılır. Böylece gönderimin boyutu ne olursa olsun meta veri deposunda yalnızca sabit sayıda okuma ve yazma işlemi gerekir.

Önemli bir nokta da Pando sunucularının birbiriyle ilgisiz referanslara yapılan gönderimleri bağımsız olarak kabul edebilmesidir. Erişim için yarışılan tek anahtar, atomik bir karşılaştır-ve-değiştir benzeri işlemle koruduğumuz referans işaretçisidir. Pando sunucuları, meta veri deposu ölçeklendirme birimleri veya blob depoları ekleyerek yazma işlem kapasitesini artırabiliriz.

Hat hızında getirme ve klonlama performansı

Bir istemci getirme işlemi yaptığında sunucuya, protokolde have ve want olarak bilinen iki commit kimliği kümesi sunar. Geleneksel bir Git sunucusu, mevcut commit’lerden erişilebilen nesneler ile istenen commit’lerden erişilebilen nesneler arasındaki küme farkını hesaplar, bu nesneleri sıkıştırır ve sonucu istemciye gönderir. Açık kaynak Git sunucusunda bu işlemi hızlandırmak için çeşitli yerel dizinler bulunsa da bu paket dosyalarını hesaplamak maliyetli olabilir ve bunlar genellikle önbelleğe almaya elverişli değildir.

Pando ise gönderimler sırasında kaydedilen commit eşlemelerini kullanarak istemcinin have ve want kümelerini arar ve bunları, ilgili commit’leri içeren bir referans sürümleri kümesiyle eşler. Bu referans sürümlerinin her biri, Pando’nun daha önce aldığı bir paket dosyasına bağlıdır. Sunucu, bu bilgiden yararlanarak istemcinin ihtiyaç duyduğu nesne kümesini kapsayan paket dosyalarını seçer.

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, Git protokolündeki mevcut ve istenen commit’leri bir referans sürümleri dizisiyle eşler. Bir yan dalı istemek, onun ana daldaki bağımlılıklarına kadar uzanan geçmişi de istemek anlamına gelir.

Git protokolü, klonlama veya getirme işlemlerinde HTTP yanıtında yalnızca tek bir paket dosyası gönderilmesine izin verir. Ancak Git protokolünün pek bilinmeyen bir özelliği olan packfile-uri, sunucunun ek paketleri ayrı olarak indirmek için bağlantılar döndürmesine olanak tanır. Pando yalnızca bu yöntemi kullanır ve yanıtın içinde boş bir paket dosyası iletir. OpenAI, bu özelliği desteklemek için packfile-uri üzerinde yaptığı bir dizi iyileştirmeyi ana projeye aktardı.

OpenAI sistemleri birçok coğrafi bölgede çalıştığından, her bölgede yerel önbelleklere sahip Pando kümeleri bulunduruyoruz. Paket dağıtım hizmeti Express, blob depomuzu önbelleğe alırken ayrı bir hizmet de meta veri deposunu önbelleğe alır. Bu önbellekler sayesinde Pando, bir commit kimliğine yönelik istekleri veya bir referansın özel okuma isteklerini tamamen ilgili kümenin önbelleğindeki durumdan karşılayabilir. Paket dosyası isteklerini Express sunucularına yönlendirmek için tutarlı karma kullanıyoruz ve %99’un üzerinde önbellek isabet oranına ulaşıyoruz.

Bu önbellek sistemi, istemciler ile Pando arasındaki ağ kapasitesinin tamamını kullanabilecek kadar yüksek işlem kapasitesi sağlar. Bu paket dosyası URI’leri ve yerel önbelleklerle, yalnızca bulut sağlayıcılarının standart sanal makinelerini kullanarak klonlama ve getirme işlemlerinin 10 Gbps ağ bağlantılarının kapasitesini tamamen kullanabildiğini doğruladık.

Paket birleştirme ve ilk bayta kadar geçen süre

Çoğu Git sunucusunda, büyüyen bir deponun performansını korumak için yeniden paketleme ve çöp toplama gerekir. Bu iki adım da depolama alanını geri kazandırır, ancak yoğun hesaplama ve G/Ç gerektirir.

OpenAI’ın monoreposu gibi hızla değişen bir depoda, son getirme işleminden bu yana yapılan her gönderim için ayrı bir paket dosyası URI’si göndermek uygulanabilir değildir. Bir istemcinin ana dalını güncel tutmak için her gün on binlerce paket dosyası indirmesi gerekirdi.

Pando bunun yerine, günlük yapılı birleştirme ağaçlarındaki sıkıştırmaya benzer bir tasarımla paketleri arka planda birleştirerek bu ek yükü azaltır. Her gönderim, Pando’nun “L0” olarak adlandırdığı bir paket dosyası üretir. Bu, Pando’nun iletebildiği en küçük veri birimidir. Arka plan işçileri zaman içinde bu paket dosyalarını, bir referans sürümleri dizisini kapsayan ve L0’dan başlayarak geometrik olarak büyüyen kapsama gruplarında toplar. Bugün monorepomuzda L4’e kadar paket dosyası kapsama grupları kullanılıyor.

Bu kapsama grupları iki amaca hizmet eder. Bir kapsama grubu yeterince dolduğunda, bir birleştirme işçisi onu devralır ve tek bir paket dosyası üretir. Bu, paket dosyasındaki nesneleri yeniden düzenleyerek sıkıştırma oranını artırır. Ayrıca referans sürümleri üzerinde bir atlama listesi işlevi görerek getirme uzlaşmasının performansını da artırır. Pando, her düzeydeki dizinleri aynı anda tarayarak istemciye gönderilecek paket kümesini logaritmik sürede seçebilir. Bu paket dosyası kapsama grupları, yeniden paketlenmiş olsun veya olmasın, getirme planlamasına daha verimli bir çözüm sunarak Pando’nun hem işlem kapasitesi hem de gecikme gereksinimlerini azaltır.

Compaction in Pando

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

Compaction in progressPublished cover

Paket birleştirme, birçok referans sürümündeki paket dosyalarını arka planda bir araya getirerek daha az dosyayla daha verimli getirme işlemleri sağlar.

Birçok Git sunucusunun aksine, paket dosyaları her düzeyde tutulur; atılmaz veya çöp toplamayla silinmez. Her düzeydeki paketler, istemcilerin getirme isteğinde belirttiği have ve want kümelerine göre sürekli olarak hizmet vermek için kullanılır. Pando’yu API olarak kullanan istemciler içinse bir paket dosyasından rastgele bir nesneyi okumak, L4’e kıyasla L0’dan genellikle daha az veri okumayı gerektirir; çünkü nesnenin sıkıştırılmış olma olasılığı daha düşüktür.

Pando’daki paket birleştirme, istemcilere aktarılan toplam veri boyutunu önemli ölçüde azaltır. Ayrıca istemcinin ne kadar geride kaldığına bağlı olarak paket planlama maliyetinin doğrusalın altında artmasını sağlar. Bu, yazılım geliştirmede yeni normal hâline gelmesini beklediğimiz büyük ve hızla değişen depolar için özellikle önemlidir.

Pando’nun geliştirilmesi

Pando’yu Codex ile geliştirdik; projenin başlangıcından üretim ortamına geçişe kadar yalnızca 10 hafta geçti. Pando’nun çekirdeğini tek bir mühendis tasarladı, ajan ekipleri kodladı ve az sayıda mühendis kullanıma aldı. Pando, bu yılın nisan ayında üretim trafiğine hizmet vermeye başladı ve OpenAI’da kullanılan daha kapsamlı bir kaynak kodu yönetim araçları sisteminin parçası.

Pando’nun tasarımını doğrularken çok çeşitli yaklaşımlar kullandık. Daha güvenli yazılım temelleri oluşturma⁠ ilkelerimizi uyguladık. Sıfırdan başlayan bir proje olduğu için Pando’yu Rust ile ve eski sistem bağımlılıkları olmadan geliştirmeyi seçtik. Ajanların değişikliklerini standart Git davranışıyla karşılaştırarak test edebilmesi için bir uygunluk testi paketi yazdık. Sistemin değişmezlerini ve özelliklerini kontrol etmemizi sağlayan deterministik simülasyon test düzenekleri de geliştirdik. Böylece uzlaşma ve paket planlamadaki zor fark edilen yarış koşullarını ve gerilemeleri bulup etkilerini azaltabildik.

Bu sisteme birçok model katkıda bulundu. Yeni modeller kullanıma sunuldukça Pando’nun performansını ve güvenilirliğini artırmak için bunlardan yararlandık. GPT‑6 Astra kullanan ajanlar, bir “Pando teorisini” biçimselleştirmek için Lean ve TLA+ kodu yazdı. Bu teoriyi, uygulamadaki hataları bulup düzeltmek ve sistemin özelliklerini kanıtlamak için kullandık. Birden fazla kesintisiz oturum, Pando’daki uç durumları bulup düzeltmek için yüzlerce saat boyunca çalıştı. Ayrıca Daybreak yeteneklerine sahip modellerle Git ve Pando üzerinde saldırı odaklı güvenlik testleri yapmak için ajanları günün her saati çalıştırıyoruz.

Pando’nun kullanıma alınması

Pando’yu ilk olarak, okuma ve yazma işlemlerini mevcut kaynak kodu kontrol sistemimize ileten bir vekil sunucu olarak devreye aldık. Bu sayede Pando ile önceki sistemi eş zamanlı tutabildik ve trafiği kesinti olmadan güvenle Pando’ya taşıyabildik.

Pando, önbellek tasarımının bir parçası olarak istekleri iletmeden önce yinelenenleri ayıklayıp birleştirebilir. Böylece sistemleri birer birer Pando’ya taşırken mevcut araçlarımızın sorunsuz çalışmasını sağladık. Pando’ya daha fazla iş yükü taşıdıkça, bunların gönderimleri Pando’nun önbelleğini doldurdu ve önceki arka ucumuzun güvenilirliğini artırdı.

Pando kullanırken performansı optimize etmek için herkese açık Git dağıtımımızı geliştirici makinelerine ve ChatGPT masaüstü uygulamasına sunmaya başladık. İstemcileri aşamalar hâlinde Pando’ya geçirmek için Git CLI’ını saran kurum içi bir araç kullandık. Değişiklik taleplerini ve kod incelemelerini yönetmek için birçok başka araç, otomasyonlar için de API’ler oluşturduk. Git trafiğinin yanı sıra eski API’lere, webhook’lara ve depo indirmelerine bağımlı yüzlerce entegrasyonu da taşıdık.

Codex de geliştiricilerin ve ekiplerin geçiş yapmasına yardımcı oldu. Mevcut çalışma kopyalarını hazırlamak ve ajanları yeni sistemi kullanmaya yönlendirmek için beceriler oluşturduk. Böylece geliştiricilerin, geliştirme ortamlarını ve hizmetlerini taşımak için Codex’ten bunu istemeleri yeterli oldu.

Pando’ya geçiş, birleştirme güvenilirliğini artırdı ve medyan gönderim gecikmesini yaklaşık %80 azalttı.

Pando, yalnızca birkaç CPU çekirdeğiyle günde bir petabayt Git verisi sunabiliyor. Pando, tek bir saatte 57 terabayttan fazla Git paketi sundu; bu baytların %99’undan fazlası yerel disk önbelleğinden sağlandı.

Paketleri yeniden kullanmak, hizmet veren düğümlerde çok az hesaplamayla çok sayıda istemcinin aynı depo geçmişini indirmesini sağlar. Bu örnek tek bir depoyu gösteriyor, ancak Pando çok daha fazlasını desteklemek üzere tasarlandı. Talep arttıkça veri dağıtımını ve Git uzlaşmasını birbirinden bağımsız olarak ölçeklendirebiliriz.

Git projesine katkılarımız

Pando’yu Git protokolü ve istemcisi üzerine kurmak, Git’teki hataları ve performans iyileştirme fırsatlarını belirlememize yardımcı oldu.

Örneğin Pando, yeniden kullanılabilir paketleri doğrudan istemcilere sunuyor. Bu nedenle paketlerin indirilmesi ve dizinlenmesi, sistemi hızlı ve güvenilir kılmanın temel bir parçası hâline geldi. Bu çalışma; eş zamanlı indirmelerde, kesintiye uğrayan aktarımlardan sonraki temizleme işlemlerinde ve zaten tamamlanmış indirmeleri sürdürmede uç durumları ortaya çıkardı. Bu kod yollarını düzelttik, getirme işlemi için mevcut bir paket kabul edilirken doğrulamalar ekledik ve bu çalışmaları ana projeye aktardık.

Zamanın ve bant genişliğinin nerede harcandığını da görmemiz gerekiyordu. Bu nedenle packfile-uri indirmeleri için izleme ve paket üretimi için bayt sayaçları ekledik. Böylece yavaş bir getirme işleminin zamanını paket üretiminde mi yoksa aktarımda mı harcadığını anlamak kolaylaştı.

OpenAI’ın Git derlemesinde⁠(yeni bir pencerede açılır) istemci, bağımsız paketleri paralel olarak indirip dizinleyebilir. Sunucunun bir dalın güncel uç commit’ini, gönderim yapan istemcinin paketinden çıkarabileceği geçmişten ayrı olarak bildirebilmesi için “açıkça belirtilen mevcut commit’ler” kavramını ekledik. Pando’da bu, istemcilerin --force-with-lease kullanarak bir özellik dalının ucunun değişmediğini kontrol etmesini ve paketini yalnızca ana dalı temel alarak oluşturmasını sağlar. Ardından bu ana dal geçmişine sahip herkes aynı paketi kullanarak dalı getirebilir.

Bu iyileştirmelerin birkaçı şimdiden ana Git projesinde, birçoğu da herkese açık depomuzda yer alıyor. Bu iyileştirmelerin Pando dışında da faydalı olması için Git topluluğuyla çalışmaya devam ediyoruz. Bu çalışmaları herkese açık yama takip sistemimizden⁠(yeni bir pencerede açılır) izleyebilirsiniz.

Sırada ne var?

Pando, Git depolarının nasıl ölçeklendirileceğine dair yerleşik kabulleri yeniden ele alarak, mühendislerimizin ve ajanlarımızın kullandığı Git iş akışlarını değiştirmeden monorepomuzu ölçeklendirmemizi sağlıyor. Pando, monorepomuzda daha fazla paralel çalışmaya olanak tanıdı, birleştirme güvenilirliğini artırdı ve kod değişikliklerini yayımlama süresini kısalttı. Monorepomuzun ötesinde, çok sayıda ajanın ortak bir Git deposunda birlikte çalışmasını gerektiren iş yükleri de dâhil olmak üzere, OpenAI bünyesinde Pando’nun başka kullanım alanlarını da araştırıyoruz.

Mühendisler ve ajanlar daha fazla yazılım ürettikçe, OpenAI dışındaki daha çok ekip de kaynak kodu kontrolünde bizim karşılaştığımız sınırlara ulaşacak. Şu anda Pando’yu mümkün kılan teknolojiyi kullanıcılarımızla paylaşmanın yollarını araştırıyoruz.

Pando, Git depolarını kullanan ve işletenlerin yirmi yıllık deneyimine dayanıyor. Pando’yu geliştirmeye devam ederken, Git kullanıcılarına daha geniş ölçekte fayda sağlayacak iyileştirmeleri paylaşmak için açık kaynak Git topluluğuyla birlikte çalışacağız.

Yazar

OpenAI