Як нове сховище Git на GitHub обробляє 7200 push за секунду?
GitHub створив новий спосіб зберігання репозиторіїв Git. Він дозволяє тисячам автоматизованих агентів одночасно надсилати код у один проєкт.
Чому довелося переписувати систему
Агенти змінили вигляд навантаженого репозиторію. Сьогодні найактивніші репозиторії на GitHub.com отримують максимум декілька десятків записів за секунду, зазвичай у коротких спалахів. Коли більше роботи переходить до агентів, що працюють паралельно і безперервно, ці спалахи стають постійним потоком. Кожен push спричиняє додаткові читання: CI запускається, системи розгортання тягнуть нову фіксацію, інші агенти забирають її для контексту. Це розгалуження читань створює ефект підсилення, що масштабується разом із записами. Один репозиторій на GitHub показав зростання щоденних запитів у 4 рази з березня по травень, і експоненційна траєкторія триває. Команда поставила ціль — принаймні два порядки більшого масштабу.
Віртуальні репліки та лог Ref-CAS
Нова архітектура розділяє обчислення та стійкий стан. Spokes все ще керує маршрутизацією та балансуванням навантаження. Під ним працює флотилія віртуальних реплік — витратних обчислювальних вузлів, що обслуговують читання або запис. Також вони можуть працювати в обох режимах. Локальний диск репліки — це кеш, який можна перебудувати в будь-який момент. Spokes спрямовує трафік репозиторію до тих самих віртуальних реплік, щоб кеші залишалися «теплими», але коректність не залежить від конкретної репліки. Коли одна зникає, інші продовжують працювати, а заміна наповнює свій кеш по мірі надходження трафіку. Коли репозиторій стає активнішим, додаються нові віртуальні репліки.
Стійкий стан живе в двох місцях. Git-об’єкти — паки, індекси, біти доступності, багатопакові індекси, кешовані відповіді fetch, чекпоінти — лежать у Azure Blob Storage. Посилання та маніфест паків знаходяться в реплікованій службі журналів під назвою Ref-CAS (Reference Compare-and-Swap). Ref-CAS — це невелика група консенсусу, на яку посилався попередній допис. Кожне оновлення посилання — це compare-and-swap, зафіксований кворумом. Транзакція блокує лише ті посилання, які чіпає, перевіряє їх очікувані значення і стає видимою, коли більшість реплік її записала. Агенти переважно пушать у власні гілки, тому більшість оновлень не конфліктують і реплікуються паралельно. Коли два пуші оновлюють одне посилання, лише один дописує в лог. Кожна фіксація отримує номер послідовності (LSN). Версія репозиторію — це LSN останньої фіксації.
Як запис стає видимим
Вхідний пак індексується та завантажується в Blob Storage віртуальною реплікою. Потім вона фіксує одну транзакцію до Ref-CAS, яка додає пак у маніфест і оновлює посилання. Пак уже стійкий до того, як посилання вкаже на нього. Пізніше вузол компактації об’єднає його з іншими, щоб прискорити читання. Такий дизайн дозволяє десятки тисяч операцій з посиланнями за секунду. Віртуальна репліка ніколи не перевіряє дані об’єктів всередині Ref-CAS. Вона зберігає лише вказівники.
Як читання залишається дешевим
Віртуальна репліка порівнює свою версію посилань із Ref-CAS. Якщо версія актуальна, відповідь не містить посилань взагалі. Незмінний репозиторій коштує одне порівняння. Якщо репліка відстає, вона отримує поточні посилання та версію; також вказівники на дані паків і додаткову інформацію — наприклад, час останнього циклу збирання сміття. Репліка завантажує потрібні паки з Blob Storage у свій кеш і віддає читання звідти. Читань на порядки більше за записів, тому цей швидкий шлях критичний.
Компактація працює на окремому флоті
Кожен push додає пак у Blob Storage. Git може обслуговувати репозиторій з багатьох паків, але кожен додатковий пак трохи ускладнює пошук, тому їх кількість не може рости безмежно. У старій архітектурі компактація запускалася на тих самих репліках, що обслуговують трафік, і повинна була виконуватися на кожній. У новій вона працює у фоновому режимі на окремих вузлах компактації безпосередньо над стійким сховищем, використовуючи Ref-CAS для повідомлення віртуальних реплік про зміну стану.
Вузол компактації отримує оренду на підмножину даних репозиторію, тому одночасно лише один вузол компактує шарду. Поки це відбувається, віртуальні репліки продовжують завантажувати паки і записувати їх у маніфест. Вузол читає маніфест, знаходить нові паки і збирає їх у відкритий сегмент. Коли сегмент досягає ліміту за кількістю паків або розміром, він закривається — нові паки туди не потрапляють — і об’єднується у меншу кількість більших. Вік сегмента також враховується. Паралельно наступний сегмент для того ж шарду починає накопичувати вхідні паки. Перед публікацією вузол перевіряє, що нові паки містять саме ті об’єкти, що й вхідні. Потім завантажує їх і додає до маніфесту. Друга транзакція видаляє вхідні паки з маніфесту, а прибирання стирає їх пізніше. Процес йде незалежно для багатьох шардів. Хеш пака визначає шарду, тому додавання шардів і вузлів підвищує пропускну здатність компактації. Скомплектований пак, що все ще менший за ліміт, повертається у шарду за власним хешем і знову об’єднується — паки ростуть ярусами, доки не досягнуть межі. Результат — фактично плоска кількість паків при зростанні пушів.
Збирання сміття йде тим самим шляхом
Збирання сміття видаляє об’єкти, на які нічого не посилається — наприклад, залишкові після видалених гілок та force-push. Воно працює дуже схоже на компактацію, використовуючи виділених робітників для важких обчислень. Має кілька особливостей для безпечного видалення даних. Команда каже, що ці особливості заслуговують на окремий розбір. Завдяки цим підходам вони стабільно демонструють плоску криву затримки при зростанні записів і мають плановані оптимізації для прискорення компактації та збирання сміття.
Що дає ця архітектура
Два базові принципи дозволяють кожній частині масштабуватися самостійно. Читання та запис масштабуються додаванням віртуальних реплік. Ref-CAS упорядковує кожне посилання окремо, а не весь репозиторій. Обслуговування масштабується додаванням вузлів компактації. Мета — щоб кожен репозиторій, від побічного проєкту до найбільш навантаженого монорепо, працював на одній архітектурі без спеціальної обробки для зайнятих. Репозиторії d
Джерела, за якими написано статтю
- "GitHub Blog
https://github.blog/engineering/architecture-optimization/inside-githubs-new-git-storage-architecture/"