Dev48
ЯЗЫК
  • О нас
  • Услуги
  • Индустрии
  • Технологии
  • Статьи
  • Контакты
Забронировать звонок
    Главная/Статьи/Kak my sozdali git hranilische dlya millionov proektov netlify
Dev48

© 2026 · All rights reserved.

Как мы создали Git-хранилище для миллионов проектов Netlify

Источник: Netlify

Как мы создали Git-хранилище для миллионов проектов Netlify

Источник: Netlify

Технический обзор того, как Netlify создала Netlify Source: надежное Git-хранилище на базе S3, масштабируемое кэширование, согласованность операций push и поддержка миллионов проектов.

25 сентября 2026 г.

За последние десять лет Netlify выросла из сервиса непрерывного развертывания и хостинга в более широкую платформу выполнения: функции, edge-функции, хранилища BLOB-объектов, базы данных, аутентификация, ИИ и многое другое. Однако сама разработка по-прежнему происходила где-то в другом месте: на собственном Git-хостинге пользователя. Код отправлялся туда, мы получали уведомление, собирали, развертывали и предоставляли доступ к полученному сайту. Это была честная работа.

За последние 12–18 месяцев кое-что изменилось.

Все чаще наши пользователи начали отправлять нам файлы напрямую: папку или ZIP-архив через Netlify Drop, Netlify API, Netlify MCP… или это делал ваш агент. Они также начали создавать и обновлять проекты напрямую с помощью Agent Runners.

Они стали делать это очень часто. В прошлом месяце 93% новых проектов были созданы одним из этих способов; два года назад этот показатель составлял 70%. В абсолютном выражении это миллионы новых проектов в месяц, что означает 14-кратный рост.

Теперь каждому проекту нужен Git-репозиторий.

Оказалось, что Git-хостинги выполняли много ценной и тяжелой работы. Мы не хотим становиться провайдером Git-хостинга, но мы хотим, чтобы каждый проект на Netlify получал преимущества Git.

Когда вы и агент одновременно вносите изменения, вам нужно где-то хранить текущую работу (значит… коммит в ветке?), иметь общее понимание того, с чего все началось и что изменилось (значит… родительский коммит и diff?), иметь способ перехватить управление у агента (значит… git pull?), способ передать изменения обратно агенту (значит… git push?) и способ включить вышестоящие изменения перед публикацией своих (значит… git merge или rebase?). О, и способ обеспечить работу всего этого с десятками миллионов репозиториев, с высокой производительностью, безупречной целостностью данных и масштабируемой экономикой.

…Да, это и есть Git.

Мы случайно, шаг за шагом, перестраивали Git. Наша ошибка.

Поэтому мы создали интегрированный, управляемый Netlify Git-репозиторий для каждого проекта, который попадает в Netlify без подключенного хоста (то же самое, что мы представили в прошлом месяце в беседе Даны Лоусон о будущем Git, где мы называли его внутренним именем Netlify Source). Эти репозитории используют тот же протокол Git, что и любой другой удаленный репозиторий. Наша инфраструктура сборки, Agent Runners, ваш редактор и ваш собственный агент могут работать с ними. Продолжение работы с того места, где остановился агент, должно быть таким же обычным делом, как клонирование репозитория.

В этом техническом обзоре мы углубимся в инженерную работу, которая была проделана для проектирования, масштабирования и обеспечения безопасности этой системы.

Архитектура

S3 как надежный источник истины

Сначала краткий ликбез по внутреннему устройству Git. Git хранит содержимое файлов (BLOB-объекты), списки каталогов (деревья) и коммиты как объекты, идентифицируемые хешами. Коммит указывает на дерево для своего снимка состояния и на свои родительские коммиты. Эти объекты не меняются; ветка, например main, — это небольшой указатель, который меняется. Указатели веток — это тип ссылок (refs). Git может хранить объекты по отдельности («свободные объекты») или объединять их в пакетные файлы (packfiles).

Наш Git-сервис написан на Go и запускает бинарный файл Git для локального «голого» (bare) репозитория (полная история, без рабочего каталога). Сам Git использует протокол Smart HTTP, согласовывает передачу объектов с клиентом и обрабатывает пакетные файлы.

Задача нашего сервиса — перемещение данных между S3 и этим локальным репозиторием до и после выполнения команд Git. S3 хранит надежные данные: свободные объекты и пакетные файлы в нативных форматах Git, а ссылки — в виде небольших JSON-записей. Каждый под (pod) хранит локальный кэш репозиториев на диске, удаляя наименее используемые, когда требуется место.

Для клонирования или получения данных (fetch) сервис сначала синхронизирует локальные ссылки и объекты из S3, а затем позволяет Git обслуживать их с диска. При «холодном» кэше это означает инициализацию голого репозитория и загрузку его данных. При «теплом» кэше мы все равно проверяем S3 и загружаем все недостающее. Если под исчезает, другой восстановит репозиторий из S3 при следующем запросе к этому репозиторию.

Обеспечение надежности git push

git receive-pack — это RPC-команда Git, которая принимает push. Как только она обновляет локальный репозиторий, она готова сообщить об успехе. Но под может исчезнуть до того, как эти изменения попадут в S3.

У нас есть два требования: ссылка (например, указатель ветки) никогда не должна опережать объекты, на которые она указывает, и мы никогда не должны сообщать клиенту об успехе, пока изменения не будут сохранены в надежном хранилище (S3).

Поэтому мы выполняем следующую последовательность, удерживая локальную блокировку записи (мьютекс Go) на этом репозитории:

  • Синхронизируем ссылки и объекты из S3, затем передаем полезную нагрузку запроса в git receive-pack.
  • Загружаем новые пакетные файлы и свободные объекты в S3.
  • Обновляем ссылки в S3 с помощью условной записи, только если их текущие значения совпадают с ожидаемыми.
  • Передаем ответ receive-pack из шага 1 клиенту.

Загрузки объектов могут перекрываться: объекты неизменяемы и идентифицируются по их содержимому. Ссылки изменяемы; два писателя могут не согласиться с тем, куда должна указывать ветка main.

Мьютекс координирует запросы только внутри одного пода. Во время развертывания или изменения маршрутизации два пода могут начать с того, что main указывает на коммит A, и попытаться продвинуть его к B и C. Оба могут загрузить свои объекты, но только один может переместить main из A.

Мы считываем ссылку из S3 и проверяем, что она все еще указывает на A. Это чтение также возвращает ETag — токен для проверки того, изменилась ли сохраненная запись ссылки. Это отличается от хеша коммита Git. Мы записываем новую ссылку с условием If-Match для этого ETag. S3 проверяет условие и записывает атомарно, поэтому изменение между нашим чтением и записью приводит к сбою обновления. Новая ссылка использует If-None-Match: *, чтобы потребовать, чтобы она еще не существовала.

Если условная запись отклоняется, push сообщает об ошибке синхронизации хранилища. При ошибке сохранения мы удаляем локальный кэш, чтобы следующий запрос восстановил его из S3.

Как только Git принимает изменения локально, сохранение продолжается по собственному тайм-ауту, даже если клиент отключается. Push может достичь S3 без получения подтверждения клиентом.

Удержание этого ответа может оставить соединение «тихим» на несколько минут при большом push. Клиенты и прокси склонны иметь свое мнение на этот счет. Когда клиенты поддерживают это (большинство должно поддерживать в наши дни), мы отправляем (и сбрасываем буфер!) сообщения о прогрессе Git side-band каждые десять секунд во время синхронизации с S3.

Преимущества в производительности

Одним из мгновенных преимуществ стала замена нашего собственного «rebase» для Agent Runners. Раньше мы просили агента повторно применить накопленный diff выполнения поверх исходного кода более нового развертывания. С интегрированным Git-репозиторием git merge обрабатывает обновление. Агент все еще может помочь с конфликтами, если они есть. Чистые слияния полностью пропускают сессию агента, в результате чего большинство этих операций занимают около одной секунды (и ноль токенов!) вместо нескольких минут. Публикация выполнения агента теперь (в большинстве случаев) — это просто git merge в main, за которым следует push.

Подготовка рабочего пространства исходного кода для сборок и запусков агентов занимала в среднем 0,76 с, а на уровне p95 — 2,26 с в период с 7 по 13 сентября 2026 года. Для проектов, подключенных к GitHub, этот же этап занимал 0,84 с и 2,51 с соответственно. Для относительно простого Git-сервиса, где постоянным хранилищем является S3, это достаточно близко к показателям GitHub, чтобы казаться обыденным — в этом и заключалась цель.

Git-хостинг в 2026 году

Мы создали это не для того, чтобы конкурировать с кем-либо из них. Наша цель заключалась в том, чтобы предоставить каждому проекту, попадающему на Netlify без Git-хоста, полноценный хостинг, причем незаметно. Но поскольку сейчас несколько команд решают смежные задачи, стоит сравнить ограничения.

Cursor Origin создает полноценную платформу для разработки. Их бэкенд Continuity также рассматривает объектное хранилище как постоянное, а локальные репозитории — как кэши, но они оптимизируют решение для другой задачи: высокой пропускной способности записи и конкуренции внутри одного репозитория. Они публикуют пуши через журнал упреждающей записи (write-ahead log) и пакетно обновляют свой индекс; мы же загружаем Git-объекты и обновляем каждую ссылку напрямую.

East River Source Control переосмысливает хранение для растущих репозиториев и более интенсивной параллельной разработки. Они строят свою систему на базе Jujutsu, стремясь устранить узкие места производительности для огромных монорепозиториев и выходя за рамки Git.

Code Storage от The Pierre Computer Company предлагает программируемое Git-хранилище с API, разработанными для машин, поддерживаемое кворумом реплик.

Entire объединяет распределенный Git-хостинг с сессиями агентов и обсуждениями кода.

Для сравнения, опубликованная архитектура Spokes от GitHub реплицирует пуши между файловыми серверами и требует кворума. Gitaly от GitLab хранит авторитетные репозитории в файловых системах обслуживающих узлов, в то время как Praefect координирует несколько реплик. Эти системы должны отслеживать и защищать копии репозиториев на этих узлах. Мы решили положиться на гарантии доступности, долговечности и атомарности S3 и его условной записи. Это также позволяет нам сохранять архитектуру относительно простой.

GitLab также изучает возможность такого разделения. Их предложение Scaling Git сделало бы объектное хранилище авторитетным, а узлы Gitaly — кэшами. Оно предлагает пользовательские бэкенды Git-хранилища и единый указатель манифеста для атомарной публикации согласованного снимка репозитория. Мы сохранили нативные форматы объектов Git и используем условную запись для каждой ссылки с более узкой атомарностью, описанной выше.

Что дальше

Реорганизация того, как мы храним и обрабатываем исходный код «под капотом», была лишь первым, невидимым шагом. С созданием этой основы открылся ряд потенциальных возможностей.

Чего не будет

Netlify всегда выступал за свободу выбора и композицию. Ничего не меняется и не изменится в отношении Git-хостов, которые вы можете подключить к Netlify — GitHub, GitLab, Bitbucket, Azure DevOps и, с прошлой недели, Cursor Origin. Если рабочий процесс вашей команды уже связан с одним из них, именно так вам и следует использовать Netlify.

Речь идет о том, чтобы предоставить проектам, которые попадают на Netlify без одного из этих хостов, ту же основу, а не о том, чтобы уводить кого-то с уже выбранного хоста. И поскольку это настоящий Git-репозиторий, использующий стандартный протокол, с нашей стороны нет никакой привязки. Клонируйте его, добавьте любой удаленный репозиторий, который хотите, и делайте пуш. Вы даже можете экспортировать свой код на GitHub одним кликом.

Все, что будет дальше, не станет эксклюзивом для проектов с интегрированным Git-репозиторием. Мы можем это гарантировать.

Подробнее во второй части

Нам есть еще много чем поделиться о технической архитектуре, проблемах масштабирования, безопасности и о том, как мы перенесли миллионы существующих проектов в эту систему. Ожидайте вторую часть этой серии.

← Все статьи