Перейти к основному содержимому
Перейти к основному содержимому

Запись запросов в ClickHouse с использованием данных GitHub

Этот набор данных содержит все коммиты и изменения для репозитория ClickHouse. Его можно сгенерировать с помощью встроенного инструмента git-import, распространяемого с ClickHouse.

Сгенерированные данные предоставляют файл tsv для каждой из следующих таблиц:

  • commits - коммиты со статистикой.
  • file_changes - файлы, измененные в каждом коммите, с информацией об изменениях и статистикой.
  • line_changes - каждая измененная строка в каждом измененном файле в каждом коммите с полной информацией о строке и информации о предыдущем изменении этой строки.

На 8 ноября 2022 года каждый TSV имеет примерно следующий размер и количество строк:

  • commits - 7.8М - 266,051 строк
  • file_changes - 53М - 266,051 строк
  • line_changes - 2.7Г - 7,535,157 строк

Генерация данных

Это необязательно. Мы свободно распространяем данные - смотрите Скачивание и вставка данных.

Это займет около 3 минут (на 8 ноября 2022 года на MacBook Pro 2021) для завершения для репозитория ClickHouse.

Полный список доступных опций можно получить из встроенной помощи инструмента.

Эта помощь также предоставляет DDL для каждой из вышеуказанных таблиц, например:

Эти запросы должны работать в любом репозитории. Не стесняйтесь исследовать и сообщать о своих находках. Некоторые рекомендации относительно времени выполнения (на ноябрь 2022 года):

  • Linux - ~/clickhouse git-import - 160 минут

Скачивание и вставка данных

Следующие данные можно использовать для воспроизведения рабочей среды. Кроме того, этот набор данных доступен на play.clickhouse.com - см. Запросы для получения дополнительной информации.

Сгенерированные файлы для следующих репозиториев можно найти ниже:

Для вставки этих данных подготовьте базу данных, выполнив следующие запросы:

Вставьте данные, используя INSERT INTO SELECT и функцию s3. Например, ниже мы вставляем файлы ClickHouse в каждую из их соответствующих таблиц:

commits

file_changes

line_changes

Запросы

Инструмент предлагает несколько запросов через свой вывод помощи. Мы ответили на них, а также на некоторые дополнительные интересные вопросы. Эти запросы имеют примерно возрастающую сложность по сравнению со случайным порядком инструмента.

Этот набор данных доступен на play.clickhouse.com в базах данных git_clickhouse. Мы предоставляем ссылку на эту среду для всех запросов, адаптируя имя базы данных по мере необходимости. Обратите внимание, что результаты в play могут отличаться от представленных здесь из-за различий во времени сбора данных.

История одного файла

Самый простой запрос. Здесь мы смотрим на все сообщения коммитов для StorageReplicatedMergeTree.cpp. Поскольку они, вероятно, более интересны, мы сортируем по самым недавним сообщениям в первую очередь.

play

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

play

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

Найти текущие активные файлы

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

Обратите внимание, что, похоже, была повреждена история коммитов в отношении файлов в каталогах dbms, libs, tests/testflows/ во время их переименований. Поэтому мы также исключаем их.

play

Обратите внимание, что это позволяет файлам быть переименованными, а затем переименованными обратно в свои оригинальные значения. Сначала мы агрегируем old_path для списка файлов, удаленных в результате переименования. Мы объединяем это с последней операцией для каждого path. Наконец, мы фильтруем этот список, чтобы исключить те, где финальное событие не является Delete.

play

Обратите внимание, что мы пропустили импорт нескольких каталогов во время импорта, т.е.

--skip-paths 'generated\.cpp|^(contrib|docs?|website|libs/(libcityhash|liblz4|libdivide|libvectorclass|libdouble-conversion|libcpuid|libzstd|libfarmhash|libmetrohash|libpoco|libwidechar_width))/'

Применяя этот шаблон к git list-files, мы получаем 18155.

Наше текущее решение, таким образом, является оценкой текущих файлов.

Разница здесь вызвана несколькими факторами:

  • Переименование может произойти одновременно с другими модификациями файла. Эти события перечислены как отдельные события в file_changes, но с одинаковым временем. Функция argMax не может их различать - она выбирает первое значение. Естественный порядок вставок (единственный способ узнать правильный порядок) не сохраняется при объединении, поэтому могут быть выбраны измененные события. Например, ниже файл src/Functions/geometryFromColumn.h имеет несколько модификаций перед тем, как быть переименованным в src/Functions/geometryConverters.h. Наше текущее решение может выбрать событие Modify как последнее изменение, что приводит к тому, что src/Functions/geometryFromColumn.h сохраняется.

play

  • Поврежденная история коммитов - отсутствуют события удаления. Источник и причина TBD.

Эти различия не должны существенно повлиять на наш анализ. Мы приветствуем улучшенные версии этого запроса.

Список файлов с наибольшим количеством модификаций

Ограничивая текущими файлами, мы считаем количество модификаций как сумму удалений и добавлений.

play

В какой день недели обычно происходят коммиты?

play

Это имеет смысл с некоторым снижением продуктивности по пятницам. Здорово видеть, как люди коммитят код в выходные! Огромное спасибо нашим контрибьюторам!

История подкаталога/файла - количество строк, коммитов и контрибьюторов с течением времени

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

play

Эти данные хорошо визуализируются. Ниже мы используем Superset.

Для добавленных и удаленных строк:

Для коммитов и авторов:

Список файлов с максимальным количеством авторов

Ограничено только текущими файлами.

play

Самые старые строки кода в репозитории

Ограничено только текущими файлами.

play

Файлы с самой длинной историей

Ограничено только текущими файлами.

play

Наша основная структура данных, Merge Tree, очевидно, находится под постоянной эволюцией с долгой историей изменений!

Распределение участников в отношении документации и кода за месяц

Во время захвата данных изменения в папке docs/ были отфильтрованы из-за очень запятнанной истории коммитов. Результаты этого запроса, следовательно, не точны.

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

play

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

Авторы с наибольшим распределением влияния

Мы рассматриваем разнообразие здесь как количество уникальных файлов, к которым автор внес вклад.

play

Посмотрим, у кого самые разнообразные коммиты в их недавней работе. Вместо ограничения по дате, мы ограничим последние N коммитов автора (в данном случае, мы использовали 3, но вы можете изменить):

play

Любимые файлы автора

Здесь мы выбираем нашего основателя Alexey Milovidov и ограничиваем наш анализ текущими файлами.

play

Это имеет смысл, поскольку Алексей отвечает за поддержку Change log. Но что если мы используем базовое имя файла, чтобы определить его популярные файлы - это позволяет учитывать переименования и должно сосредоточиться на вкладках кода.

play

Это, возможно, более точно отражает его области интересов.

Крупнейшие файлы с наименьшим количеством авторов

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

Чтобы оценить, предположим, что мы ограничиваемся текущими файлами, мы суммируем добавления строк и вычитаем удаления. Затем мы можем вычислить соотношение длины к количеству авторов.

play

Текстовые словари, возможно, не являются реалистичными, поэтому давайте ограничим рекламу только кодом через фильтр по расширению файла!

play

В этом есть некоторый уклон в сторону актуальности - более новые файлы имеют меньше возможностей для коммитов. Что если мы ограничим файлы, которым минимум 1 год?

play

Распределение коммитов и строк кода по времени; по дням недели, по авторам; для конкретных подпапок

Мы интерпретируем это как количество строк, добавленных и удаленных по дням недели. В этом случае мы сосредотачиваемся на директории Functions

play

И по времени суток,

play

Это распределение имеет смысл, учитывая, что большая часть нашей команды разработки находится в Амстердаме. Функции bar помогают нам визуализировать эти распределения:

play

Матрица авторов, показывающая, какие авторы часто переписывают код других авторов

sign = -1 указывает на удаление кода. Мы исключаем знаки препинания и вставку пустых строк.

play

Диаграмма Санки (SuperSet) позволяет визуализировать это очень наглядно. Обратите внимание, что мы увеличиваем наш LIMIT BY до 3, чтобы получить 3 лучших удаляющих код для каждого автора, чтобы улучшить разнообразие в визуализации.

Алексей явно любит удалять код других. Давайте исключим его для более сбалансированного взгляда на удаление кода.

Кто является самым высоким процентным вкладчиком по дням недели?

Если мы рассмотрим всего по количеству коммитов:

play

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

play

Это все еще немного просто и не отражает работу людей.

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

play

Распределение возраста кода по репозиторию

Мы ограничиваем анализ текущими файлами. Для краткости мы ограничиваем результаты глубиной 2 и 5 файлами на корневую папку. При необходимости скорректируйте.

play

Какой процент кода для автора был удален другими авторами?

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

play

Список файлов, которые были переписаны наибольшее количество раз

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

Это не охватывает понятие "переписывания", однако, где большая часть файла изменяется в любом коммите. Для этого требуется более сложный запрос. Если мы считаем переписыванием, когда больше 50% файла удаляется и 50% добавляется. Вы можете настроить запрос в соответствии с вашим собственным пониманием того, что представляет собой такое переписывание.

Запрос ограничен только текущими файлами. Мы перечисляем все изменения файла, группируя по пути и хешу коммита, возвращая количество добавленных и удаленных строк. Используя оконную функцию, мы оцениваем общий размер файла в любой момент времени, выполняя кумулятивную сумму и оценивая влияние любого изменения на размер файла как строки, добавленные - строки, удаленные. Используя эту статистику, мы можем рассчитать процент файла, который был добавлен или удален для каждого изменения. Наконец, мы считаем количество изменений файла, которые составляют переписывание, т.е. (percent_add >= 0.5) AND (percent_delete >= 0.5) AND current_size > 50. Обратите внимание, что мы требуем, чтобы файлы имели более 50 строк, чтобы избежать ранних вкладов в файл, которые могли бы считаться переписыванием. Это также избегает предвзятости по отношению к очень маленьким файлам, которые могут быть более вероятно переписаны.

play

В какой день недели код имеет наибольшую вероятность оставаться в репозитории?

Для этого нам нужно уникально идентифицировать строку кода. Мы оцениваем это (так как одна и та же строка может появиться несколько раз в файле) по пути и содержимому строки.

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

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

play

Файлы, отсортированные по среднему возрасту кода

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

play

Кто склонен писать больше тестов / CPP кода / комментариев?

Есть несколько способов, как мы можем рассмотреть этот вопрос. Сосредоточив внимание на соотношении кода к тестам, этот запрос относительно прост - посчитать количество взносов в папки, содержащие tests, и вычислить соотношение к общему количеству взносов.

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

play

Мы можем изобразить это распределение в виде гистограммы.

play

Большинство участников пишут больше кода, чем тестов, как и ожидалось.

Что насчет тех, кто добавляет больше всего комментариев при внесении кода?

play

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

Как меняются коммиты авторов со временем по отношению к проценту кода/комментариев?

Для вычисления этого по автору тривиально,

Тем не менее, мы хотим увидеть, как это меняется в среднем по всем авторам с первого дня, когда они начали коммитить. Уменьшают ли они со временем количество комментариев, которые они пишут?

Чтобы вычислить это, мы сначала вычисляем соотношение комментариев для каждого автора со временем - аналогично Кто склонен писать больше тестов / CPP кода / комментариев?. Затем мы присоединяем это к дате начала каждого автора, позволяя нам рассчитать соотношение комментариев по неделям.

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

play

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

Каково среднее время до переписывания кода и медиана (период полураспада кода)?

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

play

Когда наихудшее время для написания кода в том смысле, что код имеет наибольшую вероятность быть переписанным?

По аналогии с Каково среднее время до переписывания кода и медиана (период полураспада кода)? и Список файлов, которые переписывались наибольшее количество раз или большинством авторов, мы агрегируем по дню недели. Скорректируйте по мере необходимости, например, по месяцу года.

play

Код каких авторов наименее подвержен изменениям?

Мы определяем "липкий" как то, насколько долго код автора остается прежним, прежде чем его переписывают. Аналогично предыдущему вопросу Каково среднее время до переписывания кода и медиана (период полураспада кода)? - используя тот же критерий для переписываний, т.е. 50% добавлений и 50% удалений из файла. Мы вычисляем среднее время переписывания по авторам и учитываем только тех авторов, которые внесли изменения в более чем два файла.

play

Наибольшее количество подряд идущих дней коммитов автором

Этот запрос сначала требует от нас рассчитать дни, когда автор сделал коммиты. Используя оконную функцию, разбивая по авторам, мы можем вычислить дни между их коммитами. Для каждого коммита, если время с последнего коммита составляет 1 день, мы помечаем его как последовательное (1), а в противном случае - 0, сохраняя этот результат в consecutive_day.

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

play

История коммитов файла построчно

Файлы могут быть переименованы. Когда это происходит, мы получаем событие переименования, где столбец path установлен на новый путь файла, а old_path представляет собой предыдущее местоположение, например:

play

Это затрудняет просмотр всей истории файла, так как у нас нет единого значения, соединяющего все изменения строк или файлов.

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

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

Вызывая file_path_history('src/Storages/StorageReplicatedMergeTree.cpp'), мы рекурсивно проходим через историю переименований, причем каждая функция вызывает следующий уровень с old_path. Результаты объединяются с помощью arrayConcat.

Например,

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

Нерешенные вопросы

Git blame

Это особенно трудно получить точный результат из-за неспособности в настоящее время сохранять состояние в массивных функциях. Это станет возможным с arrayFold или arrayReduce, которые позволяют удерживать состояние на каждой итерации.

Приблизительное решение, достаточное для высокоуровневого анализа, может выглядеть следующим образом:

Мы приветствуем точные и улучшенные решения здесь.