Как однажды была чуть не сорвана разработка Gardenscapes

    Disclaimer: Эта история произошла несколько лет назад. Но кажется, что она и до сих пор не утратила актуальности.


    … Мы разрабатывали Gardenscapes. В нём всё ещё оставались следы старого Gardenscapes под Windows. Он даже был не Match-3, а Hidden Object. И никто даже и представить не мог высот, которых достигнет игра.

    И вот в один прекрасный день…

    Как всё начиналось


    При обращении к репозиторию мы увидели следующее сообщение:

    «This repository has been disabled. Access to this repository has been disabled by GitHub staff due to excessive use of resources, in violation of our Terms of Service. Please contact support to restore access to this repository. Read here to learn more about decreasing the size of your repository.»

    Как вы уже поняли, мы используем github в качестве хостинга репозиториев git. И вот, внезапно и без объявления войны, github заблокировал наш репозиторий за превышение максимально допустимого размера. Точная цифра у них на сайте не была приведена. На момент блокировки размер папки .git был равен примерно 25 Гб. (Примечание 2020: сейчас лимиты стали побольше, и на сайте github явно указано, что размер репозитория не должен превышать 100 Гб).

    Как же мы сумели сделать такой большой репозиторий? Причина понятна: мы храним в нём бинарные файлы. Везде написано, что так делать не рекомендуется, но нам так гораздо проще. Мы хотим, чтобы игра запускалась из репозитория сразу, без дополнительных усилий. Поэтому мы коммитим в репозиторий графику и другие игровые ресурсы.

    Но это ещё полбеды. Важный урок, который мы извлекли из всей этой истории: никогда никому не рассказывай о Бойцовском клубе нельзя коммитить в репозиторий бинарные часто меняющиеся файлы. А мы так делали: коммитили исполняемый файл и текстурные атласы. Теперь мы очень сильно поумнели, и у нас есть Teamcity, который может скомпилировать бинарник и собрать атласы, плюс специальные скрипты, которые скачивают всё это добро пользователю. Но это совсем другая история. А для совсем больших файлов мы используем Git LFS, Google Drive и другие блага цивилизации.

    Борьба за историю


    Итак, ни у кого ничего не работает. Мы сказали команде, что день придётся поработать локально, но не очень стараться, а то потом конфликты разгребать (все очень расстроились и сразу ушли пить чай). И стали думать, что делать. Понятно, что нужен новый репозиторий, но что туда закоммитить? Простой способ — текущее состояние всех веток. Но нам так не понравилось, потому что будет утрачена история изменений, всеми любимая команда git blame сломается, и всё пойдёт кувырком. Поэтому мы решили сделать так: стереть историю бинарных файлов, а историю текстовых файлов сохранить.


    Шаг 1. Удаляем историю бинарных файлов


    У нас была полная локальная копия репозитория. Первым делом мы нашли отличную утилиту BFG Repo-Cleaner. Она очень простая и одновременно очень быстрая, и название хорошее.

    Примерный сценарий выполнения:

    java -jar bfg.jar bfg --delete-files *.{pvrtc,webp,png,jpeg,fla,swl,swf,pbi,bin,mask,ods,ogv,ogg,ttf,mp4} path_to_repository
    

    В параметрах — все расширения бинарных файлов, которые мы смогли придумать. Из всех на свете коммитов удалятся сведения о файлах с этими расширениями. Утилита умная и при удалении истории файла оставляет его самую последнюю версию. Вдобавок эта последняя версия будет включена в самый последний коммит ветки. Мы хотели ещё удалить историю exe- и dll-файлов, но утилита выдавала ошибку. Видимо, по каким-то причинам обработка в виде *.exe запрещена. При этом если явно указать файл, например, gardenscapes.exe, то всё работает. (Примечание 2020: возможно, баг уже исправлен).

    Шаг 2. Сжимаем репозиторий


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

    Чтобы файлы удалились физически, нужно выполнить команду git gc, а именно:

    git reflog expire --expire=now --all
    

     и потом:

    git gc --prune=now --aggressive
    

    Именно такая последовательность команд рекомендована автором утилиты. Вот gc действительно долго работает. Кроме того, при настройках репозитория по умолчанию клиенту git не хватает памяти, чтобы завершить операцию, и нужны некоторые пляски с бубном. (Примечание 2020: тогда у нас была 32-битная версия git. Скорее всего, в 64-битной версии этих проблем уже нет).

    Шаг 3. Записываем коммиты в новый репозиторий


    Это оказалось самой интересной частью квеста. 

    Чтобы понимать дальнейшее, нужно представлять, как работает git. Подробнее почитать про git можно много где, включая наш блог:

    1. Git: советы новичкам – часть 1
    2. Git: советы новичкам – часть 2
    3. Git: советы новичкам – часть 3

    Итак, у нас локально есть очень-очень много коммитов, эти коммиты правильные, то есть без истории бинарных файлов. Казалось бы, достаточно выполнить git push и всё отработает само. Но нет!

    Если выполнить просто команду git push -u master, то git бодро начинает процесс загрузки данных на сервер, но вылетает с ошибкой примерно на отметке в 2 Гб. Значит, так много коммитов за 1 раз залить не получится. Будем кушать слона по частям. Мы прикинули, что 2 000 коммитов наверняка поместятся в 2 Гб. Общий объём нашего репозитория тогда составлял примерно 20 000 коммитов, распределённых между 4 ветками: master-v101-v102-v103. (Примечание 2020: эх, юность! С тех пор всё стало куда серьёзнее. Коммитов в этом репозитории уже более 100 000, а релизных веток — несколько десятков. При этом мы всё ещё укладываемся в ограничения Github )

    Первым делом считаем число коммитов в ветках при помощи команды:

    git rev-list --count <branch-name>
    

    К примеру, в ветке master оказалось примерно 10 000 коммитов. Теперь мы можем воспользоваться расширенным синтаксисом команды git push, а именно:

    git push -u origin HEAD~8000:refs/origin/master
    

    HEAD~8000:refs/origin/master — это так называемый refspec. Левая часть говорит, что нужно взять коммиты вплоть до коммита, отстоящего от HEAD на 8 000, то есть как раз примерно 2 000 коммитов. А правая часть — что нужно их запушить в удалённую ветку master. Здесь нужен именно полный путь к ветке refs/origin/master.

    После этого ветки master пока ещё нет, и, например, git fetch скачать её не сможет. Что неудивительно — ведь коммита, который бы указывал на её HEAD, ещё не существует. Тем не менее, повторив команду git push HEAD~8000:refs/origin/master, мы увидели ответ, что эти коммиты на сервере уже есть, и, значит, работа всё-таки выполнена.

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

    git push origin HEAD~6000:refs/origin/master
    git push origin HEAD~5000:refs/origin/master
    git push origin HEAD~4000:refs/origin/master
    git push origin HEAD~3000:refs/origin/master
    git push origin HEAD~2000:refs/origin/master
    git push origin HEAD~1000:refs/origin/master
    git push origin HEAD~10:refs/origin/master
    git push origin master
     
    git checkout v101
     
    git push -u origin HEAD~1000:refs/origin/v101
    git push origin HEAD~10:refs/origin/v101
    git push origin v101
     
    git checkout v102
    … и т.д.
    

    То есть мы последовательно записываем на сервер все наши ветки, по 2 000 коммитов за один push, и последние 10 коммитов отдельно.

    Времени вся эта история заняла немало, и часы показывали уже ближе к 12 ночи. Так что скрипт мы оставили работать на ночь, произнесли полагающиеся молитвы Ктулху (Примечание 2020: тогда он был ещё относительно популярен) и пошли по домам. 

    Финал. Хэппи-энд


    Утром, открыв репозиторий на сайте github, мы убедились, что скрипт отработал успешно и все коммиты и ветки на месте.

    В итоге: размер репозитория (папка .git) уменьшен с 25 Гб до 7.5 Гб. При этом вся важная история коммитов — всё, кроме бинарных файлов — сохранена. Гейм-дизайнеры выпили больше чая, чем обычно. Программисты получили незабываемый экспириенс. И срочно начали думать, а как бы так сделать, чтобы не надо было коммитить исполняемый файл в репозиторий, но работать при этом было бы удобно.
    Playrix
    Разработчик мобильных free-to-play игр

    Похожие публикации

    Комментарии 46

      +15
      Ставить под угрозу разработку крупного проекта путём отдачи репозитория сторонней компании, которая может всё без предупреждения отключить только потому, что левая пятка зачесалась — бесценно, чёрт побери.
      Мне кажется, главный урок, который следовало/следует усвоить из этой истории — никогда не отдавать ничего критичного неконтролируемым тобой людям.
      И свой репозиторий на своём сервере уж точно обошёлся бы дешевле, чем день простоя команды.
        +1
        Максимально несовременная точка зрения в эпоху, когда «все крутится на Амазоне».

        А свои репозитории на своих серверах за все эти годы точно обошлись бы сильно дороже, чем день простоя. Притом, что свои сервера совсем не гарантируют отсутствие простоев.
          +1
          Ну да. Тогда как Ваша «максимально современная» точка зрения привела к дню простоя, причём неконтролируемого и это стало поводом для статьи на хабре.

          Ну банально — разбирательства с гитхабом могли затянуться на неопределённый срок. Здесь на Хабре достаточно примеров, как гитхаб блокировал репозитории просто потому что «они так решили». А ещё у них может случиться любая авария, утечка данных или они просто могут закрыться.
          При этом шансов отсудить у гитхаба фактическую стоимость простоя бизнеса, в общем-то, не существует. Это же касается и Амазона «на котором всё крутится» и много кого ещё.

          При этом имеется вполне достаточно локальных инструментов — тот же Gitlab, Phabricator, Gitea и т.д., с которыми вы не будете ограничены ни смехотворными 100гб на репозиторий, сможете делать бэкапы по любой удобной вам схеме, можете держать хоть кластер dev-серверов.
          А также вы сможете в любой момент проверить фактическую работоспособность этих бэкапов, а в случае сбоя — у вас будут ваши люди, которые будут чинить этот сбой, а не безликий баннер на сайте «у нас технические проблемы, но мы уже знаем и наши инженеры работают над этим».
            +2

            Кто мешает сделать зеркало на свои сервера?
            GitHub берет на себя инфраструктуру и обслуживание, а бекапы никто не отменял.

              +5
              А смысл? git устроен так, что по сути у каждого разработчика на компьютере полный бекап. Хостингов миллион, не понравился один — переедем на другой.

              Все же сейчас облака — это стандарт де факто, строить свои датацентры желающих мало.

              Ну и 100Гб на репозиторий — это вообще то много, git на такое на самом деле не особо рассчитан. То есть мы объективно работали неправильно, и этот случай помог нам пересмотреть свои подходы.
              +1
              В том то и дело, все полагаются на облачные технологии, на их надёжность и забывают, что они, по сути, просто большое количество серверов, которые тоже могу ломаться.

              Я, например, один из рабочих продуктов, разметил в Gilab, Github, Bitbucket и локальном репозитории. Это даёт мне возможность работать с продуктом из разных мест. Я просто читаю все репозитории и пишу тоже во все. Если один из ресурсов недоступен, то на него изменения прилетят в следующий раз. Во всех местах хранятся все ветки и все коммиты. В любой момент можно залить на очередной репозиторий и в нём окажется все, что есть в остальных.

              На счёт бинарных данных, хорошо заметили. В проекте они тоже есть, но обновляются крайне редко. Хотя, урок для себя из статьи я вынес по данному моменту.
                +1
                Не знаете, к чему придраться — назовите противную точку зрения «несовременной». Я пришёл к диаметрально противоположному выводу: всё перетаскивать «на Амазон» в конечном итоге дороже, да и возиться с кучей поставщиков услуг надоедает — везде свои заморочки. А нужно многое: файловое хранилище, репозитории, билд-машина, хранилище документации, баг-трекер, какие-то собственные инструменты в виде веб-приложений, которые нужно расшарить на команду.

                Поставил простой обычный NAS от Synology, держу всё это дело там и вообще ни о каких «планах», ограничениях, подписках и прочей ерунде не думаю.
                  +1
                  Всю инфраструктуру для сотен разработчиков из десятков городов на NAS? И туда же еще и production с миллионами игроков давайте закинем. Нет уж, мы лучше повозимся с поставщиками.

                  Да, когда-то и у нас было что-то подобное. Просто для разных масштабов нужны разные решения.
                    +1
                    Аргумент вида «соломенное чучело»: поздравляю, achievement unlocked. Я не предлагал вам засовывать на NAS базу данных всех игроков.

                    У меня NAS для разработчиков, а для пользователей, разумеется, обычная production база данных в дата-центре. А вы попробуйте покритиковать исходную идею. Да, NAS для разработчиков из десятков городов. А в чём, собственно, проблема? Назовите хотя бы две штуки.
                      0
                      Я не буду ввязываться в дискуссию. Мне достаточно аргумента про «бутылочное горлышко». Отключили интернет или (надолго) электричество там, где стоит NAS — и все встало. Можно, конечно, ставить генераторы и резервные каналы связи. Только нужно ли? Мы хотим доступности всех сервисов для разработки на том же уровне, что и production для игроков. И не хотим заморачиваться с обеспечением этого уровня сами. Если вы хотите — ваше решение, каждому свое.

                      И при любых обстоятельствах, все наше добро на один NAS ну вот никак не поместится.
                        +1
                        Я не буду ввязываться в дискуссию. Мне достаточно аргумента про «бутылочное горлышко».

                        Разумеется. Ведь достаточно обозвать оппонента «ретроградом», а потом ещё и приписать ему то, что он не говорил.

                        Можно, конечно, ставить генераторы и резервные каналы связи. Только нужно ли?

                        Ну, ваша статья ровно об этом: «отключили интернет или электричество» или «закрыли доступ к репозиторию» — одного уровня проблемы. А они случаются и на вашей стороне, на стороне поставщиков — это всё неизбежно, и сложно сказать, что чаще бывает.

                        И при любых обстоятельствах, все наше добро на один NAS ну вот никак не поместится.

                        Да, пожалуй, ваши объёмы превосходят мои фантазии. Ну что ж.
                          0

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

                            0
                            Это же «что-то» должно вам подсказывать, что при наших масштабах бедствия «пара серверов» — это самое начало истории. У нас были сервера, их было гораздо больше, чем пара. Постепенно выпиливаем. В какой-то момент роста облака становятся и проще, и дешевле, и доступнее.

                            Серверная конечна по определению. И есть 3 бесконечные вещи — Вселенная, человеческая глупость, Амазон (С).
                              0
                              На следующем витке вы считаете экономику амазона и начинаете задумываться, не пора ли строить свой дата-центр :). Пока годовой счет от амазона меньше зарплаты 10-ти инженеров — туда даже смотреть не нужно.
                                0
                                Ну и кстати облако облаку рознь. Вы можете там инстансы гонять и по сути это будет почти своя инфраструктура, за которую вы «всего лишь» раза в два переплачиваете, в обмен на гибкость и отсутствие головняка с физическим масштабированием. А можете платить за «сервис» — когда в расчете на гигабайт/гигабит/гигафлопс вы переплачиваете раз в 20, но еще с вас снимают головняк по логическому масштабированию, безопасности и обеспечивают какой-то уровень SLA. В конечном итоге все сводится к тому, кто заплатит инженерам за чертову магию :)
                                  0
                                  Мы делаем и так и так. Иногда «гоняем инстансы», иногда сервис под ключ.

                                  Низконагруженное выгоднее брать под ключ. Там получится «раз в 20 дороже» — $100 вместо $5, но зато все из коробки.

                                  Высоконагруженное обычно лучше запилить свое, получится и дешевле, и можем сделать более кастомно. Там еще очень распространенный кейс: пользователи проснулись, надо в 3 раза больше серверов. Уснули — в 3 раза меньше. Или какой-то экшен в игре, то же самое. В Амазоне мы можем так сделать, у себя пришлось бы держать запас под максимальную нагрузку.
                  +4
                  git же. «Отдача» на чужой сервер ничего особо не меняет – это просто +1 точка, где лежат исходники. И просто восстановить работу можно было очень быстро – разослав письмо с адресом локального репозитория и инструкцией по переключению.
                    +1
                    Так тем более непонятно, почему это стало такой проблемой. Или они настолько слепо верили в непогрешимость облаков, что даже не озаботились поддержкой локального зеркала?
                      +2
                      А что о ней заботиться? У каждого разработчика своё локальное зеркало есть, залить на общедоступный сервер недолго.

                      Подождём ответа от s_shestakov, думаю, что просто решили воспользоваться моментом и привести репу в порядок.
                        0
                        думаю, что просто решили воспользоваться моментом и привести репу в порядок.


                        Возможно :)

                        В этом случае ещё более доставляет заголовок статьи «была чуть не сорвана разработка Gardenscapes». Скандалы, интриги, расследования :)
                        А по факту просто мануал «как вычистить г… но из репы».
                          0
                          Так да ;) Понятно, что угрозы потери данных не было, git же. А заголовок — нарочно, чтобы завлечь любопытных. Теперь модно же так, а-ля желтая пресса.
                            +4
                            Сделайте игру про разработку игры. С событиями типа «репозиторий заблокирован, что делать? Поднять локальный или вычистить мусор?»
                              0
                              Каждый день в нее играем. Особенно увлекательное событие: «У нас баг, что делать? Добавить костылек или делаем рефакторинг?»
                                0
                                Ну вот не в виде летсплея, как здесь, выложить, а с эффектом погружения :-)
                                  +2
                                  Так приходите к нам на работу;) Еще и денег заплатят за успешную игру. Эффект погружения гарантирую.
                        0

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

                    +12

                    Я понимаю, что история про гит, но ваша реклама на ютубе задолбала.


                    а) Не интересно.
                    б) Содержит сцены насилия с криками и т.д.
                    в) Раздражает.

                      –5
                      (тем спорная, так что официально: это моя личная точка зрения, а не точка зрения компании)

                      Мы делаем подобную рекламу не потому, что любим сцены насилия и прочие ужасы. Мы делаем ее потому, что она работает лучше, чем «обычная». То есть для среднестатистического игрока больше вероятность установить игру после «плохой» рекламы, чем после «хорошей». Почему — мы не знаем. Возможно, с человечеством что-то не так :). Вы — видимо, не среднестатистический, так что извините за доставленные неудобства.

                      Я вообще рассматриваю это как перевоспитание. Человек приходит в игру, думая что там треш. А там на тебе, милый Остин восстанавливает сад, и все вокруг его друзья. И человек, сам того не замечая, становится добрее.
                        +11

                        А ещё эта реклама показывается в детских видео (включая колыбельные и т.д., в том числе в середине колыбельной). Если вас когда-нибудь забанят, знайте, где-то есть человек, который тщательно помечает его как inappropriate, потому что оно inappropriate.

                          0
                          Не защищая автора, просто обратите внимание на баннерорезки. Читая ужасы о рекламе, я в недоумении, понимаю, что далек от этого. На телефоне кстати снесены приложения ютуба, и он прекрасно работает через браузер.
                            0
                            Youtube Premium меньше 4 баксов в месяц стоит. Не паришься о рекламе, плюс фоновый режим на мобилках работает.
                            –3
                            Очевидно, что дети, которые смотрят колыбельные — не наша целевая аудитория. Мы сами не хотим показывать им эту рекламу, это бессмысленно. Тут вопрос скорее к алгоритмам YouTube. Ваши inappropriate помогают им настроить алгоритмы, так что здесь мы с вами солидарны.
                              +2

                              А поставить галку "не показывать в детских видео" не судьба?

                                0
                                Была бы — поставили бы. Я же говорю, это и в наших интересах тоже — показывать рекламу только тем, кому она подходит. Таргетинг мобильной рекламы устроен не так просто. А ребята из гугла считают, что им виднее, какие крутилки нужны. Мы иногда пробуем их переубедить, но, как говорится, где гугл — а где мы.
                                  0
                                  Друзья, ничего нового. Типичная «компания для людей» со свиным рылом капитализма. Расходимся.
                              0
                              Мсье Густав, это вы?
                            +2
                            Полностью поддерживаю. Более того, она даже не соответствует реальной игре. В сторе полно отзывов об этом обмане. Ребенок тоже повелся, установили и в итоге там оказалось совсем другое. Эта реклама повсюду, и достала многих. Вы бы постыдились в открытую писать название этой игры здесь :)
                              0
                              Ну игра на самом деле неплохая, но… Мне у них не нравятся две вещи — вот эта вот дурацкая реклама (раньше была лучше и давала адекватное представление об игре) и то, что они ввели премиум-пасс. Я, конечно, понимаю, что постоянный доход это хорошо, но не по цене же сравнимой с крупными MMORPG). В общем момент введения премиум-пасса стал моментом, когда я удалил эту игру, пусть и с сожалением.
                                0
                                Есть ещё адекватные регуляторы.
                                0
                                В рекламе ещё и чей-то чужой геймплей показан. За что такие игры и получают свою заслуженную звёздочку, но рекламщики, похоже, только увеличивают бюджет.
                                0
                                А зачем делать git gc в этом сценарии? Разве без него не зальется и так?
                                  +1

                                  Я предполагаю, что это была попытка оценить итоговый размер репозитория после всех этих операций.


                                  С другой стороны, после gc можно было вызвать git repack, упаковать все в один или несколько пакетов и потом только вызывать git push, это могло уменьшить количество передаваемых данных и возможно позволило бы запушить без разделения на несколько посылок, хотя не факт и над тем, как разбивать коммиты на эти пакеты пришлось бы отдельно думать.

                                    0
                                    Да, вы правы. Без git gc видимо залилось бы так же. Но мы должны были убедиться, что мы действительно удалили лишнюю историю.
                                  +1
                                  Странно конечно. Помню раньше на странице самого github была статья — что если вам таки действительно надо удалить файл и чтобы на 146%, то это можно сделать только через их техподдержку
                                    +1
                                    Да, удалить файл насовсем сложно.

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

                                      Мне кажется, проще сразу карту блокировать, кто знает, куда всё попало.

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

                                  Самое читаемое