GitHub عيدت بناء تخزين Git عشان الـ agents: ٧٬٢٠٠ push في الثانية على ريبو واحد

مشروع Rubicon: الـ packs في Azure Blob Storage، والـ references في Ref-CAS بـ compare-and-swap لكل ref، و virtual replicas الـ disk بتاعها cache. ٥٬٠٠٠ agent و٧٬٢٠٠ push/ثانية في اختبارات GitHub.

GitHub نشرت يوم ٥ نوفمبر ٢٠٢٦ على مدونتها الهندسية تفاصيل معمارية تخزين Git الجديدة، اللي أعلنت عنها في GitHub Universe. الهدف واضح: إن آلاف الـ agents يقدروا يعملوا push على نفس الريبو في نفس الوقت، من غير ما القراءة تبطّأ.

البوست كتبه Karthik Raman و Brian Celenza، وده جزء من سلسلة بينشروها عن الموضوع.

الأرقام

حسب GitHub، في اختبارات داخلية:

  • ٥٬٠٠٠ agent شغالين على ريبو واحد في نفس الوقت
  • ٧٬٢٠٠ push في الثانية على نفس الريبو
  • القراءة فضلت بـ latency منخفض وثابت

وللمقارنة: أنشط ريبو على GitHub.com النهارده بياخد عشرات الـ writes في الثانية بالكتير، وغالبًا في دفعات قصيرة. GitHub متوقعة إن الدفعات دي تبقى معدل ثابت خلال سنة، عشان الـ agents شغالين بالتوازي وطول اليوم. وكل push بيجرّ وراه قراءات: CI و deployments و agents تانيين بيعملوا fetch. عشان كده حطّوا هدف scale أكبر بمرتبتين (١٠٠ مرة) على الأقل.

الفكرة الأساسية: مين محتاج يتفق؟

المبدأين اللي GitHub بنت عليهم: قلّل التنسيق، وافصل التخزين عن الـ compute.

الملاحظة المهمة إن أغلب الـ push مش محتاج ترتيب. الـ Git objects ثابتة ومتسمية بالـ hash بتاع محتواها، فاتنين يقدروا يخزّنوا objects في نفس الوقت من غير ما يتفقوا على حاجة. الجزء الوحيد اللي محتاج اتفاق هو تحديث الـ references، وده صغير جدًا مقارنة بباقي الـ push.

وفي البوست اللي قبله، GitHub قالت إنها نقلت 99.99999% من الريبوهات لـ Azure، وخلّت الـ write quorum على مستوى الـ reference الواحد، وبدأت virtual replicas على Azure Blob Storage.

مشروع Rubicon: التصميم

بدأوا الشغل في مايو ٢٠٢٦ وسمّوه «Rubicon». الـ Spokes لسه بيعمل الـ routing والـ load balancing، لكن طبقة البيانات تحته جديدة:

  • Virtual replicas: أسطول compute قابل للاستبدال (disposable) بيخدم القراءة والكتابة. الـ disk المحلي مجرد cache يتبني تاني في أي وقت، والـ correctness مش معتمد على إن الـ request يروح لـ replica معيّن
  • Azure Blob Storage: فيه الـ packs وبيانات تسريع القراءة (زي multi-pack indexes و reachability bitmaps)
  • Ref-CAS (Reference Compare-and-Swap): خدمة replicated log مخصوصة للـ references وقايمة الـ packs (الـ pack manifest)
  • Compaction nodes: شغالة في الخلفية مباشرة على Blob Storage و Ref-CAS، مش على الـ replicas اللي بتخدم الـ traffic

الكتابة: الـ replica بيعمل index للـ pack ويرفعه على Blob Storage الأول، وبعدين يعمل transaction واحدة على Ref-CAS تضيف الـ pack للـ manifest وتحدّث الـ references. يعني الـ reference عمره ما بيشاور على objects لسه مش durable.

القراءة: الـ replica بيبعت الـ version اللي عنده لـ Ref-CAS. لو لسه هو الحالي، الرد مفيهوش references خالص، والريبو اللي ماتغيّرش بيكلّف مقارنة واحدة. لو متأخر، بياخد الـ references الجديدة ويسحب الـ packs اللي محتاجها بس.

ليه مش Blob Storage لكل حاجة؟

GitHub جرّبت تحط الـ references في Blob Storage، والنتيجة كانت مفيدة:

  • أول محاولة: write-ahead log لكل الـ references في blob واحد، وكل تحديث conditional write عليه. القراءة رخيصة، لكن الكتابة واحدة بس في كل round trip، فوقفوا عند كام مية كتابة في الثانية (GitHub كتبت mid-hundreds)
  • تاني محاولة: تقسيم الـ references على كذا segment blob. الكتابة بقت أسرع، لكن القراءة بقت لازم تشيّك كل الـ segments، وبقت بسرعة أبطأ segment. والقراءات أكتر من الكتابات بمراتب

عشان كده بنوا Ref-CAS: كل تحديث compare-and-swap بيتأكد بـ quorum، والـ transaction بتقفل الـ references اللي بتلمسها بس. والـ agents غالبًا بيعملوا push على branches خاصة بيهم، فأغلب التحديثات مش بتتعارض. ولو اتنين حدّثوا نفس الـ reference، واحد بس اللي بيتسجّل. وحسب GitHub، الطريقة دي بتسمح بـ عشرات الآلاف من عمليات الـ reference في الثانية.

الـ compaction من غير ما يأثر على الـ traffic

كل push بيضيف pack، وكتر الـ packs بيبطّأ الـ lookups. في المعمارية القديمة، الـ compaction كان بيشتغل على نفس الـ replicas اللي بتخدم الـ traffic، وعلى كل replica.

في Rubicon:

  • كل compaction node بياخد lease على shard من packs الريبو، فـ node واحد بس بيشتغل على الـ shard
  • بيجمع الـ packs الجديدة، ويدمجها في packs أقل وأكبر، ويتأكد إن الـ objects هي هي بالظبط قبل ما ينشر
  • الـ shards بتشتغل بالتوازي، فزيادة الـ nodes بتزوّد سرعة الـ compaction

والنتيجة إن عدد الـ packs بيفضل ثابت تقريبًا والـ latency ثابت حتى مع زيادة الكتابة. والـ garbage collection بيشتغل بنفس الأسلوب على workers منفصلين.

إيه اللي يفرق معاك؟

  • Git زي ما هو: نفس الـ push والـ fetch والـ branching، ونفس الحماية
  • المعمارية في private preview محدود دلوقتي، والـ rollout الأوسع أول السنة الجاية
  • GitHub بتخطط لحاجات زي: شغل الـ agents من غير clone، وatomic commit API لتعديل كذا ملف في عملية واحدة، وevent streams للتغييرات، وتحسينات في Git نفسه زي partial clone و sparse checkout

رأيي

أهم درس هنا مش Azure ولا Blob Storage. الدرس إنك تسأل: إيه الجزء الصغير اللي فعلًا محتاج ترتيب؟ وتحطّه في مكان مصمم للتنسيق، وتسيب الباقي يشتغل بالتوازي من غير اتفاق. ده نفس السؤال اللي ينفع تسأله لأي نظام عندك فيه كتابة متزامنة، سواء queue أو inventory أو حتى counters.

المصدر

  • GitHub
  • Git
  • AI Agents
  • System Design
  • Distributed Systems
  • Azure