Привет Хабр! Предлагаю вашему вниманию перевод статьи Git Virtual File System Design History. Продолжение следует…

Виртуальная файловая система Git (Git Virtual File System, далее GVFS) была создана для решения двух основных задач:

  • Скачивать только файлы необходимые пользователю
  • Локальные команды Git должны брать в расчет не всю рабочую директорию (working directory), а только файлы, с которыми работает пользователь

В нашем случае основной сценарий использования GVFS — это репозиторий Windows с его 3 миллионами файлов в рабочей директории в сумме занимающих 270 Гбайт. Чтобы клонировать этот репозиторий вам придется скачать упаковочный файл (packfile) размером в 100 Гбайт, что займет несколько часов. Если вам все же удалось его клонировать, все локальные команды git вроде checkout (3 часа), status (8 минут) и commit (30 минут) выполнялись бы слишком долго из-за линейной зависимости от количества файлов. Несмотря на все эти сложности мы решили мигрировать весь код Windows в git. В то же время мы старались оставить git практически нетронутым, так как популярность git и количество общедоступной информации о нем были одними из основных причин для миграции.

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

Предыстория


Почему монолитный репозиторий?


Разберемся сразу с самым простым вопросом: зачем вообще кому-то нужен репозиторий таких размеров? Просто ограничьте размер ваших репозиториев и все будет в порядке! Правильно?
Не все так просто. Уже написано много статей о преимуществах монолитных репозиториев. Несколько больших команд в Microsoft уже пробовали разбивать свой код на множество маленьких репозиториев и в результате склонились к тому, что монолитный репозиторий лучше.

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

VSTS (Visual Studio Team System)


Набор инструментов VSTS состоит из нескольких связанных сервисов. Поэтому мы решили что разместив каждый из них в отдельном репозитории git мы сразу избавимся от проблемы масштабирования, и одновременно создадим физические границы между различными частями кода. На практике же эти границы ни к чему хорошему не привели.

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

Во-вторых, значительно усложнился наш процесс релиза. Параллельно с релизом новой версии VSTS каждые три недели мы выпускаем коробочную версию TeamFoundation Server каждые три месяца. Для корректной работы TFS необходима установка всех сервисов VSTS на одном компьютере, то есть все сервисы должны понимать от каких версий других сервисов они зависят. Собирать воедино сервисы, которые в течение трех последних месяцев разрабатывались совершенно независимо оказалось непростой задачей.

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

Windows


Примерно по этим же причинам команда, работающая над Windows, решила перейти на Git. Код Windows состоит из нескольких компонент, которые теоретически могли бы быть разбиты на несколько репозиториев. Однако у этого подхода были две проблемы. Во-первых, несмотря на то что большинство репозиториев были небольшие, для одного из репозиторев (OneCore), который занимал около 100 Гбайт, нам все равно пришлось бы решать проблему масштабируемости. Во-вторых, такой подход ни коим образом не облегчил бы внесение изменений в несколько репозиториев одновременно.

Философия дизайна


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

Рассмотренные альтернативные варианты


Итак за последние несколько лет мы потратили много времени пытаясь заставить Git работать работать с большими репозиториями. Перечислим некоторые из рассмотренных нами вариантов решения этой проблемы.

Подмодули Git


Сначала мы попробовали использовать подмодули. Git позволяет указать (reference) любой репозиторий как часть другого репозитория, что позволяет для каждого коммита в родительском репозитории указать коммиты в под-репозиториях от которых этот родительский коммит зависит и где именно в рабочей директории родителя эти коммиты должны быть размещены. Выглядит как идеальное решение для разбивки большого репозитория на несколько маленьких. И мы потратили несколько месяцев работая над утилитой командной строки для работы с подмодулями.

Основной сценарий применения подмодулей — это использование кода одного репозитория в другом. В каком-то роде подмодули — это те же самые пакеты npm и NuGet, т.е. библиотека или компонент, который не зависит от родителя. Любые изменения вносятся в первую очередь на уровне подмодуля (в конце концов это независимая библиотека со своим независимым процессом разработки, тестирования и релиза), а затем родительский репозиторий переключается на использование этой новой версии.

Мы решили, что могли бы воспользоваться этой идеей разбив наш большой репозиторий на несколько маленьких, а затем склеив их обратно в один супер-репозиторий. Мы даже создали утилиту, которая бы позволяла выполнять «git status» и коммитить код поверх всех репозиториев одновременно.

В конце концов мы забросили эту идею. Во-первых, стало понятно, что таким образом мы только усложняем жизнь разработчика: каждый коммит теперь стал двумя или более коммитами одновременно, так как необходимо было обновить как родительский, так и каждый из затрагиваемых подмодульных репозиториев. Во-вторых, в Git нет атомарного механизма для выполнения коммитов одновременно в нескольких репозиториях. Можно было бы конечно назначить один из серверов ответственным за транзакционность коммитов, но в итоге все опять упирается в проблему масштабируемости. И в-третьих, большинство разработчиков не хотят быть экспертами в системах контроля версий, они бы предпочли, чтобы доступные инструменты делали это за них. Для работы с git разработчику необходимо научиться работать с направленным ациклическим графом (DAG, Directed Acyclic Graph), что уже непросто, а тут мы просим его работать с несколькими слабосвязанными направленными ациклическими графами одновременно и к тому же следить за порядком выполнения checkout/commit/push в них. Это уже слишком.

Несколько репозиториев собранных воедино


Если не получилось с подмодулями, то может быть получится с несколькими репозиториями склеенными вместе? Подобный подход был применен андроидом в repo.py и мы тоже решили попробовать. Но ничего хорошего из этого не вышло. Работать в рамках одного репозитория стало проще, зато намного усложнился процесс внесения изменений в несколько репозиториев одновременно. И так как коммиты в разных репозиториях теперь совершенно не связаны друг с другом, непонятно какие коммиты из разных репозиториев должны быть выбраны для определенной версии продукта. Для этого понадобилась бы еще одна система контроля версий поверх Git.

Запасные хранилища (alternates) Git


В Git существует понятие запасных хранилищ объектов (alternate object store). Каждый раз когда git ищет коммит, дерево или блоб он начинает поиск c папки .git\objects, затем проверяет pack-файлы в папке .git\objects\pack, и наконец, если указано в настройках git-а, ищет в запасных хранилищах объектов.

Мы решили попробовать использовать сетевые папки в качестве запасных хранилищ чтобы избежать копирование огромного количества блобов с сервера при каждом clone и fetch. Такой подход более-менее решил проблему количества копируемых файлов из репозитория, но не проблему размера рабочей директории и индекса.

Еще одна неудачная попытка использования функционала Git не по назначению. Запасные хранилища были созданы в Git для избежания повторного клонирования объектов. При вторичном клонировании того же репозитория можно использовать хранилище объектов первого клона. Предполагается, что все объекты и pack-файлы находятся локально, доступ к ним моментален, нет надобности в дополнительном кэше. К сожалению, это не работает если запасное хранилище находится на другой машине в сети.

Поверхностное клонирование (shallow clones)


В Git существует возможность ограничить количество клонируемых коммитов. К сожалению, этого ограничения недостаточно для работы с огромными репозиториями вроде Windows, так как каждый коммит вместе со своими деревьями и блобами занимает до 80 Гбайт. Тем более, что в большинстве случаев для нормальной работы нам не нужно содержимое коммитов целиком.
К тому же поверхностное клонирование не решает проблемы большого количества файлов в рабочей директории.

Частичный (sparse) чекаут


При чекауте Git по умолчанию помещает все файлы из этого коммита в вашу рабочую директорию. Однако в файле .git\info\sparse-checkout можно ограничить список файлов и папок которые могут быть помещены в рабочую директорию. Мы возлагали большие надежды на этот подход поскольку большинство девелоперов работаю лишь с небольшим подмножеством файлов. Как оказалось, у частичных есть свои недостатки:

  • Действие частичных чекаутов не распространяется на индекс, только на рабочую директорию. Если даже вы ограничите размер рабочей директории до 50 тысячи файлов, индекс будет все равно включать все 3 миллиона файлов;

  • Частичный чекаут статичен. Если вы включили директорию А, а кто-нибудь добавил позже зависимость на директорию B, ваш билд будет сломан до тех пор, пока вы не включите B в список директорий для частичного чекаута;

  • Часчтичный чекаут не распространяется на файлы, скачиваемые при операциях clone и fetch, так что если даже ваши чекауты не имеют никакого отношения к 95% файлов вам все равно придется их скачать;

  • UX (User experience) частчиных чекаутов неудобен в использовании

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

Хранилище для больших файлов (LFS, Large File Storage)


Каждый раз, когда мы изменяем файл больших размеров, в истории изменений Git создается его копия. Для экономии места Git-LFS подменяет эти большие блоб-файлы на их указатели, сами файлы при этом помещаются в отдельное хранилище. Таким образом, при клонировании вы скачиваете только указатели на файлы, а затем LFS скачивает только файлы, которые вы чекаутите.

Было непросто заставить LFS работать с репозиторием Windows. В итоге нам это удалось, что позволило существенно уменьшить общий размер репозитория. Но мы так и не решили проблему большого количества файлов и размера индекса. Пришлось отказаться и от этого подхода.

Виртуальная файловая система (Virtual file system)


Вот к каким выводам мы пришли после всех вышеперечисленных экспериментов:

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

В итоге мы решили сосредоточиться на идее виртуальной файловой системы, основные преимущества которой включают:

  • Скачивание только необходимого минимума блоб-файлов. Для большинства разработчиков это означает порядка 50-100 тысяч файлов и их истории изменений. Большая же часть репозитория так и не будет никогда скопирована.
  • С небольшими ухищрениями можно заставить git брать в расчет только файлы, с которыми работает девелопер. Таким образом, операции вроде git status и gitcheckout будут выполняться гораздо быстрее, чем если бы они выполнялись на всех 3 миллионах файлов
  • С помощью частичных чекаутов мы можем размещать только используемые фалы в рабочую директорию. И что более важно, каждый чекаут будет ограничен только файлами необходимыми разработчику
  • Все используемые нами инструменты разработки продолжат работать без изменений, файловая система позаботится о том, чтобы запрашиваемые файлы были всегда доступны

Однако и без трудностей, конечно, не обойдется:

  • Разработка файловой системы дело непростое
  • Производительность должна быть на высоте. Нам могут простить небольшую задержку при первом доступе к файлу, но повторный доступ к этому же файлу должен быть практически моментальным
  • Задержки в доступе к файлам могут и должны быть уменьшены с помощью предзагрузки. Как правило, большое количество мелких объектов привносит наибольшие задержки. Примером может служить огромное количество очень маленьких объектов-деревьев git
  • Нам еще предстоит понять, как заставить git работать с виртуальной файловой системой. Как избежать обхода git-ом всех 3.5 миллионов файлов если для git это выглядит так будто все эти файлы действительно находятся на диске? Хорошо ли работают команды git с учетом настроек частичных чекаутов? Возможно ли такое что git обойдет слишком много блоб-файлов?

В следующей статье мы обсудим как эти проблемы были решены в GVFS.
Поделиться с друзьями
-->

Комментарии (20)


  1. maydjin
    02.06.2017 12:28
    +1

    Боюсь вас расстраивать, но такое имя (gvfs) уже занято пруф


    1. Azoh
      02.06.2017 12:44
      -1

      Боюсь вас расстраивать, но это перевод, а название придумал кто-то в недрах MS


      1. maydjin
        02.06.2017 12:49
        +3

        :) Ну, это в их духе.


        Про перевод не увидел, потому что нет тэга.


        1. kafeman
          02.06.2017 18:28

          Gnome довольно успешно отстаивает свои права на название. Хотя не думаю, что gvfs — это чей-то торговый знак.


  1. NaHCO3
    02.06.2017 13:07

    Интересно, как эту проблему решают под линуксом?


    1. maydjin
      02.06.2017 14:20

      То же ядро линукс достаточно толстое, что бы просто пойти и выкачать его на 3g канале, но всегда можно склонировать с нужной глубиной.


      Имхо, такие вещи решаются никак не написанием костылей.
      В git есть штатная подсистема для декомпозиции репозитория, я не верю, что кодовую базу которая, видимо, размером не менее 500mb нельзя разбить на независимые компоненты.


      1. knstqq
        02.06.2017 15:24

        > каждый коммит вместе со своими деревьями и блобами занимает до 80 Гбайт.
        не просто более 500mb, а до 80 Гбайт со служебной информацией о коммите.

        Есть проблема с репозиториями, если кода достаточно много и разные компании решают эту проблему по разному (Google, Yandex, Facebook, Microsoft). Ядро линукса — не такой и большой проект.
        В Yandex когда-то давно SVN был, теперь Mercurial, в Google свой велосипед, который можно чек-аутить как Git — как пишут в интернетах.


        1. maydjin
          04.06.2017 12:25

          Вот дело как раз в том, что товарищи не хотят следовать принципу разделяй и властвуй(о чём, как раз и заявляют). Заместо этого, как обычно следуют принципу embrace, extend and extinguish, напрягая своими телодвижениями как минимум два сообщества за раз:) Сейчас эта поделка взлетит у windows only людей, а потом они захотят портировать решение. А как? А git г-но, там же нет gvfs из коробки и вообще под этим вашим линуксом какая то хрень в пакете gvfs лежит, наша монструозная кучка испражнений даже на ci будет хрен пойми как работать.


          Ну это мой взгляд на проблему. На самом деле, это open source и кто как хочет, тот так и развивает свои форки.


          При этом, проблема с git это, имхо, только симптом — раз есть такой монолитный и огромный кодебейз, это сколько же людей в ms понимают его хотя бы на компонентном уровне?


          1. NaHCO3
            04.06.2017 16:28

            > При этом, проблема с git это, имхо, только симптом — раз есть такой монолитный и огромный кодебейз

            Монолитный кодбейз — это норма. Между программами всегда есть связи. И если весь этот софт на компоненты смежности, то выяснится, что там всего одна. А дальше возникает вопрос, как с этим бороться.

            Можно взять и разбить кодбейс на компоненты сильной связности, распихать их по разным git репозиториям. Потом ещё написать интеллектуальный сборщик, который будет их подтягивать. Потом интеллектуальную систему тестирования и автоматического формирования тестовых контейнеров. Потом интеллектуальный issue tracker и code review tool, которые способны работать с несколькими git репозиториями одновременно.

            Т.е. для работы со всеми проектами одновременно, и со всему их связями, явными и неявными, всё равно нужна единая система. Можно написать и внедрить такую систему. Можно сделать как майкрософт — дополнить функциональность git чтобы он мог работать с единым большим репозиторием, и переиспользовать уже существующие инструменты для разработки с git. Но нельзя закрыть глаза и представить, что проблема исчезнет, если о ней перестать думать.

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


            1. maydjin
              04.06.2017 22:25

              Gitlab/stash/bitbucket/github уже всё это умеют :) Я специально поставил в начало списка то, что чаще всего используют для закрытых проектов.


              man git-submodule


              Ещё и версионированные компоненты получаются. Т.е. можно держать например саппортную ветку где компоненты потухлее а бизнесс логика пилится например потихоньку под нужды 2-х с половиной жирных клиентов. Очень просто откатить бажный компонент к стабильной ветке естественным путём.


              1. NaHCO3
                05.06.2017 20:12

                Далеко не всё они умеют. Именно поэтому майкрософту пришлось писать свои костыли для работы с git-submodule. Но костыли со временем показывают свою несостоятельность — их надо постоянно расширять для совместимости с другими решениями, к тому же развивающимися по независимым направлениям.

                Гит сабмодуль не может быть удобным для всех — иначе люди не предпочитали бы пользоваться пакетными менеджерами вроде maven и gems. Опять же, я пробовал использовать git для ведения вики — очень неудобно, т.к. различные страницы должны иметь больше независимости, чем им даёт гит.


                1. maydjin
                  06.06.2017 00:08

                  пробовал использовать git для ведения вики

                  Я тоже пробовал на велосипеде с зилом гоняться, есть даже пару мест, где обогнать смог.


                  git таки scm, а не пакетный менеджер и не wiki-движок


                  По теме: статья про то, что надо использовать не сабмодули а огромный репо, в который штатный механизм гит производит коммит около часа, т.к. обьём имеющейся файловой системы есть ~280Gb. Т.е. если у вас огромный кодебейз, над 1 процентом которого (не забываем это сорцы) физически нереально работать одному разработчику, то нет, не надо его декомпозировать, давайте лучше напишем костыль, что бы нивелировать совместимость с и все достоинства исходного решения, а потом, промоем неофитам мозги в сми, что бы они возлюбили щупальца.


  1. exchg
    02.06.2017 14:15
    +2

    Для работы с git разработчику необходимо научиться работать с направленным ациклическим графом (DAG, Directed Acyclic Graph), что уже непросто

    Серьезно? Понять ациклический граф сложно?


    Знающие люди объясните мне пожалуйста, зачем они взяли существующую децентрализованную систему и приделали к ней костыль, чтобы она стала по сути централизованной? Я не могу понять.


    1. petropavel
      04.06.2017 00:16

      Серьезно? Понять ациклический граф сложно?


      Как ни странно. Мы несколько лет назад перешли на git, но до сих пор иногда приходится «ну, представь, вся история — это один большой граф, а бранчи — стрелочки на отдельные коммиты»



      Вот xkcd это очень точно подметил. Очень многие просто зазубривают десяток команд — и это 99% всех потребностей покрывает.


      1. exchg
        04.06.2017 21:10

        Да, есть такое, некоторые просто заучивают серии команд, ну или на крайний случай машинально запоминают куда, что и в какой последовательности нажать в GUI. А когда-то программисты смеялись над секретаршами )))


        Но меня удивило именно про понимание графа. А извините меня, деревья это частный случай графа. Т.е. судя по написанному — программисты которые работают в Microsoft и пользуются Git с трудом понимают как работают деревья в том числе. Или я уже что-то проспал и деревья стали продвинутой структурой данных?


        1. taujavarob
          05.06.2017 14:35

          Но меня удивило именно про понимание графа. А извините меня, деревья это частный случай графа. Т.е. судя по написанному — программисты которые работают в Microsoft и пользуются Git с трудом понимают как работают деревья в том числе. Или я уже что-то проспал и деревья стали продвинутой структурой данных?

          HTML есть частный случай XTML.
          JSX есть частный случай XTML.

          А вот XTML в чистом виде похоже не нужен никому.


          1. exchg
            05.06.2017 15:36

            Т.е. деревья разработчикам не нужны? Но все равно, я в общем то говорил о понимании, а не о нужности ненужности.


  1. EvgenT
    02.06.2017 15:57

    команд в Микрософт
    Вот, а они себя называют Майкрософт.


    1. antonvildyaev
      03.06.2017 14:58

      переименовал в Microsoft шоб уж наверняка :)


  1. heleo
    05.06.2017 11:08

    И в-третьих, большинство разработчиков не хотят быть экспертами в системах контроля версий, они бы предпочли, чтобы доступные инструменты делали это за них. Для работы с git разработчику необходимо научиться работать с направленным ациклическим графом (DAG, Directed Acyclic Graph), что уже непросто, а тут мы просим его работать с несколькими слабосвязанными направленными ациклическими графами одновременно и к тому же следить за порядком выполнения checkout/commit/push в них. Это уже слишком.

    Тут получается разработчик достаточно грамотный что бы код писать, но не достаточно грамотный что бы уметь его хранить.