Делай как вGoogle
Скачать 5.77 Mb.
|
Кто и когда? Около 8 % случаев использования Code Search — это попытки получить ответы на вопросы о том, кто или когда добавил определенный фрагмент кода в VCS. Напри- мер, Code Search позволяет узнать, когда и кем была добавлена конкретная строка (подобно команде blame в Git), и перейти к соответствующему обзору кода. Панель 350 Глава 17. Code Search истории также может помочь определить, к кому лучше обратиться с вопросом о коде или с просьбой выполнить обзор изменений 1 Зачем понадобился отдельный веб-инструмент? За пределами Google большинство вышеупомянутых исследований проводится в локальной IDE. Так зачем понадобился еще один инструмент? Масштаб База кода в Google настолько велика, что локальная копия полной кодовой базы — необходимое условие для большинства IDE — просто не поместится ни на одном ком- пьютере. Но даже если удастся преодолеть этот фундаментальный барьер, создание локальных индексов поиска и перекрестных ссылок при запуске IDE на компьютере каждого разработчика неизбежно приведет к снижению скорости разработки. А без использования индексов поиск (например, с помощью grep ) может выполняться очень медленно. Использование централизованного поискового индекса предпо- лагает выполнение этой работы только один раз и заранее, что приносит пользу всем. Например, индекс для поиска по коду обновляется с каждым отправленным изменением, что обеспечивает постоянную стоимость поддержки индекса 2 В обычном веб-поиске быстро меняющиеся текущие события смешиваются с эле- ментами, меняющимися более медленно, такими как страницы Википедии. Это же свойство можно распространить на поиск в коде и сделать индексацию инкремен- тальной, снизив ее стоимость и позволив мгновенно находить изменения в кодовой базе. При отправке изменений требуется переиндексировать только затронутые файлы, что позволяет параллельно и независимо обновлять глобальный индекс. К сожалению, быстро обновить индекс перекрестных ссылок подобным способом невозможно. К нему неприменим прием инкрементального обновления, потому что любое изменение в нем потенциально может затрагивать всю кодовую базу, а на практике часто влияет на тысячи файлов. Многие (в Google почти все) двоичные файлы приходится повторно собирать 3 (или, по крайней мере, повторно анали- 1 Отметим, что с учетом частоты фиксаций изменений, выполняемых автоматизированными инструментами, снижается ценность поиска «виноватых» авторов изменений. 2 Для сравнения: модель «каждый разработчик имеет свою IDE и выполняет индексацию в своем рабочем пространстве» масштабируется почти квадратично: разработчики создают примерно постоянный объем кода в единицу времени, поэтому база кода масштабируется линейно (даже при фиксированном количестве разработчиков). Линейное количество IDE каждый раз выполняет линейно растущее количество работы — это никак нельзя назвать рецептом хорошего масштабирования. 3 Для извлечения узлов и ребер семантического графа из исходного кода Kythe ( https://www. kythe.io ) использует процесс сборки. Сначала она конструирует частичные графы пере- крестных ссылок для каждого правила сборки. Затем из частичных графов создается один глобальный граф и оптимизируется для обработки наиболее типичных запросов (перейти к определению, найти все места использования, выбрать все объявления из файла). Стои- Зачем понадобился отдельный веб-инструмент? 351 зировать), чтобы определить их полную семантическую структуру. Для ежеднев- ного (текущая частота) создания индекса перекрестных ссылок нужны огромные вычислительные ресурсы. Несоответствие между индексом поиска и ежедневным индексом перекрестных ссылок является причиной редких, но повторяющихся проблем у пользователей. Отсутствие необходимости настраивать глобальное представление кода Возможность мгновенно и эффективно просматривать всю кодовую базу позволяет с легкостью находить подходящие библиотеки и хорошие примеры. IDE, которые создают индексы в момент запуска, требуют настройки проектов или видимых объемов кода, чтобы сократить время запуска и избежать появления шума в таких инструментах, как автодополнение. Веб-интерфейс Code Search не требует настрой- ки (например, описания проекта, среды сборки) и позволяет легко и быстро находить код, что повышает эффективность разработки. Также нет опасности пропустить зависимости, поскольку, например, при обновлении API сокращается количество проблем слияния и управления версиями библиотек. Специализация Возможно, это покажется необычным, но одно из преимуществ Code Search заключа- ется в том, что он не является средой разработки, что позволяет оптимизировать его для просмотра и изучения кода, а не для редактирования, которое обычно является основной задачей IDE (это видно по сочетаниям клавиш, меню, щелчкам мышью и организации экранного пространства). Благодаря отсутствию текстового курсора каждому щелчку мышью на символе в Code Search можно придать свое значение (например, показать все места использования символа или перейти к его опреде- лению), отличное от намерения переместить текстовый курсор. Это преимущество настолько велико, что разработчики часто открывают несколько вкладок Code Search рядом с редактором. Интеграция с инструментами разработки Как основной инструмент просмотра исходного кода, Code Search является плат- формой для размещения информации о коде. Это освобождает создателей других инструментов от необходимости реализовать пользовательский интерфейс для ото- бражения результатов своей работы, которые покажет всем Code Search. Регулярно в кодовой базе Google выполняется множество разных видов анализа, и их результаты обычно отображаются в Code Search: например, Code Search выявляет и выделяет при просмотре файлов «мертвый» (неиспользуемый) во множестве язык. мость обоих этапов — извлечения и постобработки — примерно равна стоимости полной сборки. К примеру, создание индекса Kythe для Chromium занимает около шести часов в рас- пределенной среде, поэтому строить индекс на рабочих станциях разработчиков слишком дорого. Из-за таких затрат индекс Kythe строится только раз в день. 352 Глава 17. Code Search Кроме того, ссылки на исходные файлы в Code Search считаются каноническим спо- собом «адресации» и используются во многих инструментах разработчика (рис. 17.2). Например, мы привыкли, что записи в файлах журналов включают имя файла и номер строки, где находится инструкция журналирования. Средство просмотра журналов использует ссылки Code Search, чтобы связать инструкцию журналиро- вания с фактическим кодом. В зависимости от доступной информации это может быть прямая ссылка на файл определенной версии или результат простого поиска по имени файла и номеру строки. Если существует только один подходящий файл, щелчок на ссылке откроет его на строке с соответствующим номером. В противном случае будут показаны фрагменты с нужной строкой во всех найденных файлах. Рис. 17.2. Интеграция Code Search со средством просмотра журналов Аналогичным образом стековые фреймы связываются с исходным кодом в инстру- менте просмотра отчетов о сбоях или в записях журнала (рис. 17.3). В зависимости от языка программирования для создания ссылки используется поиск по имени файла или символа. Поскольку состояние репозитория, в котором находился ис- комый элемент в момент создания двоичного файла, известно, то поиск фактически можно ограничить одной версией. Ссылки долго останутся действительными, даже если соответствующий код будет изменен или удален. Наконец, практические учебные пособия Codelab и другая документация могут включать ссылки на API и примеры реализации. Такие ссылки могут создаваться поисковыми запросами, отыскивающими конкретные классы или функции, и оста- ются действительными даже после изменения структуры файла. Кроме того, самая свежая реализация в главной ветви позволяет с легкостью встраивать фрагменты кода в страницы документации (рис. 17.4), не засоряя исходный файл дополнитель- ными аннотациями. Влияние масштаба на дизайн 353 Рис. 17.3. Интеграция Code Search с инструментом просмотра отчетов о сбоях Рис. 17.4. Интеграция Code Search с документацией Открытый API Code Search имеет открытый API поиска, перекрестных ссылок и подсветки синтак- сиса. Разработчики могут использовать все эти возможности в своих инструментах без необходимости повторно реализовать их. Кроме того, для редакторов и IDE, таких как vim, emacs и IntelliJ, в Code Search есть плагины поиска и перекрестных ссылок. Эти плагины частично компенсируют невозможность индексирования кодовой базы локально и способствуют увеличению продуктивности разработчиков. Влияние масштаба на дизайн В предыдущем разделе мы рассмотрели некоторые аспекты пользовательского ин- терфейса Code Search и выяснили причины создания отдельного инструмента для просмотра кода. В следующих разделах мы подробнее обсудим проблемы его реали- 354 Глава 17. Code Search зации: масштабирование и особенности поиска и просмотра фрагментов в большой кодовой базе. Затем мы расскажем, как мы решили эти проблемы и на какие уступки нам пришлось пойти при создании Code Search. Наибольшую сложность для масштабируемого 1 поиска представляет размер ис- следуемого корпуса кода. Для поиска в небольшом репозитории, занимающем пару мегабайт, подойдет метод перебора с помощью grep . Чтобы выполнить поиск в корпусе с объемом в несколько сотен мегабайт, можно создать простой локальный индекс и ускорить поиск на порядок или даже больше. А для поиска в гигабайтах или даже терабайтах исходного кода необходимо привлекать облачные решения с не- сколькими серверами. Ценность централизованного решения растет с увеличением числа разработчиков, использующих его, и расширением кодовой базы. Задержка обработки поисковых запросов Быстрый и отзывчивый пользовательский интерфейс мы воспринимаем как само собой разумеющееся, но добиться низкой задержки поиска очень непросто. Чтобы оправдать затраченные на это усилия, их можно сравнить с временем, которое позже сэкономят пользователи. Ежедневно Code Search обрабатывает более миллиона по- исковых запросов от разработчиков Google. Если время обработки каждого запроса увеличить всего на одну секунду, то в сумме получится время, соответствующее бездействию примерно 35 инженеров каждый день. Для сравнения: создать и под- держивать серверную часть сможет группа инженеров с численностью в десять раз меньше. То есть при 100 000 запросов в день (которые посылают менее 5000 разработ- чиков) уменьшение задержки в одну секунду станет своего рода точкой окупаемости. На самом деле потеря продуктивности растет не в прямой линейной зависимости от увеличения задержки. Пользовательский интерфейс считается отзывчивым, если задержки не превышают 200 мс ( https://oreil.ly/YYH0b ). Но уже через секунду внимание разработчика часто начинает переключаться. Если пройдет еще 10 секунд, разработчик, скорее всего, полностью переключится на что-то другое, что, как при- нято считать, очень негативно влияет на продуктивность. Лучший способ удержать разработчика в продуктивном «рабочем» состоянии — обеспечить сквозную задержку менее 200 мс для всех частых операций и вложить средства в создание подходящих для этого серверных компонентов. Большая часть запросов к Code Search посылается в процессе навигации по кодо- вой базе. В лучшем случае «следующий» файл должен находиться на расстоянии одного щелчка мышью от «предыдущего» (это может быть подключаемый файл или файл с определениями символов). Но еще эффективнее, если инструмент поиска не требует указывать имя искомого файла (или символа) полностью и предлагает воз- можные варианты завершения вводимого текста запроса. Этот подход становится более актуальным с ростом кодовой базы (и дерева файлов). 1 Запросы обрабатываются независимо, поэтому большее количество серверов может обслу- жить больше пользователей. Влияние масштаба на дизайн 355 Обычный переход к конкретному файлу в другой папке или другом проекте требует от пользователя выполнить несколько действий. А при использовании механизма поиска для этого может быть достаточно нажать всего пару клавиш. Чтобы сделать поиск таким эффективным, движку поиска можно предоставить дополнительную информацию о контексте (например, о просматриваемом в данный момент файле). Контекст может ограничить поиск рамками определенного проекта или повлиять на ранжирование файлов. В пользовательском интерфейсе Code Search 1 есть воз- можность заранее определить несколько контекстов и быстро переключаться между ними по мере необходимости. Открытые в редакторах файлы неявно используются в качестве контекста для определения релевантности результатов поиска по степени близости к этим файлам. Еще одним критерием качества инструмента поиска можно считать мощность языка поисковых запросов (например, описание искомых файлов с использованием регу- лярных выражений). Мы обсудим этот вопрос в разделе, посвященном компромиссам, далее в этой главе. Задержка индексирования Часто разработчики не замечают, что индексы устарели. Их интересует только узкая область кода, и они не знают, есть ли более свежий код. Однако когда они пишут или оценивают некое изменение, отсутствие синхронизации может вызвать большую путаницу. Обычно неважно, является ли изменение небольшим исправлением, ре- зультатом рефакторинга или совершенно новым фрагментом кода — разработчики просто ожидают получить его согласованное представление. Когда разработчик пишет код, он ожидает мгновенной индексации измененного кода. Невозможность найти вновь добавленные файлы, функции или классы вызывает недовольство и нарушает нормальный рабочий процесс разработчиков, привыкших полагаться на перекрестные ссылки. Другой пример — когда удаленный код сразу исчезает из результатов поиска, это не только удобно, но и помогает учитывать но- вое состояние при последующих сеансах рефакторинга на основе операций поиска и замены. При работе с централизованной VCS разработчику может потребоваться мгновенная индексация отправленного кода, если предыдущее изменение больше не является частью набора файлов, измененного локально. С другой стороны, иногда бывает полезно вернуться назад к предыдущей версии кода. Расхождение между индексом и выполняющимся кодом во время расследо- вания инцидента может иметь особенно негативные последствия, скрыть реальные причины проблемы и обратить внимание инженера на детали, не имеющее отноше- ния к инциденту. Эта проблема усложняет использование перекрестных ссылок, потому что текущая технология построения индекса в масштабе Google занимает часы, а из-за высокой сложности хранится только одна «версия» индекса. Конечно, 1 Пользовательский интерфейс Code Search тоже имеет классическое дерево файлов, поэтому навигация по нему тоже возможна. 356 Глава 17. Code Search можно внести некоторые исправления и организовать сопоставление нового кода со старым индексом, но эту задачу еще предстоит решить. Реализация в Google Конкретная реализация Code Search в Google адаптирована к уникальным характери- стикам кодовой базы компании, и в предыдущем разделе мы изложили конструктив- ные ограничения, мешающие созданию надежного и гибкого индекса. В следующем разделе мы расскажем, как команда Code Search реализовала свой инструмент. Поисковый индекс Огромный размер базы кода в Google — это проблема для Code Search. Первое время мы использовали подход, основанный на триграммах. Затем Расс Кокс выпустил упрощенную версию этого инструмента с открытым исходным кодом ( https://github. com/google/codesearch ). В настоящее время Code Search индексирует около 1,5 Тбайт контента и обрабатывает около 200 запросов в секунду со средней задержкой на сто- роне сервера менее 50 мс и средней задержкой индексации (время между фиксацией кода и появлением его в индексе) менее 10 секунд. Давайте примерно оценим требования к ресурсам для достижения такой произво- дительности при использовании метода простого перебора на основе grep. Библио- тека RE2, которую мы используем для сопоставления регулярных выражений, об- рабатывает около 100 Мбайт/с данных, находящихся в ОЗУ. Учитывая временное окно в 50 мс, для обработки 1,5 Тбайт данных потребуется 300 000 процессорных ядер. Поскольку в большинстве случаев достаточно простого поиска подстроки, со- поставление регулярных выражений можно заменить специальным поиском строк, способным при определенных условиях обрабатывать данные со скоростью 1 Гбайт/с 1 , что уменьшает потребность в процессорных ядрах в 10 раз. До сих пор мы рассматри- вали только требования к ресурсам для обработки одного запроса в течение 50 мс. А если взять за основу 200 запросов в секунду, 10 из которых будут обрабатываться одновременно в указанном окне 50 мс, то мы вновь возвращаемся к необходимости иметь 300 000 процессорных ядер для самого простого поиска по строкам. Эта оценка не учитывает возможности прекращения поиска в момент, когда будет найдено определенное количество результатов, или существование более эффек- тивной оценки ограничений выбора файлов, а также дополнительных затрат на сетевые взаимодействия, ранжирование и параллельную работу десятков тысяч машин. Однако она достаточно точно отражает масштаб и наглядно объясняет, по- чему команда Code Search постоянно работает над совершенствованием индексации. С течением времени на смену реализации на основе триграмм пришла реализация на основе массива суффиксов, а затем и текущая реализация, основанная на раз- 1 См. https://blog.scalyr.com/2014/05/searching-20-gbsec-systems-engineering-before-algorithms и http://volnitsky.com/project/str_search Реализация в Google 357 реженных n-граммах. Это последнее решение более чем в 500 раз эффективнее метода перебора и способно обрабатывать запросы с регулярными выражениями с молниеносной скоростью. Одна из причин, по которой мы отказались от решения на основе массива суффиксов в пользу решения на основе n-грамм на основе лексем, заключалась в возможности использовать первичный стек индексации и поиска. В решении на основе массива суффиксов создание и распространение нестандартных индексов сами по себе явля- ются проблемой. Используя «стандартную» технологию, мы извлекаем выгоду из всех достижений в сфере построения обратного индекса, кодирования и обслуживания, сделанных основной командой поиска. Мгновенное индексирование — это еще одна особенность, присущая стандартным стекам поиска, и сама по себе представляет большую проблему при применении в масштабных решениях. Применение стандартных технологий — это компромисс между простотой реализа- ции и производительностью. Несмотря на то что реализация Code Search в Google основана на стандартных обратных индексах, фактическое извлечение, сопоставле- ние и ранжирование настраиваются и оптимизируются в весьма широких пределах. Без этого некоторые из продвинутых особенностей Code Search были бы просто невозможны. Для индексации истории изменений в файлах мы разработали специ- альную схему сжатия, при использовании которой индексирование полной истории увеличивало потребление ресурсов всего в 2,5 раза. Первые версии Code Search обрабатывали все данные в оперативной памяти. С уве- личением размера индекса мы переместили обратный индекс ( https://oreil.ly/OtETK ) во флеш-память. Флеш-память как минимум на порядок дешевле оперативной памяти, но время доступа к ней как минимум на два порядка дольше. То есть ин- дексы, показывающие хорошую производительность в оперативной памяти, могут не подходить для работы с флеш-памятью. Например, первоначальный индекс на основе триграмм требовал извлекать из флеш-памяти большое количество обратных индексов огромного размера. В схемах с n-граммами количество обратных индексов и их размеры можно уменьшить за счет увеличения основного индекса. Для поддержки локальных рабочих пространств (содержимое которых немного отличается от содержимого глобального репозитория) у нас есть несколько машин, выполняющих поиск простым методом перебора. Данные из рабочей области загру- жаются при первом запросе и затем синхронизируются при обнаружении изменения файлов. Когда у нас заканчивается память, мы удаляем с машин самое старое рабочее пространство. Поиск не изменявшихся документов производится с помощью индекса истории. Как следствие, поиск неявно ограничен состоянием репозитория, с которым синхронизируется рабочее пространство. |