Як нове сховище Git у GitHub обробляє 7200 push-ів за секунду?
GitHub замінив шар зберігання, що лежить в основі кожного репозиторію на платформі. Нова система обслуговує тисячі записів у секунду, зберігаючи затримку читання стабільно низькою.
Минулого року GitHub переніс 99,99999 відсотків репозиторій на Azure Blob Storage. Та міграція стала мостом до нової архітектури.
Внутрішні тести показали 5000 одночасних агентів, які робили 7200 push-ів за секунду до одного репозиторію. Читаючий трафік масштабувався паралельно із постійно низькою затримкою. Компанія очікує, що постійні швидкості запису витіснять сьогоднішні короткі спайки вже протягом року.
Два принципи визначали кожен компонент
Мінімізувати координацію. Розділити сховище від обчислень. Об'єкти Git незмінні та іменуються своїм вмістом. Два автори можуть зберігати об'єкти одночасно, не домовляючись про нічого. Консенсус потрібен лише для оновлення посилання — вказівника, що каже «гілка main тепер вказує на коміт abc123». Це оновлення дрібне порівняно з даними паку.
Стара архітектура зв'язувала зберігання даних та обслуговування запитів у одному місці з підтримкою репозиторій. Новий дизайн розділяє ці відповідальності, щоб кожна масштабувалася незалежно. Розробники все ще пушать код і роблять fetch. Створення гілок працює так само. Захисти гілок та інші механізми безпеки залишилися на місці.
Віртуальні репліки витіснили фіксовані набори
Spokes продовжує керувати маршрутизацією та балансуванням навантаження. Внутрішнім API, яким користується решта GitHub, теж керує Spokes. Під Spokes флот віртуальних реплік обслуговує читання та запис, або обидва режими. Локальний диск віртуальної репліки — це кеш, який можна перебудувати в будь-який момент.
Сервіс спрямовує трафік репозиторію до тих самих віртуальних реплік, коли це можливо. Теплі кеші дають низьку затримку. Коректність не залежить від конкретної репліки. Коли одна зникає, інші продовжують працювати. Замінник наповнює свій кеш по мірі надходження трафіку. Якщо репозиторій стає гарячим, додаються ще віртуальні репліки.
Довговічний стан живе в двох місцях. Об'єкти Git — паки та індекси, а також біти досяжності — лежать у Azure Blob Storage. Мульти-пак-індекси та кешовані відповіді fetch також там. Посилання та маніфест паків знаходяться в окремому реплікованому лог-сервісі під назвою Ref-CAS.
Ref-CAS упорядковує оновлення посилань, не тримаючи об'єкти
Кожне оновлення посилання в Ref-CAS — це compare-and-swap, зафіксований кворумом. Транзакція блокує лише ті посилання, які торкається, перевіряє, чи вони ще мають очікувані значення, і стає видимою, коли більшість реплік журналу її записала. Агенти переважно пушать у власні гілки, тому більшість оновлень не конфліктують і реплікуються паралельно.
Кожен коміт отримує номер послідовності журналу. Версія репозиторію — це LSN його останнього коміту. Читання починається з перевірки версії. Віртуальна репліка надсилає версію, яку вже тримає. Якщо вона актуальна, відповідь не містить посилань взагалі. Незмінний репозиторій коштує одне порівняння.
Якщо репліка відстає, вона отримує поточні посилання та нову версію, а також вказівники на дані паків, які потрібні. Ref-CAS ніколи не тримає і не валідує дані об'єктів. Віртуальна репліка завантажує пак push-у до Blob Storage до коміту транзакції посилань. Посилання ніколи не вказує на об'єкти, які ще не стали довговічними.
Цей дизайн дає десятки тисяч операцій з посиланнями за секунду. Він також створює базу для анотацій, що допомагають читанням вирішити, що завантажити для конкретного запиту.
Компактація йде на виділених вузлах, а не на обслуговуючих репліках
Push додає пак до Blob Storage. Git може обслуговувати репозиторій з багатьох паків, але кожен додатковий пак трохи ускладнює пошук. Кількість паків не може рости безмежно.
У старій архітектурі компактація виконувалася на тих самих репліках, що обслуговували трафік, і повинна була запускатися на кожній. У новій архітектурі вузли компактації працюють у фоні безпосередньо з Blob Storage та Ref-CAS. Вони ніколи не запускаються на віртуальній репліці, тому процесор і пам'ять залишаються повністю для обслуговування запитів.
Вузол компактації захоплює шард через лізу в Blob Storage. Поки віртуальні репліки завантажують паки і перелічують їх у маніфесті, вузол читає маніфест, збирає нові паки в відкритий сегмент і закриває його, коли досягає заданого ліміту за кількістю паків та розміром, або віком. Потім він об'єднує паки в меншу кількість більших.
Перед публікацією вузол перевіряє, чи нові паки містять точно такі самі об'єкти, як вхідні. Він завантажує нові паки, додає їх до маніфесту, а друга транзакція видаляє вхідні. Очищення стирає їх пізніше. Хеш кожного пака відображається на шард. Додавання шардів і вузлів підвищує пропускну здатність компактації. Скомпактований пак, що все ще менший за ліміт, повертається в шард за власним хешем і знову об'єднується в ярусах, доки не досягне ліміту.
Результат — ефективно плоска кількість паків при зростанні push-ів. Збирання сміття — крок, що видаляє об'єкти, на які нічого не посилається — працює схоже з виділеними робітниками і має спеціальну поведінку для безпечного видалення.
Як виглядає графік викатки
Архітектура вже працює під GitHub, поки платформа функціонує. Репозиторії поступово підключаються до нової системи. Мета — щоб кожен репозиторій, від побічного проекту до найзавантаженішого монорепо, працював на одній архітектурі без спеціального відношення до гарячих. Компанія не публікувала дату завершення міграції.
Джерела, за якими написано статтю
- GitHub Blog
https://github.blog/engineering/architecture-optimization/inside-githubs-new-git-storage-architecture/