OpenAI

2 พฤศจิกายน 2569

วิศวกรรม

ขยายขีดความสามารถของ Git ที่ OpenAI

สร้างระบบควบคุมซอร์สโค้ดให้รองรับงานระดับเอเจนต์ พร้อมลงทุนในระบบนิเวศ Git

โดย Aaron Friel, Taylor Blau และ Ted Nyman สมาชิกทีมเทคนิค

กำลังโหลด…

ตลอดปีที่ผ่านมา ขนาดและความต้องการใช้งาน monorepo ของเราเพิ่มขึ้นอย่างรวดเร็วจนน่าทึ่ง เห็นได้ชัดว่าเราต้องปรับปรุงระบบควบคุมซอร์สโค้ดครั้งใหญ่ เพื่อรองรับการทำงานพร้อมกันในระดับที่ Codex ทำให้เป็นไปได้

เราสร้าง Pando ซึ่งเป็นแบ็กเอนด์ใหม่สำหรับ Git Repository โดยไม่ขัดจังหวะงานวิศวกรรมใดๆ หรือเปลี่ยนวิธีที่ทีมของเราใช้ Git ปัจจุบัน Pando รองรับงานพัฒนาทั้งหมดใน monorepo ของ OpenAI Pando ออกแบบมาเพื่อตอบโจทย์ใหญ่สองข้อ ข้อแรกคือขยายการเขียนในแนวนอนได้ โดยปรับให้เหมาะกับการ push ไปยังหลายบรานช์พร้อมกัน ข้อที่สองคือเพิ่มอัตราการประมวลผลด้านการอ่านให้สูงสุด เพื่อให้ไคลเอนต์ดาวน์โหลดข้อมูลได้เร็วเต็มแบนด์วิดท์เครือข่ายที่มี

เราบรรลุเป้าหมายเหล่านี้โดยยังคงเข้ากันได้กับโปรโตคอลและไคลเอนต์ Git ทำให้ทั้งวิศวกรและเอเจนต์ของเรานำ Pando ไปใช้ในเวิร์กโฟลว์เดิมได้อย่างราบรื่น ในการใช้งานจริง Pando ลดค่ามัธยฐานความหน่วงของการ push ลงเหลือประมาณหนึ่งในห้า ปัจจุบันระบบส่งข้อมูล Git เกือบหนึ่งเพตะไบต์ต่อวัน

บทความนี้จะเล่าถึงแรงผลักดันในการสร้างระบบควบคุมซอร์สโค้ดใหม่ และเหตุผลที่เราเลือกคงความเข้ากันได้กับ Git เราจะเปรียบเทียบระบบของเรากับแนวทางทั่วไปในการขยาย Git พร้อมอธิบายว่าเหตุใด Pando จึงตอบโจทย์การขยายระบบของ OpenAI ได้อย่างโดดเด่น เรายังจะพูดถึงก้าวต่อไป และแนวทางที่เราจะร่วมพัฒนา Git ต่อไปเพื่อรองรับงานในระดับที่เอเจนต์สร้างขึ้น

เหตุใดเราจึงต้องมีระบบควบคุมซอร์สโค้ดใหม่

Codex เปลี่ยนทั้งขนาดและความเร็วของงานพัฒนาที่ OpenAI ทำให้วิศวกรทำงานคู่ขนานได้มากกว่าเดิมอย่างมาก นั่นหมายถึงบรานช์ การ push และการ merge ที่มากขึ้น รวมถึงการ fetch และ clone จากสภาพแวดล้อมการพัฒนา งานวิจัย และ CI ที่มากขึ้นด้วย

ความต้องการเหล่านี้เพิ่มภาระทบกันเป็นทอดๆ ตอนนี้วิศวกรหนึ่งคนมีการเปลี่ยนแปลงหลายชุดที่พร้อมเข้า CI ในเวลาเดียวกัน และการ push แต่ละครั้งอาจกระตุ้นให้หลายงาน fetch คอมมิตเดียวกัน

ตั้งแต่ราวปลายปีที่แล้ว ระบบที่รองรับ monorepo ของเราเริ่มรับภาระไม่ทัน เราเผชิญทั้งการ fetch และ push ที่ช้า รวมถึงการ merge ที่ล้มเหลว โดยบางช่วงความน่าเชื่อถือลดลงต่ำกว่า 90% เรามาถึงขีดจำกัดของ Git เอง หรือขีดจำกัดของวิธีที่เราให้บริการ monorepo กันแน่? Git เติบโตมาจากชุมชนที่แลกเปลี่ยนแพตช์เคอร์เนลกันทางอีเมล ตลอดเวลากว่าสองทศวรรษ นักพัฒนาได้ปรับเครื่องมือให้ทำงานร่วมกับ Git ตามวิธีพัฒนาซอฟต์แวร์ที่เปลี่ยนไป เมื่อ monorepo ของเราเริ่มติดขัด สิ่งสำคัญสำหรับเราคือยังสามารถต่อยอดจากงานเหล่านั้นได้

เราเห็นหนทางที่จะรักษาเวิร์กโฟลว์ที่คุ้นเคยไว้ ขณะสร้างแบ็กเอนด์ใหม่ให้รองรับการ push ไปยังบรานช์ต่างๆ แบบคู่ขนาน และส่งประวัติเดียวกันให้ผู้อ่านจำนวนมากพร้อมกัน การใช้ Git เป็นรากฐานของเวิร์กโฟลว์ควบคุมซอร์สโค้ด หมายความว่าการปรับปรุงไคลเอนต์จะเป็นประโยชน์นอก OpenAI ได้ด้วย เราต้องการส่งการปรับปรุงเหล่านั้นจากสำเนา Git ของเรา⁠(เปิดในหน้าต่างใหม่)กลับไปยังโครงการต้นทาง เพื่อแบ่งปันกับชุมชนในวงกว้าง

ออกแบบ Git ให้รองรับงานขนาดใหญ่

การขยายระบบควบคุมซอร์สโค้ดมีสองมิติสำคัญ ได้แก่ ผู้เขียนจำนวนมากที่ push ไปยังบรานช์ต่างกัน และผู้อ่านจำนวนมากที่ fetch ประวัติเดียวกัน Pando ปรับประสิทธิภาพให้เหมาะกับทั้งสองด้าน

ประการแรก Pando ประสานการอัปเดตแต่ละบรานช์อย่างเป็นอิสระ การอัปเดตบรานช์ที่ไม่เกี่ยวข้องกันทำงานคู่ขนานได้ และผู้อ่านที่ fetch บรานช์จะได้รับประวัติที่จำเป็นสำหรับรีวิชันนั้น สิ่งที่ต้องแลกคือผู้อ่านที่ดูหลายบรานช์อาจเห็นสถานะของแต่ละบรานช์ ณ เวลาที่ต่างกัน

ประการที่สอง Pando ใช้ไฟล์แพ็ก (รูปแบบของ Git สำหรับบีบอัดหลายออบเจ็กต์เข้าด้วยกัน) ซ้ำระหว่างการ fetch แทนที่จะสร้างไฟล์แพ็กแยกให้ไคลเอนต์แต่ละราย ไคลเอนต์อาจดาวน์โหลดบางออบเจ็กต์ที่มีอยู่แล้ว แต่เซิร์ฟเวอร์สามารถส่งข้อมูลชุดเดียวกันให้ไคลเอนต์จำนวนมากโดยใช้การคำนวณเพียงเล็กน้อย

แนวทางเหล่านี้ช่วยให้เราขยายการเขียนข้ามบรานช์ต่างๆ และรองรับการอ่านจากแคชได้ เพื่อให้เห็นว่าทั้งหมดทำงานร่วมกันอย่างไร เรามาเริ่มจากวิธีที่ Git จัดเก็บและรับส่งประวัติกัน

พื้นฐานการจัดเก็บออบเจ็กต์ของ Git

Git ต้องทำให้ข้อมูลสองส่วนของ Repository ตรงกันอยู่เสมอ ได้แก่ ออบเจ็กต์และตัวอ้างอิง

Git Repository บันทึกสแนปช็อตของไฟล์และประวัติที่เชื่อมโยงกันไว้ในรูปกราฟของออบเจ็กต์ ออบเจ็กต์อาจเป็นบล็อบ ทรี คอมมิต หรือแท็ก บล็อบเก็บเนื้อหาของไฟล์แต่ละไฟล์ ส่วนทรีเชื่อมชื่อเข้ากับบล็อบหรือทรีอื่นๆ เพื่อสร้างโครงสร้างไดเรกทอรี คอมมิตชี้ไปยังทรีรากและคอมมิตแม่ตั้งแต่ศูนย์รายการขึ้นไป ประกอบกันเป็นประวัติของ Repository

ออบเจ็กต์แต่ละรายการระบุด้วยค่าแฮชของชนิดและเนื้อหา ออบเจ็กต์ยังแก้ไขไม่ได้ด้วย การบันทึกไฟล์ที่เปลี่ยนแปลงจะสร้างบล็อบใหม่ ส่วนไฟล์และไดเรกทอรีที่ไม่เปลี่ยนแปลงสามารถใช้ออบเจ็กต์เดิมซ้ำได้

บรานช์และแท็กเป็นตัวอ้างอิง หรือ “ref” ที่ชี้ไปยังคอมมิต การคอมมิตบนบรานช์จะสร้างคอมมิตใหม่และอัปเดต ref ของบรานช์ให้ชี้ไปยังคอมมิตนั้น

สาขา refs/heads/main อ้างอิงถึงคอมมิต C2 ซึ่งมี C1 เป็นคอมมิตก่อนหน้า โดย C1 อ้างอิงถึงโครงสร้างไดเรกทอรีหลัก T1 ซึ่งไฟล์ parser.py เชื่อมโยงกับ blob B1 ส่วน C2 อ้างอิงถึงโครงสร้างไดเรกทอรีหลัก T2 ซึ่งไฟล์ parser.py เชื่อมโยงกับ blob B2 ทั้งนี้ โครงสร้างไดเรกทอรีหลักทั้งสองอ้างอิงถึงโครงสร้างไดเรกทอรี T3 เดียวกันผ่าน docs/ โดยไฟล์ README.md ภายใน T3 อ้างอิงถึง blob B3 และไฟล์ usage.md อ้างอิงถึง blob B4

ออบเจ็กต์ใน Git อ้างอิงถึงกันผ่านรหัสประจำออบเจ็กต์ โดยคอมมิตจะอ้างอิงถึง tree ส่วน tree จะอ้างอิงถึง tree อื่นหรือ blob สาขา refs/heads/main อ้างอิงถึงคอมมิต C2 ซึ่งมี C1 เป็นคอมมิตแม่ โดย C1 อ้างอิงถึง tree หลัก T1 ซึ่งไฟล์ parser.py เชื่อมโยงกับ blob B1 ส่วน C2 อ้างอิงถึง tree หลัก T2 ซึ่งไฟล์ parser.py เชื่อมโยงกับ blob B2 ทั้ง T1 และ T2 ต่างอ้างอิงถึง tree T3 เดียวกันผ่านไดเรกทอรี docs/ โดยภายใน T3 รายการ README.md อ้างอิงถึง blob B3 และ usage.md อ้างอิงถึง blob B4

Git จัดเก็บออบเจ็กต์ในสถานะ “แยกไฟล์” หรือ “รวมแพ็ก” ออบเจ็กต์แบบแยกไฟล์จะเก็บในไฟล์แต่ละไฟล์ที่ระบุด้วย ID ของออบเจ็กต์ ออบเจ็กต์แบบรวมแพ็กจะเก็บรวมกันในไฟล์แพ็ก ทำให้จัดเก็บออบเจ็กต์ที่คล้ายกันเป็นส่วนต่างเทียบกับออบเจ็กต์อื่นได้

ไฟล์ parser.py สองเวอร์ชันถูกจัดเก็บไว้ใน $GIT_DIR/objects โดยเมื่อจัดเก็บเป็นวัตถุข้อมูลแยกกัน blob A จะมีฟังก์ชัน parse() ส่วน blob B จะมีทั้งฟังก์ชัน parse() และ parse_file() แต่เมื่อจัดเก็บในไฟล์รวมข้อมูล ระบบจะเก็บ blob A ไว้ทั้งหมด และเก็บ blob B ในรูปแบบคำสั่งให้คัดลอกเนื้อหาทั้งหมดของ A แล้วต่อท้ายด้วย parse_file() ส่วนไฟล์ดัชนี *.idx ที่อยู่คู่กันจะใช้ค้นหาตำแหน่งของวัตถุข้อมูลทั้งสองภายในไฟล์รวมข้อมูลโดยอาศัยรหัสประจำวัตถุข้อมูล

Git สามารถจัดเก็บ blob แยกเป็นไฟล์แต่ละรายการ หรือรวมไว้ในไฟล์เดียวกัน โดยบันทึกเฉพาะส่วนที่แตกต่างจากอีก blob หนึ่งแทนการเก็บข้อมูลทั้งหมดซ้ำ

Git สามารถค้นหาออบเจ็กต์ที่เกี่ยวข้องกับคอมมิตหนึ่งได้ โดยไล่ตามความเชื่อมโยงจากคอมมิตนั้นไปยังคอมมิตแม่ tree และ blob หากค้นพบออบเจ็กต์ใดผ่านเส้นทางเหล่านี้ ก็ถือว่าออบเจ็กต์นั้นเข้าถึงได้จากคอมมิตดังกล่าว เมื่อไคลเอนต์ขอดึงข้อมูล จะต้องบอกเซิร์ฟเวอร์ก่อนว่ามีคอมมิตใดอยู่แล้วและต้องการคอมมิตใดเพิ่มเติม เซิร์ฟเวอร์จึงตรวจสอบว่าคอมมิตที่ต้องการเชื่อมโยงกับออบเจ็กต์ใดบ้าง แล้วเลือกส่งเฉพาะรายการที่ไคลเอนต์ยังไม่มี โดยรวบรวมเป็นไฟล์รูปแบบเดียวกับที่ Git ใช้เก็บข้อมูลบนดิสก์ แต่หากเป็นการคัดลอก Repository ใหม่ทั้งหมด ไคลเอนต์จะยังไม่มีออบเจ็กต์ใดเลย จึงต้องดาวน์โหลดออบเจ็กต์ทั้งหมดที่เชื่อมโยงกับประวัติที่ต้องการคัดลอ

ส่วนการส่งการเปลี่ยนแปลงจะเป็นการถ่ายโอนออบเจ็กต์ในทิศทางตรงกันข้าม โดยไคลเอนต์จะขอให้เซิร์ฟเวอร์อัปเดตรายการอ้างอิงอย่างน้อยหนึ่งรายการ และใช้ข้อมูลรายการอ้างอิงที่เซิร์ฟเวอร์แจ้งไว้เพื่อหลีกเลี่ยงการส่งออบเจ็กต์ที่มีอยู่แล้วซ้ำอีก ดังนั้น ไฟล์รวมข้อมูลที่ส่งไปจึงอาจต้องอาศัยออบเจ็กต์ที่มีอยู่บนเซิร์ฟเวอร์ก่อนหน้า เพราะเพียงได้รับไฟล์ดังกล่าวก็ยังไม่เพียงพอที่จะสร้างประวัติการเปลี่ยนแปลงใหม่ให้ครบถ้วน

การทำสำเนา Repository ของ Git ไปยังเซิร์ฟเวอร์หลายเครื่องต้องทำให้ทั้งออบเจ็กต์และรายการอ้างอิงพร้อมใช้งานบนเซิร์ฟเวอร์เหล่านั้น ก่อนที่เซิร์ฟเวอร์เครื่องใดจะให้บริการข้อมูลอ้างอิงที่เพิ่งอัปเดตได้ ก็ต้องมีออบเจ็กต์ทั้งหมดที่สามารถเข้าถึงได้จากข้อมูลอ้างอิงเวอร์ชันใหม่นั้นพร้อมให้บริการด้วย นอกจากนี้ ระบบยังต้องจัดการกรณีที่มีการส่งการเปลี่ยนแปลงมาอัปเดตรายการอ้างอิงเดียวกันพร้อมกัน เช่น เมื่อไคลเอนต์หลายรายพยายามเปลี่ยนคอมมิตล่าสุดของสาขาเดียวกันจากคอมมิตเดิมไปเป็นคอมมิตใหม่คนละรายการ หากระบบบังคับให้การเขียนข้อมูลทั้งหมดใน Repository ต้องเรียงลำดับร่วมกัน แม้แต่การส่งการเปลี่ยนแปลงไปยังสาขาที่ไม่เกี่ยวข้องกันก็อาจต้องรอกัน

แนวทางการออกแบบระบบ Git แบบกระจายที่ใช้กันทั่วไป

ระหว่างออกแบบ Pando เราได้ประเมินแนวทางทั่วไปในการขยายระบบควบคุมเวอร์ชันโค้ดที่ใช้ Git อยู่สองแนวทาง แนวทางแรกคือการใช้กระบวนการยืนยันการเปลี่ยนแปลงแบบสามขั้นตอน (3PC) ซึ่งใช้ประสานการยืนยันธุรกรรมระหว่างโหนดหลายตัว เพื่อจัดการการอัปเดตรายการอ้างอิงให้สอดคล้องกันระหว่างสำเนา Repository ส่วนอีกแนวทางคือการใช้บันทึกข้อมูลล่วงหน้า (WAL) ที่จัดเก็บร่วมกันในระบบจัดเก็บ blob เพื่อกำหนดลำดับการอัปเดต โดยบันทึกดังกล่าวจะเก็บรายละเอียดการเปลี่ยนแปลงไว้ก่อนนำไปดำเนินการจริง

การยืนยันการเปลี่ยนแปลงแบบสามขั้นตอน (3PC)

ในแบบจำลองระบบที่ใช้การยืนยันการเปลี่ยนแปลงแบบสามขั้นตอน บริการ Git จะเก็บสำเนา Repository ทั้งหมดไว้บนเซิร์ฟเวอร์หลายเครื่อง เมื่อเซิร์ฟเวอร์เครื่องใดได้รับการเปลี่ยนแปลง ก็จะทำสำเนาไฟล์รวมข้อมูลที่ได้รับและส่งไปยังเซิร์ฟเวอร์ทุกเครื่องพร้อมกัน ก่อนเผยแพร่การเปลี่ยนแปลง ระบบต้องได้รับการยอมรับให้อัปเดตรายการอ้างอิงจากเซิร์ฟเวอร์ส่วนใหญ่ โดยมีเซิร์ฟเวอร์หนึ่งเครื่องทำหน้าที่ประสานงาน ซึ่งต้องติดต่อสื่อสารกับเซิร์ฟเวอร์ที่เก็บสำเนาแต่ละเครื่องหลายรอบ

ก่อนยอมรับการอัปเดตรายการอ้างอิง เซิร์ฟเวอร์ที่เก็บสำเนาแต่ละเครื่องจะล็อกรายการอ้างอิงของตนไว้ แล้วตรวจสอบว่ายังอ้างอิงถึงคอมมิตที่ไคลเอนต์คาดไว้หรือไม่ หากตรวจสอบผ่าน เซิร์ฟเวอร์ที่ทำหน้าที่ประสานงานจะส่งข้อความเตรียมยืนยันการเปลี่ยนแปลงไปยังเครื่องที่ยอมรับการอัปเดต เมื่อได้รับการตอบรับจากเซิร์ฟเวอร์ส่วนใหญ่แล้ว จึงสั่งให้ดำเนินการอัปเดต หลังจากนั้น เซิร์ฟเวอร์ที่เก็บสำเนาเครื่องใดก็ตามที่มีข้อมูลเป็นปัจจุบันจะสามารถแจ้งคอมมิตล่าสุดของสาขาจากสำเนา Repository ในเครื่องให้ไคลเอนต์ทราบได้

การเก็บสำเนา Repository แบบครบถ้วนไว้บนเซิร์ฟเวอร์หลายเครื่องทำให้แต่ละเครื่องสามารถให้บริการอ่านข้อมูลจากสำเนาของตนเองได้ และรับประกันว่าไคลเอนต์จะเห็นสถานะของรายการอ้างอิงทั้งหมดอย่างสอดคล้องกัน ไม่ว่าจะใช้เซิร์ฟเวอร์เครื่องใดก็ตาม แต่ข้อแลกเปลี่ยนคือระบบต้องอัปเดตรายการอ้างอิงทีละรายการ และการส่งการเปลี่ยนแปลงแต่ละครั้งก็ต้องมีการรับส่งข้อความระหว่างเซิร์ฟเวอร์หลายรอบ เพื่อประสานการอัปเดตข้อมูลระหว่างเครื่องที่เก็บสำเนา

การต้องรอการยืนยันจากเซิร์ฟเวอร์ที่เก็บสำเนาส่วนใหญ่ทำให้ความเร็วของดิสก์และความล่าช้าในการสื่อสารระหว่างเซิร์ฟเวอร์ส่งผลโดยตรงต่อเวลาที่ใช้ส่งการเปลี่ยนแปลง ยิ่งมีการเขียนข้อมูลมากขึ้น เซิร์ฟเวอร์เหล่านี้ก็ยิ่งต้องรับมือกับไฟล์รวมข้อมูลที่ส่งเข้ามา พร้อมกับรองรับคำขออ่านข้อมูลให้ทันด้วย

การบันทึกข้อมูลล่วงหน้า (WAL)

ในแบบจำลองนี้ บริการ Git ใช้บันทึกส่วนกลางในระบบจัดเก็บ blob เพื่อจัดลำดับการเปลี่ยนแปลงทั้งหมดใน Repository เมื่อไคลเอนต์ส่งการเปลี่ยนแปลงเข้ามา เซิร์ฟเวอร์จะนำไฟล์รวมข้อมูลไปจัดเก็บก่อน พร้อมเตรียมอัปเดตรายการอ้างอิงใน Repository ที่มีอยู่ในเครื่อง ก่อนเผยแพร่การเปลี่ยนแปลง เซิร์ฟเวอร์จะเพิ่มรายการลงในบันทึกส่วนกลางเพื่อระบุรายละเอียดการอัปเดตรายการอ้างอิงและไฟล์รวมข้อมูลที่เกี่ยวข้อง จากนั้นจึงอัปเดตบันทึกดังกล่าวด้วยวิธีตรวจสอบค่าเดิมก่อนแทนที่ด้วยค่าใหม่ โดยทั้งสองขั้นตอนต้องเกิดขึ้นเป็นการดำเนินการเดียวที่แบ่งแยกไม่ได้ หากไม่สามารถอัปเดตได้ ระบบจะปฏิเสธการเปลี่ยนแปลงนั้น แต่หากสาเหตุเกิดจากมีการอัปเดตของผู้อื่นแทรกเข้ามา เซิร์ฟเวอร์จะอ่านรายการเปลี่ยนแปลงที่เกิดขึ้นระหว่างนั้น ตรวจสอบว่าการอัปเดตรายการอ้างอิงของตนยังทำได้หรือไม่ แล้วลองดำเนินการอีกครั้งโดยใช้ไฟล์รวมข้อมูลที่อัปโหลดไว้แล้ว

แม้ผู้ส่งข้อมูลหลายรายจะอัปโหลดไฟล์รวมข้อมูลพร้อมกันได้ แต่การส่งการเปลี่ยนแปลงไปยังสาขาที่ไม่เกี่ยวข้องกันก็ยังต้องอัปเดตบันทึกส่วนกลางชุดเดียวกัน ความถี่ในการเผยแพร่บันทึกส่วนกลางฉบับใหม่จึงขึ้นอยู่กับระยะเวลาที่ใช้ตรวจสอบและแทนที่ข้อมูลจนสำเร็จหนึ่งรอบ หากระบบจัดเก็บ blob มีความหน่วงต่ำ ขั้นตอนนี้ก็จะทำได้รวดเร็วขึ้น ส่วนการรวมการอัปเดตหลายรายการเข้าด้วยกันช่วยให้แก้ไขรายการอ้างอิงหลายรายการได้ในการอัปเดตบันทึกส่วนกลางครั้งเดียว แต่ต้องแลกกับการรอสะสมงานและความล่าช้าที่เพิ่มขึ้น

แม้ระบบจัดเก็บ blob จะรับไฟล์รวมข้อมูลหลายไฟล์พร้อมกันได้ แต่การอัปเดตบันทึกส่วนกลางก็ยังต้องแย่งกันใช้ข้อมูลชุดเดียวกัน การเพิ่มขีดความสามารถของระบบจัดเก็บ blob เพียงอย่างเดียวจึงไม่สามารถแก้ปัญหาคอขวดนี้ได้ เพราะเซิร์ฟเวอร์ที่เก็บสำเนาแต่ละเครื่องยังต้องอ่านไฟล์รวมข้อมูลจากระบบจัดเก็บ blob และจัดทำดัชนีก่อน จึงจะให้บริการข้อมูลจากเครื่องของตนเองได้ หากมีการเขียนข้อมูลเข้ามาเร็วกว่าที่เซิร์ฟเวอร์จะจัดการทัน ข้อมูลในเครื่องเหล่านั้นก็จะตามหลังข้อมูลล่าสุด ส่งผลให้ไคลเอนต์ต้องรอนานขึ้นกว่าจะดึงคอมมิตที่เพิ่งส่งเข้ามาได้

การวางสถาปัตยกรรม Pando

จากข้อจำกัดที่กล่าวมา เราจึงพัฒนา Pando ขึ้นเป็นระบบรูปแบบใหม่สำหรับรองรับ Git Repository โดยมุ่งให้ไคลเอนต์จำนวนมากสามารถส่งการเปลี่ยนแปลงเข้ามาพร้อมกันได้ มาดูกันว่า Pando มีวิธีจัดการเรื่องนี้อย่างไร

มองข้อจำกัดในมุมใหม่

ก่อนเริ่มสร้าง Pando เรามองเห็นความเป็นไปได้ที่จะพัฒนาระบบให้ตอบโจทย์ของเราได้โดยยังคงใช้งานร่วมกับ Git ได้ตามเดิม สิ่งที่ต้องปรับเปลี่ยนไม่ใช่ Git แต่เป็นข้อกำหนดบางอย่างที่ระบบ Git ส่วนใหญ่ยึดถือว่าเป็นสิ่งจำเป็น ได้แก่

  • ความสอดคล้องของข้อมูลทั้ง Repository: การแสดงสถานะของข้อมูลอ้างอิงทั้งหมดให้ตรงกัน ณ เวลาใดเวลาหนึ่ง จำเป็นต้องอาศัยการประสานงานทั่วทั้ง Repository แต่หากระบบต้องจัดลำดับการอัปเดตข้อมูลอ้างอิงทั้งหมดร่วมกัน การส่งการเปลี่ยนแปลงไปยังสาขาต่างๆ ก็อาจต้องรอกัน แม้สาขาเหล่านั้นจะไม่เกี่ยวข้องกันเลยก็ตาม

  • การสร้างไฟล์รวมข้อมูลแยกสำหรับไคลเอนต์แต่ละราย: หากไคลเอนต์สองรายมีประวัติการเปลี่ยนแปลงโค้ดที่จัดเก็บไว้ในเครื่องต่างกัน แต่ต้องการดึงข้อมูลจากสาขาเดียวกัน เซิร์ฟเวอร์อาจต้องสร้างไฟล์รวมข้อมูลแยกให้แต่ละราย เพื่อหลีกเลี่ยงการส่งวัตถุข้อมูลที่ไคลเอนต์มีอยู่แล้วซ้ำอีก การสร้างไฟล์เหล่านี้ต้องใช้ทั้งกำลังประมวลผลของ CPU และทรัพยากรในการอ่านเขียนข้อมูลบนดิสก์ ทำให้การขยายระบบเพื่อรองรับไคลเอนต์ที่ดึงข้อมูลจำนวนมากเป็นเรื่องยาก

Pando จึงเลือกใช้รูปแบบการรักษาความสอดคล้องของข้อมูลที่ยืดหยุ่นกว่าเดิม โดยคำนึงถึงความสอดคล้องเชิงเหตุและผล เพื่อให้ระบบรองรับการเขียนข้อมูลจำนวนมากขึ้น พร้อมทั้งนำไฟล์รวมข้อมูลกลับมาใช้ซ้ำเพื่อรองรับการอ่านข้อมูลจำนวนมาก

ความสอดคล้องเชิงเหตุและผล (CC)

เมื่อไคลเอนต์ส่งการเปลี่ยนแปลงไปยังสาขาใดสาขาหนึ่ง Pando จะจัดเก็บไฟล์รวมข้อมูลที่ได้รับไว้ในระบบจัดเก็บ blob โดยไม่อนุญาตให้แก้ไขข้อมูลดังกล่าว ทุกครั้งที่มีการเปลี่ยนแปลงคอมมิตล่าสุดของสาขา Pando จะบันทึกเวอร์ชันของรายการอ้างอิงลงในระบบจัดเก็บข้อมูลกำกับ โดยระบุคอมมิตล่าสุด ไฟล์รวมข้อมูลที่บรรจุคอมมิตนั้น และเวอร์ชันก่อนหน้าของรายการอ้างอิงที่ไฟล์รวมข้อมูลดังกล่าวต้องใช้ประกอบ ทั้งระบบจัดเก็บ blob และระบบจัดเก็บข้อมูลกำกับสามารถขยายขีดความสามารถได้ด้วยการเพิ่มจำนวนเครื่องให้บริการ

เมื่อไคลเอนต์ดึงข้อมูลคอมมิตหรือรายการอ้างอิงจาก Pando ระบบจะต้องนำไฟล์รวมข้อมูลของรายการอ้างอิงเวอร์ชันต่างๆ ที่เกี่ยวข้องมาส่งให้ไคลเอนต์ตามลำดับ เพื่อจำลองสถานะข้อมูลที่ไคลเอนต์ซึ่งสร้างรายการอ้างอิงเวอร์ชันที่ร้องขอมีอยู่ในขณะนั้น วิธีนี้ทำให้ความสอดคล้องของข้อมูลโดยรวมทั้ง Repository มีข้อกำหนดที่ผ่อนปรนลง แต่ยังคงรับประกันว่าการอัปเดตรายการอ้างอิงแต่ละรายการจะมีลำดับที่แน่นอนเสมือนเกิดขึ้นทีละครั้ง ไคลเอนต์จะเกิดข้อขัดแย้งกันก็ต่อเมื่อส่งการเปลี่ยนแปลงไปยังรายการอ้างอิงเดียวกันพร้อมกันเท่านั้น ดังนั้น หากไคลเอนต์อ่านข้อมูลจากหลายสาขา ก็ไม่สามารถสรุปได้ว่าข้อมูลทั้งหมดนั้นสะท้อนสถานะของ Repository ณ เวลาเดียวกัน

ด้านซ้ายของภาพแสดงไฟล์รวมข้อมูลของสาขาหลัก 3 ไฟล์ที่สร้างขึ้นตามลำดับ โดยไฟล์แรกมีคอมมิต A, B และ C ไฟล์ที่สองมี D และ E ส่วนไฟล์ที่สามมี F ด้านขวาแสดงสาขาหลักและสาขางานที่แยกออกจากกันหลังคอมมิต C โดยสาขาหลักพัฒนาไปจนถึงคอมมิต G ส่วนสาขางานมีคอมมิต D, E และ F ตามลำดับ ไฟล์รวมข้อมูลล่าสุดของสาขางานมีคอมมิตทั้งสามรายการ ได้แก่ D, E และ F โดยเส้นประรอบ D และ E แสดงว่าคอมมิตทั้งสองมีอยู่ในไฟล์รวมข้อมูลก่อนหน้าของสาขางานด้วย ส่วนลูกศรสีน้ำเงินแสดงว่าไฟล์รวมข้อมูลใหม่ต้องอาศัยข้อมูลจากสาขาหลักที่คอมมิต G ขณะที่คอมมิต F มี E เป็นคอมมิตแม่ใน Git

การส่งการเปลี่ยนแปลงไปยังสาขาหลักจะเพิ่มไฟล์รวมข้อมูลที่มีเฉพาะข้อมูลใหม่ ซึ่งประกอบด้วยคอมมิตอย่างน้อยหนึ่งรายการ ส่วนการส่งการเปลี่ยนแปลงไปยังสาขาย่อยแต่ละครั้งจะรวมคอมมิตก่อนหน้าของงานในสาขานั้นเข้ามาอีกครั้ง ทำให้ไคลเอนต์ที่มีข้อมูลสาขาหลักตามเวอร์ชันที่เซิร์ฟเวอร์ประกาศไว้ สามารถดึงข้อมูลของสาขาย่อยได้จากไฟล์รวมข้อมูลเพียงไฟล์เดียว

วิธีนี้เป็นการรักษาความสอดคล้องเชิงเหตุและผล เมื่อไคลเอนต์ต้องการดึงข้อมูลรายการอ้างอิง ระบบจะทำให้ไคลเอนต์ได้รับประวัติการเปลี่ยนแปลงที่ผู้สร้างรายการอ้างอิงนั้นใช้เป็นพื้นฐาน นอกจากนี้ ระหว่างการส่งข้อมูล Pando ยังเป็นตัวกำหนดว่าทั้งไคลเอนต์และเซิร์ฟเวอร์จะถือว่ามีประวัติส่วนใดอยู่แล้วร่วมกัน เพื่อให้ไฟล์รวมข้อมูลที่ไคลเอนต์สร้างสามารถนำกลับมาใช้กับไคลเอนต์รายอื่นได้ง่ายขึ้น

ผลก็คือ เมื่อใช้งาน Git ผ่าน Pando ไคลเอนต์แต่ละรายอาจเห็นสถานะของสาขาต่างๆ ไม่ตรงกัน ณ เวลาใดเวลาหนึ่ง และไม่จำเป็นต้องเห็นการเปลี่ยนแปลงของทุกสาขาในลำดับเดียวกันระหว่างการคัดลอก Repository หรือการดึงข้อมูล ตัวอย่างเช่น หากมีการอัปเดตสาขา foo และ bar ขณะที่ระบบกำลังเจรจาแลกเปลี่ยนข้อมูลเพื่อคัดลอก Repository ทั้งหมด ไคลเอนต์รายแรกอาจได้รับข้อมูล foo เวอร์ชัน 1 และ bar เวอร์ชัน 2 ส่วนไคลเอนต์อีกรายอาจได้รับ foo เวอร์ชัน 2 และ bar เวอร์ชัน 1 ซึ่งในทางปฏิบัติไม่ได้เป็นปัญหาต่อผู้ใช้และเอเจนต์ เพราะสิ่งที่จำเป็นต่อการทำงานคือลำดับการเปลี่ยนแปลงภายในรายการอ้างอิงเดียวกัน ไม่ใช่ลำดับการเปลี่ยนแปลงของทุกรายการใน Repository

เปรียบเทียบแนวทางต่างๆ

เราเปรียบเทียบวิธีรักษาความสอดคล้องของข้อมูล 3 รูปแบบผ่านการจำลอง โดยให้ไคลเอนต์ 1,000 รายเลือกสาขาคนละสาขา แล้วส่งคอมมิตใหม่ที่ไม่ซ้ำกันหลายครั้ง โดยวัดทั้งระยะเวลารวมที่ใช้จริงและระยะเวลาตั้งแต่ส่งการเปลี่ยนแปลงจนสามารถดึงข้อมูลนั้นได้ ซึ่งตัวชี้วัดอย่างหลังมีความสำคัญต่อการประเมินภาระงาน เพราะช่วยให้เห็นว่าผู้ใช้และเอเจนต์ต้องใช้เวลารับส่งข้อมูลครบหนึ่งรอบนานเพียงใดเมื่อต้องทำงานร่วมกัน หรือทำงานผ่านระบบอื่นที่อาศัยการควบคุมเวอร์ชัน การจำลองนี้พิจารณาระบบที่ใช้เครื่องเสมือนตั้งแต่ระดับทั่วไปจนถึงระดับประสิทธิภาพสูง รวมถึงระบบจัดเก็บ blob และสำหรับวิธียืนยันการเปลี่ยนแปลงแบบสามขั้นตอน เราได้ผ่อนปรนข้อกำหนดด้านความสอดคล้องของข้อมูลทั่วทั้ง Repository สำหรับระบบยืนยันการเปลี่ยนแปลงแบบสามขั้นตอน

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 กับอีกสองแนวทางผ่านแบบจำลองนี้ โดยเลือกระดับฮาร์ดแวร์ จำนวนไคลเอนต์ และปรับการตั้งค่าอื่นๆ เพื่อดูผลลัพธ์ที่แตกต่างกัน

จากการจำลอง เมื่อระบบที่ใช้วิธียืนยันการเปลี่ยนแปลงแบบสามขั้นตอนต้องรับภาระงานมากขึ้น การเขียนข้อมูลก็ช้าลงอย่างเห็นได้ชัด เพราะดิสก์ทำงานเต็มขีดความสามารถ ส่วนระบบที่ใช้วิธีบันทึกข้อมูลล่วงหน้ากลับประสบปัญหาการอ่านข้อมูลล่าช้า เนื่องจากเซิร์ฟเวอร์ไม่สามารถอ่านข้อมูลจากบันทึกการเปลี่ยนแปลงได้ทัน ไม่ว่าจะเป็นการทดสอบจริงกับ Monorepo ของ OpenAI หรือการจำลองครั้งนี้ ผลลัพธ์ต่างแสดงให้เห็นว่า Pando รองรับไคลเอนต์หลายพันรายที่ส่งการเปลี่ยนแปลงพร้อมกันได้ โดยยังรักษาประสิทธิภาพไว้ได้แม้ระบบมีภาระงานสูง

เมื่อได้รับคำขอส่งการเปลี่ยนแปลง Pando จะตรวจสอบไฟล์รวมข้อมูลที่ได้รับไปพร้อมกับส่งข้อมูลไปยังระบบจัดเก็บ blob โดยไม่ต้องแตกไฟล์เพื่อนำข้อมูลไปจัดเก็บใน Repository ภายในเครื่อง เมื่อไคลเอนต์เริ่มดึงข้อมูล Pando จะเลือกไฟล์รวมข้อมูลที่จัดเก็บไว้ตามที่ไคลเอนต์ต้องการ จากนั้นไคลเอนต์จะดาวน์โหลดไฟล์เหล่านั้น วิธีนี้ทำให้เซิร์ฟเวอร์ไม่ต้องนำข้อมูลจากไฟล์รวมข้อมูลไปบันทึกใน Repository ภายในเครื่อง และไม่ต้องเสียทรัพยากรไปกับการแตกไฟล์หรือรวมข้อมูลเป็นไฟล์ใหม่ เนื่องจากเซิร์ฟเวอร์ของ Pando ทำหน้าที่หลักในการเจรจาแลกเปลี่ยนข้อมูลและเลือกไฟล์รวมข้อมูล จึงสามารถทำงานบนฮาร์ดแวร์ทั่วไปได้

การออกแบบนี้ทำให้ Pando รองรับการใช้งานที่เพิ่มขึ้นได้จนกว่าจะถึงขีดจำกัดของไคลเอนต์ เครือข่ายที่เชื่อมต่อกับ Pando หรือบริการจัดเก็บ blob หากระบบมีทรัพยากรเพียงพอ ไคลเอนต์ที่ทำงานบนคนละสาขาโดยไม่เกี่ยวข้องกันก็สามารถรับส่งไฟล์รวมข้อมูลได้ด้วยความเร็วสูงสุดเท่าที่การเชื่อมต่อของแต่ละรายรองรับ และเมื่อต้องการให้ไคลเอนต์จำนวนมากขึ้นรับส่งข้อมูลได้เร็วเท่าเดิม เราก็เพียงเพิ่มเซิร์ฟเวอร์ Pando และขยายระบบจัดเก็บข้อมูลกำกับกับระบบจัดเก็บ blob ไปพร้อมกัน

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

เมื่อเอเจนต์แก้ไขไฟล์และสร้างคอมมิตในเครื่องแล้ว ก็จะใช้คำสั่ง git push เพื่อส่งการเปลี่ยนแปลงไปยังสาขาที่ต้องการ โดย Git จะรวบรวมข้อมูลที่เปลี่ยนแปลงเป็นไฟล์รวมข้อมูล เมื่อ Pando ได้รับไฟล์นี้ ก็จะส่งต่อไปจัดเก็บในระบบจัดเก็บ blob และตรวจสอบความถูกต้องด้วยการถอดรหัสเพียงครั้งเดียว จากนั้นจึงบันทึกเวอร์ชันใหม่ของรายการอ้างอิงและอัปเดตตำแหน่งคอมมิตล่าสุดของสาขาผ่านการทำธุรกรรมเดียวที่ครอบคลุมข้อมูลหลายรายการ เพื่อให้การอัปเดตเกิดขึ้นอย่างครบถ้วนในคราวเดียว วิธีนี้ช่วยให้ไคลเอนต์อ่านข้อมูลที่เพิ่งส่งไปได้ทันทีหลังการอัปเดตสำเร็จ โดยไม่ต้องเพิ่มจำนวนครั้งในการอ่านเขียนข้อมูลกำกับตามปริมาณข้อมูลที่เปลี่ยนแปลง เพราะกระบวนการนี้ใช้การอ่านเขียนข้อมูลกำกับในระดับ O(1)O(1)

เมื่อไคลเอนต์อีกรายต้องการดึงข้อมูลจากสาขานั้น Pando จะตรวจสอบประวัติการเปลี่ยนแปลงที่ไคลเอนต์มีอยู่แล้ว เพื่อเลือกไฟล์รวมข้อมูลที่จำเป็นจากระบบจัดเก็บ โดยใช้การอ่านข้อมูลกำกับเพียง O(log⁡Δ)O(\log \Delta) ครั้ง ซึ่ง Δ\Delta หมายถึงระยะห่างที่สั้นที่สุดเมื่อวัดจากจำนวนเวอร์ชันของรายการอ้างอิง ระหว่างข้อมูลที่ไคลเอนต์มีอยู่กับข้อมูลที่ต้องการ จากนั้นเซิร์ฟเวอร์จะส่ง URI ของไฟล์รวมข้อมูลกลับไปให้ไคลเอนต์ดาวน์โหลดผ่านบริการ Express ซึ่งจัดเก็บไฟล์เหล่านี้ไว้ในแคชตามภูมิภาคต่างๆ

การออกแบบนี้อาศัยบริการหลักสามส่วนของ Pando ได้แก่ การส่งการเปลี่ยนแปลง การดึงข้อมูล และการรวมและจัดระเบียบไฟล์ข้อมูล

ขยายขีดความสามารถในการ push

เมื่อไคลเอนต์ส่งการเปลี่ยนแปลง เซิร์ฟเวอร์ Git ทั่วไปจะทำตัวเหมือนผู้เล่นโป๊กเกอร์ที่เปิดไพ่ในมือทั้งหมดให้ดู โดยแจ้งให้ไคลเอนต์ทราบว่ามีสาขาใดอยู่บ้าง จากนั้นไคลเอนต์จะรวบรวมคอมมิต tree และ blob เพื่อสร้างไฟล์รวมข้อมูล โดยตัดออบเจ็กต์ที่ทราบว่าเซิร์ฟเวอร์มีอยู่แล้วออก เพื่อไม่ให้ส่งข้อมูลซ้ำ

ยิ่งเซิร์ฟเวอร์แจ้งรายการอ้างอิงให้ไคลเอนต์ทราบมากเท่าไร ไฟล์รวมข้อมูลที่ส่งไปก็อาจมีขนาดเล็กลง แต่การนำไฟล์นั้นกลับมาใช้กับไคลเอนต์รายอื่นจะทำได้ยากขึ้น เพราะไคลเอนต์รายอื่นอาจไม่มีออบเจ็กต์บางรายการที่ผู้ส่งละไว้จากไฟล์รวมข้อมูล

แต่ Pando ใช้วิธีที่ต่างออกไป แทนที่จะเปิดไพ่ทั้งหมดในมือเหมือนเซิร์ฟเวอร์ Git ทั่วไป Pando จะเลือกแจ้งเฉพาะบางสาขาให้ไคลเอนต์ทราบ โดยมีเหตุผลสำคัญสองประการ ได้แก่

  1. การแสดงสถานะของรายการอ้างอิงทั้งหมดให้สอดคล้องกันต้องใช้ทรัพยากรมาก หากมีสาขาหลายล้านสาขา เฉพาะข้อมูลรายการอ้างอิงที่เซิร์ฟเวอร์ต้องแจ้งให้ไคลเอนต์ทราบก็อาจมีขนาดเกิน 100 MiB แล้ว การทำเช่นนี้ทำให้เซิร์ฟเวอร์แต่ละเครื่องต้องเลือกระหว่างการเก็บข้อมูลล่าสุดที่แสดงความสัมพันธ์ของรายการอ้างอิงทั้งหมด ซึ่งต้องประสานการทำงานกันอย่างมาก หรือการค้นหาข้อมูลจากฐานข้อมูลที่มีภาระเพิ่มขึ้นตามจำนวนรายการอ้างอิง

  2. การแจ้งรายการอ้างอิงให้ไคลเอนต์ทราบเพียงบางส่วนทำให้ Pando ควบคุมได้ว่าไคลเอนต์จะละเว้นออบเจ็กต์ใดจากไฟล์รวมข้อมูลได้บ้าง โดยไฟล์ที่สร้างขึ้นจะพึ่งพาเฉพาะออบเจ็กต์ที่เซิร์ฟเวอร์อนุญาตให้ละเว้นอย่างชัดเจนเท่านั้น เมื่อ Pando บันทึกข้อมูลที่ไฟล์ดังกล่าวต้องพึ่งพาไว้ ก็สามารถนำไฟล์รวมข้อมูลที่ไคลเอนต์ส่งมาไปใช้ตอบสนองคำขอดึงข้อมูลในภายหลังได้ โดยไม่ต้องสร้างไฟล์ใหม่

ยิ่งเซิร์ฟเวอร์ Git แจ้งรายการอ้างอิงให้ไคลเอนต์ทราบมากเท่าไร ไคลเอนต์ก็ยิ่งมีข้อมูลสำหรับพิจารณาว่าออบเจ็กต์ใดไม่จำเป็นต้องส่งซ้ำ ทำให้สร้างไฟล์รวมข้อมูลที่มีขนาดเล็กลงได้ แต่หากต้องการนำไฟล์เหล่านี้กลับมาใช้ซ้ำ เซิร์ฟเวอร์จะต้องตรวจสอบและยืนยันได้ว่าไคลเอนต์ที่ขอดึงข้อมูลมีออบเจ็กต์ทุกชิ้นที่ไฟล์นั้นต้องพึ่งพาอยู่แล้ว มิฉะนั้น ไฟล์ที่ส่งไปอาจไม่มีออบเจ็กต์บางรายการที่ไคลเอนต์จำเป็นต้องใช้

ในทางกลับกัน หากเซิร์ฟเวอร์ Git ไม่แจ้งรายการอ้างอิงใด ๆ ให้ไคลเอนต์ทราบเลย ไคลเอนต์ก็จะสร้างไฟล์รวมข้อมูลที่มีประวัติทั้งหมดของ Repository ซึ่งสามารถนำไปใช้กับไคลเอนต์รายอื่นได้ทุกราย แต่ข้อเสียสำคัญคือการดึงข้อมูลจากสาขาใดก็ตามจะกลายเป็นการดาวน์โหลดข้อมูลทั้งหมดของ Repository เสมือนคัดลอก Repository ใหม่ทั้งชุด

กราฟเส้นแสดงผลของการประกาศรายการอ้างอิงต่อขนาดไฟล์รวมข้อมูล โดยเปรียบเทียบตั้งแต่กรณีที่ไม่มีการประกาศ reference และต้องส่งประวัติทั้งหมด กรณีที่ประกาศสาขาหลัก ไปจนถึงกรณีที่ประกาศรายการอ้างอิงทั้งหมดและส่งเพียงออบเจ็กต์ใหม่

ยิ่งเซิร์ฟเวอร์แจ้งรายการอ้างอิงให้ไคลเอนต์ทราบมากขึ้น ไฟล์รวมข้อมูลก็ยิ่งมีขนาดเล็กลง แต่เพียงแจ้งรายการอ้างอิงรายการเดียวก็อาจช่วยลดขนาดไฟล์ได้เป็นส่วนใหญ่แล้ว

ตามปกติ เมื่อไคลเอนต์ส่งการเปลี่ยนแปลงเพื่อสร้างสาขาสำหรับพัฒนาฟีเจอร์ใหม่ Pando จะแจ้งเฉพาะสาขาหลักที่กำหนดไว้ให้ใช้เป็นข้อมูลอ้างอิง วิธีนี้ช่วยให้ปริมาณข้อมูลที่ต้องแจ้งไม่เพิ่มขึ้นตามจำนวนสาขาใน Repository แต่ก็มีข้อแลกเปลี่ยนอยู่เช่นกัน เพราะหากวิศวกรส่งการเปลี่ยนแปลงไปยังสาขาเดิมหลายครั้ง หรือสร้างสาขาสำหรับพัฒนาฟีเจอร์หนึ่งต่อยอดจากอีกสาขาหนึ่ง ไคลเอนต์จะต้องส่งข้อมูลทั้งหมดที่อยู่นอกประวัติของสาขาหลักซ้ำอีกครั้ง

วิธีนี้ทำให้การส่งการเปลี่ยนแปลงไปยังสาขาเดิมในครั้งต่อๆ ไปต้องใช้ไฟล์รวมข้อมูลที่มีขนาดใหญ่ขึ้น เพราะอาจมีออบเจ็กต์ที่เคยส่งไปแล้วรวมอยู่ด้วย ส่งผลให้ไคลเอนต์ใช้แบนด์วิดท์เพิ่มขึ้น อย่างไรก็ตาม สำหรับ Monorepo ของเรา ไฟล์รวมข้อมูลที่สร้างด้วยวิธีนี้มีขนาดมัธยฐานต่ำกว่า 20 KiB และแม้แต่เปอร์เซ็นไทล์ที่ 99 ก็ยังมีขนาดต่ำกว่า 3 MiB เราจึงมองว่าเป็นข้อแลกเปลี่ยนที่คุ้มค่า เพราะไคลเอนต์ที่มีประวัติของสาขาหลักตามที่เซิร์ฟเวอร์แจ้งไว้แล้ว สามารถดึงข้อมูลของสาขาที่ต้องการได้จากไฟล์รวมข้อมูลเพียงไฟล์เดียว

หลังจากเลือกแล้วว่าจะแจ้งรายการอ้างอิงใดให้ไคลเอนต์ทราบ Pando จะรับการเปลี่ยนแปลงที่ส่งเข้ามาและอัปเดตระบบจัดเก็บข้อมูลกำกับ โดยแบ่งการอัปเดตออกเป็นสองส่วน ได้แก่ ส่วนที่ต้องดำเนินการให้เสร็จทันที และส่วนที่สามารถดำเนินการภายหลังได้ การอัปเดตส่วนแรกช่วยให้ไคลเอนต์ดึงข้อมูลคอมมิตที่เพิ่งอัปเดตได้ทันที โดยระบบจะบันทึกเวอร์ชันใหม่ของรายการอ้างอิง พร้อมปรับข้อมูลที่เชื่อมโยงรายการอ้างอิงกับเวอร์ชันล่าสุด ซึ่งบันทึกทั้งคอมมิตล่าสุด ข้อมูลที่ใช้อ้างอิงร่วมกันระหว่างการเจรจาเพื่อสร้างไฟล์รวมข้อมูล และตำแหน่งของไฟล์รวมข้อมูลที่ไคลเอนต์ส่งเข้ามา

ในขณะเดียวกัน Pando จะบันทึกงานที่สามารถดำเนินการภายหลัง เช่น การจัดกำหนดการรวมและจัดระเบียบไฟล์ข้อมูล ไว้ในธุรกรรมเดียวกันด้วย หากมีการส่งการเปลี่ยนแปลงขนาดใหญ่มาก ระบบจะบันทึกคอมมิตเพียงบางส่วนให้เสร็จในขั้นตอนแรก ส่วนที่เหลือสามารถดำเนินการภายหลังได้ วิธีนี้ทำให้จำนวนครั้งที่ต้องอ่านและเขียนข้อมูลในระบบจัดเก็บข้อมูลกำกับคงที่ ไม่ว่าจะส่งการเปลี่ยนแปลงขนาดใหญ่เพียงใดก็ตาม

จุดสำคัญคือเซิร์ฟเวอร์ Pando สามารถรับการเปลี่ยนแปลงที่ส่งไปยังรายการอ้างอิงต่างกันและไม่เกี่ยวข้องกันได้อย่างอิสระ โดยมีเพียงข้อมูลที่ระบุเวอร์ชันปัจจุบันของรายการอ้างอิงแต่ละรายการเท่านั้นที่อาจเกิดการแย่งกันอัปเดต ซึ่งเราป้องกันด้วยกลไกตรวจสอบค่าเดิมก่อนแทนที่ด้วยค่าใหม่ โดยให้การดำเนินการทั้งหมดเกิดขึ้นเป็นขั้นตอนเดียวที่แบ่งแยกไม่ได้ เราจึงสามารถเพิ่มปริมาณการเขียนข้อมูลที่ระบบรองรับได้ด้วยการเพิ่มเซิร์ฟเวอร์ Pando เพิ่มหน่วยให้บริการของระบบจัดเก็บข้อมูลกำกับ หรือเพิ่มระบบจัดเก็บ blob

การดึงข้อมูลและคัดลอก Repository ด้วยความเร็วสูงสุดที่เครือข่ายรองรับ

เมื่อไคลเอนต์ดึงข้อมูลจาก Git จะต้องแจ้งรหัสคอมมิตสองชุดให้เซิร์ฟเวอร์ทราบ ได้แก่ คอมมิตที่มีอยู่แล้ว (have) และคอมมิตที่ต้องการ (want) สำหรับเซิร์ฟเวอร์ Git ทั่วไป ระบบจะตรวจสอบว่าออบเจ็กต์ใดบ้างที่เข้าถึงได้จากคอมมิตที่ต้องการ แต่ยังไม่มีอยู่ในชุดข้อมูลของไคลเอนต์ จากนั้นจึงบีบอัดและส่งออบเจ็กต์เหล่านั้นให้ไคลเอนต์ แม้เซิร์ฟเวอร์ Git แบบโอเพนซอร์สจะมีดัชนีหลายประเภทที่ช่วยให้ค้นหาข้อมูลในเครื่องได้รวดเร็วขึ้น แต่การสร้างไฟล์รวมข้อมูลด้วยวิธีนี้ก็ยังอาจใช้ทรัพยากรในการประมวลผลสูง และโดยทั่วไปไม่เหมาะกับการจัดเก็บไว้ในแคชเพื่อนำกลับมาใช้ซ้ำ

แต่ Pando ใช้วิธีต่างออกไป โดยนำข้อมูลความสัมพันธ์ของคอมมิตที่บันทึกไว้ตอนรับการเปลี่ยนแปลงมาใช้ค้นหาคอมมิตที่ไคลเอนต์มีอยู่แล้ว (have) และคอมมิตที่ต้องการ (want) เพื่อระบุเวอร์ชันของรายการอ้างอิงที่มีคอมมิตเหล่านั้นอยู่ ซึ่งแต่ละเวอร์ชันจะมีข้อมูลเชื่อมโยงไปยังไฟล์รวมข้อมูลที่ Pando เคยได้รับ จากนั้นเซิร์ฟเวอร์จะใช้ข้อมูลเหล่านี้พิจารณาว่าไคลเอนต์ยังต้องการออบเจ็กต์ใดบ้าง แล้วเลือกไฟล์รวมข้อมูลที่มีออบเจ็กต์ที่จำเป็นครบถ้วน

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 โดยหากไคลเอนต์ต้องการดึงข้อมูลจากสาขาย่อย ระบบก็จะรวมข้อมูลที่จำเป็นจากสาขาหลักซึ่งสาขาย่อยนั้นต้องอาศัยเข้ามาด้วย

โปรโตคอล Git อนุญาตให้เซิร์ฟเวอร์ส่งไฟล์รวมข้อมูลผ่านการตอบกลับ HTTP ได้เพียงไฟล์เดียวต่อการคัดลอก Repository หรือดึงข้อมูลแต่ละครั้ง แต่ Git มีความสามารถอีกอย่างที่ยังไม่เป็นที่รู้จักแพร่หลายนัก นั่นคือ packfile-uri ซึ่งเปิดให้เซิร์ฟเวอร์ส่งลิงก์สำหรับดาวน์โหลดไฟล์รวมข้อมูลเพิ่มเติมแยกต่างหากได้ Pando ใช้วิธีนี้เพียงวิธีเดียว โดยส่งไฟล์รวมข้อมูลเปล่าไปกับการตอบกลับ HTTP โดยตรง ส่วนไฟล์ที่มีข้อมูลจริงจะให้ดาวน์โหลดผ่านลิงก์แทน OpenAI ยังได้ปรับปรุง packfile-uri ในหลายส่วนและส่งการปรับปรุงเหล่านั้นกลับไปยังโครงการ Git ต้นทาง เพื่อรองรับการทำงานรูปแบบนี้

เนื่องจากระบบของ OpenAI ทำงานอยู่ในหลายภูมิภาค เราจึงติดตั้งกลุ่มเซิร์ฟเวอร์ Pando พร้อมแคชภายในแต่ละภูมิภาค โดยมี Express เป็นบริการส่งไฟล์รวมข้อมูลที่ใช้แคชสำหรับข้อมูลจากระบบจัดเก็บ blob ส่วนข้อมูลกำกับจะมีอีกบริการหนึ่งรับหน้าที่จัดเก็บไว้ในแคชโดยเฉพาะ ด้วยวิธีนี้ Pando จึงสามารถตอบสนองคำขอที่ระบุรหัสคอมมิตหรือคำขออ่านรายการอ้างอิงแบบพิเศษได้โดยใช้ข้อมูลจากแคชภายในกลุ่มเซิร์ฟเวอร์นั้นทั้งหมด เราใช้วิธีกระจายคำขอไปยังเซิร์ฟเวอร์ Express ด้วยการแฮชแบบสอดคล้องกัน โดยมีอัตราการพบข้อมูลที่ต้องการในแคชสูงกว่า 99%

ระบบแคชนี้ช่วยให้ Pando ส่งข้อมูลได้ในปริมาณมหาศาลต่อหน่วยเวลา จนสามารถใช้ความเร็วของเครือข่ายระหว่างไคลเอนต์กับ Pando ได้อย่างเต็มที่ เราได้ทดสอบและยืนยันแล้วว่า การใช้ URI ของไฟล์รวมข้อมูลร่วมกับแคชภายในแต่ละภูมิภาค ทำให้การคัดลอก Repository และการดึงข้อมูลสามารถใช้ความเร็วของการเชื่อมต่อเครือข่าย 10 Gbps ได้เต็มขีดความสามารถ แม้จะใช้เพียงเครื่องเสมือนมาตรฐานของผู้ให้บริการคลาวด์ก็ตาม

การรวมและจัดระเบียบไฟล์ข้อมูลเพื่อลดเวลารอรับข้อมูลไบต์แรก

เซิร์ฟเวอร์ Git ส่วนใหญ่จำเป็นต้องจัดเก็บออบเจ็กต์ลงในไฟล์รวมข้อมูลใหม่และกำจัดข้อมูลที่ไม่จำเป็น เพื่อรักษาประสิทธิภาพของ Repository ที่มีขนาดใหญ่ขึ้นอย่างต่อเนื่อง แม้ทั้งสองกระบวนการจะช่วยคืนพื้นที่จัดเก็บข้อมูล แต่ก็ต้องใช้กำลังประมวลผลและทรัพยากรในการอ่านเขียนข้อมูลสูง

สำหรับ Repository ที่มีการเปลี่ยนแปลงอย่างรวดเร็วอย่าง Monorepo ของ OpenAI การส่งลิงก์ดาวน์โหลดไฟล์รวมข้อมูลแยกสำหรับการเปลี่ยนแปลงทุกครั้งที่เกิดขึ้นนับจากการดึงข้อมูลครั้งล่าสุดย่อมไม่ใช่วิธีที่เหมาะสม เพราะไคลเอนต์อาจต้องดาวน์โหลดไฟล์รวมข้อมูลหลายหมื่นไฟล์ต่อวัน เพียงเพื่ออัปเดตสาขาหลักให้เป็นเวอร์ชันล่าสุด

เพื่อไม่ให้ไคลเอนต์ต้องดาวน์โหลดไฟล์จำนวนมาก Pando จะทยอยรวมไฟล์ข้อมูลเข้าด้วยกันในเบื้องหลัง โดยใช้แนวคิดคล้ายกับวิธีจัดระเบียบข้อมูลในระบบจัดเก็บแบบต้นไม้ที่รวมข้อมูลเป็นลำดับชั้น ทุกครั้งที่มีการส่งการเปลี่ยนแปลง Pando จะสร้างไฟล์รวมข้อมูลระดับ L0 ซึ่งเป็นหน่วยข้อมูลขนาดเล็กที่สุดที่ระบบส่งได้ แล้วให้กระบวนการเบื้องหลังค่อยๆ จัดกลุ่มไฟล์เหล่านี้ให้ครอบคลุมรายการอ้างอิงหลายเวอร์ชันที่เรียงต่อกัน โดยแต่ละระดับจะขยายขนาดเพิ่มขึ้นเป็นทวีคูณจาก L0 ปัจจุบัน Monorepo ของ OpenAI ใช้การจัดกลุ่มไฟล์ลักษณะนี้ไปจนถึงระดับ L4

การจัดกลุ่มไฟล์ของ Pando ช่วยเพิ่มประสิทธิภาพได้ถึงสองทาง ทางแรกคือเมื่อมีไฟล์สะสมอยู่ในกลุ่มมากพอ ระบบจะมอบหมายให้กระบวนการเบื้องหลังนำไฟล์เหล่านั้นมารวมเป็นไฟล์เดียว พร้อมจัดเรียงออบเจ็กต์ใหม่เพื่อให้บีบอัดข้อมูลได้มากขึ้น อีกทางหนึ่งคือช่วยให้ค้นหาไฟล์ที่ต้องส่งได้เร็วขึ้น เพราะกลุ่มไฟล์เหล่านี้ทำหน้าที่เหมือนทางลัดที่ช่วยข้ามรายการอ้างอิงหลายเวอร์ชัน แทนที่จะต้องตรวจสอบทีละรายการ Pando จึงค้นหาดัชนีหลายระดับพร้อมกัน และเลือกไฟล์ที่ไคลเอนต์ต้องใช้ได้โดยใช้เวลาที่เพิ่มขึ้นช้ากว่าจำนวนเวอร์ชันที่ต้องตรวจสอบมาก แม้ยังไม่ได้รวมไฟล์ในกลุ่มเข้าด้วยกันจริงๆ โครงสร้างนี้ก็ช่วยให้เลือกไฟล์ได้มีประสิทธิภาพขึ้น พร้อมลดภาระด้านการรับส่งข้อมูลและความหน่วงของระบบ

Compaction in Pando

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

Compaction in progressPublished cover

กระบวนการรวมและจัดระเบียบข้อมูลจะนำไฟล์รวมข้อมูลจากรายการอ้างอิงหลายเวอร์ชันมารวมกันในเบื้องหลัง ช่วยให้ไคลเอนต์ดึงข้อมูลได้อย่างมีประสิทธิภาพมากขึ้นโดยไม่ต้องดาวน์โหลดไฟล์จำนวนมากเหมือนเดิม

จุดที่ Pando แตกต่างจากเซิร์ฟเวอร์ Git ส่วนใหญ่คือ ระบบจะเก็บไฟล์ที่รวบรวมข้อมูลไว้ในทุกระดับ แม้จะมีการจัดระเบียบข้อมูลแล้วก็ตาม โดยไม่ลบหรือกำจัดไฟล์เหล่านั้นทิ้ง ทำให้สามารถนำไฟล์จากทุกระดับมาใช้ส่งข้อมูลให้ไคลเอนต์ได้อย่างต่อเนื่องตามคำขอแต่ละครั้ง ซึ่งระบุทั้งข้อมูลที่มีอยู่แล้ว (have) และข้อมูลที่ต้องการ (want) ส่วนการอ่านวัตถุข้อมูลใดๆ ผ่าน API ของ Pando นั้น โดยทั่วไปไฟล์ระดับ L0 จะต้องอ่านข้อมูลในปริมาณน้อยกว่าไฟล์ระดับ L4 เพราะวัตถุข้อมูลในระดับ L0 มีแนวโน้มที่จะถูกบีบอัดน้อยกว่า

การรวมและจัดระเบียบข้อมูลของ Pando ช่วยลดปริมาณข้อมูลที่ต้องส่งให้ไคลเอนต์ได้อย่างมาก ขณะเดียวกันก็ช่วยให้การเลือกไฟล์ที่ต้องส่งไม่ซับซ้อนเพิ่มขึ้นตามระยะห่างจากข้อมูลล่าสุดของระบบ แม้ไคลเอนต์จะมีข้อมูลที่ล้าหลังไปมากก็ตาม วิธีนี้สำคัญอย่างยิ่งสำหรับ Repository ขนาดใหญ่ที่มีการเปลี่ยนแปลงอยู่ตลอดเวลา ซึ่งเราคาดว่าจะพบเห็นได้ทั่วไปในการพัฒนาซอฟต์แวร์ยุคต่อไป

การสร้าง Pando

เราใช้ Codex ช่วยพัฒนา Pando จนพร้อมใช้งานจริงภายในเวลาเพียง 10 สัปดาห์นับจากเริ่มโครงการ โดยมีวิศวกรเพียงคนเดียวออกแบบส่วนสำคัญของระบบ ก่อนให้ทีมเอเจนต์ลงมือพัฒนา และใช้วิศวกรอีกเพียงไม่กี่คนในการนำระบบไปใช้งานจริง Pando เริ่มให้บริการในระบบที่ใช้งานจริงเมื่อเดือนเมษายนที่ผ่านมา และเป็นส่วนหนึ่งของชุดเครื่องมือจัดการระบบควบคุมเวอร์ชันโค้ดที่ OpenAI ใช้

เราใช้วิธีการที่หลากหลายในการตรวจสอบความถูกต้องของการออกแบบ Pando โดยยึดหลักการพัฒนาซอฟต์แวร์ให้มีรากฐานที่ปลอดภัยยิ่งขึ้น⁠ เนื่องจาก Pando เป็นโครงการที่เริ่มพัฒนาขึ้นใหม่ทั้งหมด เราจึงเลือกใช้ภาษา Rust และไม่พึ่งพาระบบหรือส่วนประกอบเดิม เราสร้างชุดทดสอบความสอดคล้องกับมาตรฐานเพื่อให้เอเจนต์สามารถตรวจสอบการเปลี่ยนแปลงของตนเทียบกับพฤติกรรมมาตรฐานของ Git เรายังพัฒนาระบบทดสอบด้วยการจำลองสถานการณ์ที่ให้ผลลัพธ์เหมือนเดิมเมื่อทดสอบภายใต้เงื่อนไขเดียวกัน เพื่อใช้ตรวจสอบว่าระบบยังคงรักษาเงื่อนไขและคุณสมบัติที่กำหนดไว้ได้หรือไม่ วิธีนี้ช่วยให้เราค้นพบและลดความเสี่ยงจากปัญหาที่ตรวจพบได้ยาก ซึ่งเกิดจากลำดับการทำงานของกระบวนการที่ทำงานพร้อมกัน รวมถึงปัญหาที่กลับมาเกิดซ้ำในขั้นตอนการเจรจาแลกเปลี่ยนข้อมูลและการวางแผนเลือกไฟล์รวมข้อมูล

เราใช้โมเดล AI หลากหลายรุ่นช่วยพัฒนาระบบนี้ โดยนำโมเดลใหม่ๆ ที่เปิดให้ใช้งานมาปรับปรุงให้ Pando ทำงานได้ดีและน่าเชื่อถือยิ่งขึ้น เราใช้ GPT‑6 Astra ให้เอเจนต์เขียนแบบจำลองทางคณิตศาสตร์ด้วย Lean และ TLA+ เพื่ออธิบายหลักการทำงานของ Pando อย่างเป็นระบบ และนำแบบจำลองดังกล่าวมาใช้ค้นหาและแก้ไขข้อบกพร่อง ตลอดจนพิสูจน์ว่าระบบมีคุณสมบัติตามที่กำหนดไว้ เอเจนต์ทำงานต่อเนื่องหลายช่วง ช่วงละหลายร้อยชั่วโมง เพื่อค้นหาและแก้ไขปัญหาที่เกิดขึ้นเฉพาะในบางเงื่อนไข เรายังให้เอเจนต์ใช้โมเดลที่รองรับความสามารถของ Daybreak ทดสอบ Git และ Pando ในลักษณะจำลองการโจมตีตลอด 24 ชั่วโมงอีกด้วย

การนำ Pando มาใช้งานจริง

ในช่วงแรก เรานำ Pando มาใช้เป็นระบบตัวกลางที่รับและส่งต่อคำขออ่านเขียนข้อมูลไปยังระบบควบคุมเวอร์ชันโค้ดเดิม ทำให้เรารักษาข้อมูลของทั้งสองระบบให้ตรงกันได้ และค่อยๆ ย้ายการใช้งานมาไว้บน Pando อย่างปลอดภัยโดยไม่ต้องหยุดให้บริการ

Pando ยังสามารถตรวจสอบคำขอที่ซ้ำกันและรวมคำขอต่างๆ ก่อนส่งต่อไปยังระบบเดิมได้ด้วย ซึ่งเป็นส่วนหนึ่งของการออกแบบระบบแคช วิธีนี้ช่วยให้เครื่องมือที่เราใช้อยู่เดิมยังทำงานได้อย่างมีเสถียรภาพระหว่างที่เราทยอยย้ายแต่ละระบบมาใช้ Pando และเมื่อมีงานย้ายมาใช้ Pando มากขึ้น การส่งการเปลี่ยนแปลงจากงานเหล่านั้นก็ช่วยเพิ่มข้อมูลในแคชของ Pando ส่งผลให้ระบบเบื้องหลังเดิมของเรามีความน่าเชื่อถือมากขึ้นด้วย

เพื่อให้การใช้งาน Pando มีประสิทธิภาพสูงสุด เราจึงเริ่มนำ Git รุ่นที่เราเผยแพร่ต่อสาธารณะมาติดตั้งบนเครื่องของนักพัฒนา รวมถึงในแอป ChatGPT สำหรับเดสก์ท็อปด้วย ส่วนการเปลี่ยนไคลเอนต์มาใช้ Pando เราพัฒนาเครื่องมือภายในที่ทำงานครอบคำสั่ง Git เพื่อช่วยทยอยย้ายไคลเอนต์เป็นระยะๆ นอกจากนี้ เรายังสร้างเครื่องมืออีกมากมายสำหรับจัดการคำขอเปลี่ยนแปลงโค้ด ตรวจสอบโค้ด และให้บริการ API สำหรับระบบอัตโนมัติ พร้อมย้ายระบบเชื่อมต่อหลายร้อยรายการที่พึ่งพา API รุ่นเก่า เว็บฮุก และการดาวน์โหลด Repository มายัง Pando ควบคู่กับการย้ายการใช้งาน Git

Codex ยังช่วยให้นักพัฒนาและทีมต่างๆ เปลี่ยนมาใช้ Pando ได้ง่ายขึ้นด้วย เราสร้างชุดทักษะสำหรับเตรียมไฟล์โค้ดที่ดึงมาไว้ในเครื่องให้พร้อมใช้งาน และแนะนำเอเจนต์ให้ทำงานกับระบบใหม่ได้อย่างถูกต้อง นักพัฒนาจึงเพียงแค่สั่ง Codex ให้ย้ายสภาพแวดล้อมการพัฒนาและบริการต่าง ๆ ของตนมาใช้ Pando ได้เลย

การเปลี่ยนมาใช้ Pando ช่วยให้การรวมโค้ดมีความน่าเชื่อถือมากขึ้น และลดค่ามัธยฐานของความหน่วงในการส่งการเปลี่ยนแปลงได้เกือบ 80%

Pando ส่งข้อมูล Git ได้หนึ่งเพตะไบต์ต่อวันด้วย CPU เพียงไม่กี่คอร์ Pando เคยส่งแพ็ก Git มากกว่า 57 เทระไบต์ภายในชั่วโมงเดียว โดยข้อมูลมากกว่า 99% มาจากแคชบนดิสก์ภายในเครื่อง

การนำไฟล์รวมข้อมูลกลับมาใช้ซ้ำช่วยให้ไคลเอนต์จำนวนมากดาวน์โหลดประวัติการเปลี่ยนแปลงชุดเดียวกันของ Repository ได้โดยที่เซิร์ฟเวอร์ไม่ต้องใช้กำลังประมวลผลมากนัก แม้ตัวอย่างนี้จะแสดงเพียง Repository เดียว แต่ Pando ได้รับการออกแบบมาให้รองรับได้อีกจำนวนมาก และเมื่อมีการใช้งานเพิ่มขึ้น เราก็สามารถขยายขีดความสามารถในการส่งข้อมูลและการเจรจาแลกเปลี่ยนข้อมูลของ Git แยกจากกันได้

การมีส่วนร่วมพัฒนาโครงการ Git

การพัฒนา Pando โดยใช้โปรโตคอลและไคลเอนต์ Git เป็นพื้นฐาน ช่วยให้เราค้นพบข้อบกพร่องและมองเห็นแนวทางปรับปรุงประสิทธิภาพของ Git ให้ดียิ่งขึ้น

ตัวอย่างเช่น Pando ส่งไฟล์รวมข้อมูลที่สามารถนำกลับมาใช้ซ้ำให้ไคลเอนต์โดยตรง การดาวน์โหลดและจัดทำดัชนีของไฟล์เหล่านี้จึงมีความสำคัญอย่างยิ่งต่อประสิทธิภาพและความน่าเชื่อถือของระบบ ระหว่างพัฒนา เราพบปัญหาในกรณีเฉพาะต่างๆ ทั้งการดาวน์โหลดหลายไฟล์พร้อมกัน การจัดการข้อมูลที่ค้างอยู่เมื่อการถ่ายโอนถูกขัดจังหวะ และการสั่งดาวน์โหลดต่อทั้งที่ดาวน์โหลดเสร็จแล้ว เราจึงแก้ไขปัญหาเหล่านี้ เพิ่มขั้นตอนตรวจสอบความถูกต้องเมื่อต้องนำไฟล์รวมข้อมูลที่มีอยู่แล้วมาใช้ในการดึงข้อมูล และส่งการปรับปรุงดังกล่าวกลับไปยังโครงการ Git ต้นทาง

เรายังต้องการทราบด้วยว่าระบบใช้เวลาและแบนด์วิดท์ไปกับขั้นตอนใดบ้าง จึงเพิ่มการติดตามขั้นตอนการดาวน์โหลดผ่าน packfile-uri และการนับจำนวนไบต์ที่ใช้ในการสร้างไฟล์รวมข้อมูล เพื่อให้ตรวจสอบได้ง่ายขึ้นว่าการดึงข้อมูลที่ล่าช้านั้นเกิดจากการสร้างไฟล์รวมข้อมูลหรือการถ่ายโอนไฟล์

Git ของ OpenAI⁠(เปิดในหน้าต่างใหม่) ช่วยให้ไคลเอนต์ดาวน์โหลดและจัดทำดัชนีไฟล์รวมข้อมูลที่ไม่ต้องพึ่งพากันหลายไฟล์ได้พร้อมกัน นอกจากนี้ เรายังเพิ่มแนวคิด “explicit haves” เพื่อให้เซิร์ฟเวอร์แจ้งคอมมิตล่าสุดของสาขาแยกจากประวัติที่ไคลเอนต์สามารถละเว้นได้เมื่อสร้างไฟล์รวมข้อมูลสำหรับส่งการเปลี่ยนแปลง เมื่อใช้กับ Pando กลไกนี้ช่วยให้ไคลเอนต์ใช้คำสั่ง --force-with-lease ตรวจสอบว่าสาขาฟีเจอร์ยังไม่มีการเปลี่ยนแปลง และสร้างไฟล์รวมข้อมูลโดยอ้างอิงเฉพาะประวัติของสาขาหลักได้ จากนั้นไคลเอนต์รายใดก็ตามที่มีประวัติสาขาหลักดังกล่าวอยู่แล้ว ก็สามารถดึงข้อมูลของสาขาฟีเจอร์ได้โดยใช้ไฟล์รวมข้อมูลเดียวกัน

ปัจจุบันเราได้ส่งต่อการปรับปรุงหลายรายการจนได้รับการรวมเข้าโครงการ Git ต้นทางแล้ว และยังเผยแพร่ผลงานอีกหลายรายการไว้ใน Repository สาธารณะของ OpenAI เราจะเดินหน้าทำงานร่วมกับชุมชน Git ต่อไป เพื่อให้สิ่งที่พัฒนาขึ้นไม่ได้เป็นประโยชน์เฉพาะกับ Pando แต่สามารถนำไปใช้กับ Git ในวงกว้างได้ด้วย โดยสามารถติดตามความคืบหน้าได้ผ่านระบบติดตามการแก้ไขโค้ดสาธารณะของเรา⁠(เปิดในหน้าต่างใหม่)

ก้าวต่อไป

แทนที่จะยึดติดกับวิธีขยายระบบ Git แบบเดิม เราเลือกคิดใหม่และพัฒนา Pando ให้รองรับ Monorepo ที่มีขนาดใหญ่ขึ้น โดยวิศวกรและเอเจนต์ยังคงทำงานกับ Git ได้ตามปกติ สำหรับ Monorepo ของ OpenAI นั้น Pando ช่วยให้เราทำงานหลายส่วนควบคู่กันได้มากขึ้น รวมโค้ดได้อย่างน่าเชื่อถือยิ่งขึ้น และเผยแพร่การเปลี่ยนแปลงโค้ดได้เร็วกว่าเดิม เรายังมองหาโอกาสนำ Pando ไปใช้กับงานด้านอื่นๆ ภายใน OpenAI โดยเฉพาะงานที่ต้องอาศัยเอเจนต์จำนวนมากร่วมกันพัฒนาโค้ดใน Git Repository เดียวกัน

เมื่อวิศวกรและเอเจนต์พัฒนาซอฟต์แวร์มากขึ้น ทีมพัฒนานอก OpenAI ก็มีแนวโน้มที่จะเผชิญข้อจำกัดของระบบควบคุมเวอร์ชันโค้ดเช่นเดียวกับที่เราเคยพบ ขณะนี้เราจึงกำลังศึกษาวิธีที่จะเปิดโอกาสให้ผู้ใช้ได้เข้าถึงเทคโนโลยีเบื้องหลัง Pando

Pando ต่อยอดจากประสบการณ์ที่ผู้ใช้และผู้ดูแลระบบ Git Repository สั่งสมมาตลอดกว่า 20 ปี เราจะเดินหน้าพัฒนา Pando ต่อไป พร้อมร่วมมือกับชุมชนนักพัฒนา Git แบบโอเพนซอร์ส เพื่อแบ่งปันการปรับปรุงต่างๆ ที่จะเป็นประโยชน์ต่อผู้ใช้ Git ในวงกว้าง

ผู้เขียน

OpenAI