Система бэкпорта
В этом документе описываются политика бэкпорта ClickHouse и автоматизированная система, которая её реализует.
Модель релизов
Версии ClickHouse следуют схеме YY.M.patch.build-type, где YY — двузначное обозначение года, M — месяц выпуска (без ведущего нуля), patch — номер патча в рамках ветки, build — последовательно увеличивающийся номер сборки, а type — stable или lts.
Пример: 25.3.8.23-lts — LTS-релиз за март 2025 года, патч 8, сборка 23.
Существует два типа релизов:
- Релизы Stable выходят примерно раз в месяц. Исправления выпускаются для трех последних стабильных релизов, что дает каждому релизу около трех месяцев активной поддержки.
- Релизы LTS (Long-Term Support) выходят в марте и августе каждого года. Одновременно поддерживаются две версии LTS, каждая как минимум в течение 12 месяцев.
Пользователям, запускающим рабочие нагрузки в продакшене, рекомендуется использовать либо последний стабильный релиз, либо LTS-релиз и своевременно обновляться до новых патч-версий, поскольку патч-релизы не вносят несовместимых изменений.
Политика бэкпорта
Бэкпортируются не все изменения. Цель — сохранять стабильность релизных веток, поэтому объём бэкпортов намеренно ограничен:
- Исправления, связанные с безопасностью — бэкпортируются всегда.
- Исправления критических ошибок (исключения (логические ошибки), потеря данных, неправильные результаты, проблемы с RBAC) — автоматически отбираются для бэкпорта по общим правилам; определяются по метке
pr-critical-bugfix, наличие которой приводит к автоматическому добавлениюpr-must-backport. - Исправления проблем со стабильностью и регрессий — бэкпортируются, когда риск изменения низок по сравнению с риском оставить ошибку неисправленной; определяются по метке
pr-must-backport, которую мейнтейнеры добавляют вручную. - Исправления незначительных ошибок при наличии обходного пути — как правило, не бэкпортируются, чтобы не дестабилизировать релизные ветки.
- Новые функции, улучшения, оптимизации производительности — не бэкпортируются.
Метка pr-must-backport служит для ручного переопределения и используется мейнтейнерами, чтобы пометить PR для бэкпорта. Метка pr-critical-bugfix приводит к тому, что pr-must-backport автоматически добавляется CI-хуком (см. pr_labels_and_category.py).
Эскалация конфликтов. Если при автоматическом бэкпорте не удаётся разрешить конфликты слияния, PR с cherry-pick всё равно должен быть создан и назначен автору, выполнившему слияние, а также текущим назначенным исполнителям исходного PR, чтобы человек мог разрешить конфликты и завершить бэкпорт.
Инструмент бэкпорта
Описанная выше политика бэкпорта реализована автоматизированным инструментом в tests/ci/cherry_pick.py. Инструмент работает как workflow GitHub Actions в инфраструктуре ClickHouse и покрывает все требования: обнаружение активных веток релизов, выбор PR, подходящих для бэкпорта, выполнение двухэтапной процедуры cherry-pick и бэкпорта, разрешение конфликтов, применение политики задержки и синхронизацию меток.
Долгосрочная цель — вынести эту реализацию в автономный Python-инструмент с открытым исходным кодом, который смогут использовать и другие проекты. Целевая архитектура выглядит так:
- Настраиваемый — все параметры политики (подходящие метки, окно задержки, пороги для устаревших PR, поведение при поэтапном развёртывании и т. д.) задаются в файле конфигурации, чтобы инструмент можно было адаптировать под требования бэкпорта в любом проекте без изменения кода.
- Распространяемый — упакован как самодостаточный Python wheel, устанавливаемый из PyPI, без зависимости от CI-инфраструктуры ClickHouse.
- Программируемый — предоставляет понятную объектную модель для pull request, меток и веток релизов, чтобы пользователи могли создавать собственные workflow поверх основного движка.
Тестирование
Запланированной частью автономного инструмента является отдельный набор тестов и облегчённая тестовая инфраструктура. Эта инфраструктура сможет поднимать временные репозитории GitHub (или их локальные аналоги), заранее заполненные:
- настраиваемым набором веток, представляющих линии релизов,
- pull request'ами с различными комбинациями меток бэкпорта,
- release PR с меткой
release, указывающими на ветки релизов.
Это позволит тестам проверять полный цикл автоматизации — обнаружение меток, создание веток для cherry-pick, обработку конфликтов, создание бэкпорт PR, логику назначения ответственных, пропуск раскатки и политику задержек — на реальном, но одноразовом репозитории, не затрагивая состояние продакшена. Ту же инфраструктуру можно повторно использовать для регрессионного тестирования изменений политики перед их деплоем.
Активные релизные ветки
Активная релизная ветка — это любая ветка, для которой соответствующий release PR (с меткой release) всё ещё открыт на GitHub. Автоматизация бэкпорта определяет такие ветки динамически при каждом запуске, поэтому при выпуске нового релиза или завершении жизненного цикла старого изменения конфигурации не требуются.
Релизная ветка может находиться в состоянии раскатки (если её release PR содержит метку rolling-out) в период, когда разворачивается новый релиз. Для веток в состоянии раскатки обычные бэкпорты приостанавливаются, чтобы не усложнять раскатку. Метки для конкретных версий (например, v25.3-must-backport) отменяют это ограничение и принудительно включают бэкпорт даже во время раскатки.
Реализация
Обзор
Автоматизация бэкпорта запускается ежечасно в виде workflow GitHub Actions CherryPick (.github/workflows/cherry_pick.yml), реализованного в tests/ci/cherry_pick.py. Она работает через GitHub API и локальные операции Git на самостоятельно размещённом раннере style-checker-aarch64.
Процесс состоит из двух этапов для каждой пары (исходный PR, ветка релиза):
- Создаётся PR для cherry-pick, чтобы вынести разрешение конфликтов отдельно от фактической целевой ветки слияния. Если конфликтов нет, он сливается автоматически.
- Создаётся PR бэкпорта для реальной ветки релиза, при этом изменения из cherry-pick объединяются в один commit.
Метки
Метки в исходном PR определяют, будет ли выполняться бэкпортирование и в какие ветки.
| Метка | Эффект |
|---|---|
pr-must-backport | Бэкпортирование во все активные ветки релиза (кроме веток, помеченных rolling-out) |
pr-must-backport-force | Бэкпортирование во все активные ветки релиза без учёта ограничений rolling-out |
pr-critical-bugfix | Автоматически включает pr-must-backport (через AUTO_BACKPORT в pr_labels_and_category.py) |
v{VER}-must-backport (например, v25.3-must-backport) | Бэкпортирование только в указанную ветку релиза; override пропуск по rolling-out для этой ветки |
pr-backports-created | Устанавливается ботом, когда созданы все необходимые PR для бэкпортирования; снимается, если PR с cherry-pick открывают повторно |
pr-cherrypick | Применяется к PR с cherry-pick, созданным ботом |
pr-backport | Применяется к PR для бэкпортирования, созданным ботом |
do not test | Применяется к PR с cherry-pick, чтобы для них не запускался CI |
rolling-out | Устанавливается для release PR, чтобы показать, что его ветка сейчас находится в процессе раскатки; обычное бэкпортирование её пропускает |
Именование веток и PR
Для каждого исходного номера PR N и ветки релиза release/X.Y:
- Ветка для cherry-pick:
cherrypick/release/X.Y/N - Ветка для бэкпорта:
backport/release/X.Y/N - Заголовок PR для cherry-pick:
Cherry pick #N to release/X.Y: <original title> - Заголовок PR для бэкпорта:
Backport #N to release/X.Y: <original title>
Пошаговый процесс
1. Определение активных релизов
BackportPRs.receive_release_prs запрашивает в GitHub все открытые PR с меткой release. Head ref этих PR — имена релизных веток (например, release/25.3). На их основе формируется набор меток совместимости: v25.3-must-backport и т. д.
2. Найдите PR для бэкпорта
BackportPRs.receive_prs_for_backport использует API поиска GitHub, чтобы находить PR, которые:
- имеют как минимум одну метку бэкпорта (
pr-must-backport,pr-must-backport-force,pr-critical-bugfixили метку для конкретной версии), и - не имеют метки
pr-backports-created, и - были объединены после самой ранней даты коммита, найденной в любой ветке релиза, и
- обновлялись в течение последних 90 дней (чтобы поисковый запрос оставался эффективным).
3. Обработка ветки rolling-out
Когда release PR помечен меткой rolling-out, общие метки бэкпорта (pr-must-backport, pr-critical-bugfix) не применяются к этой ветке. Бот закрывает все ранее созданные для этой ветки PR с cherry-pick или backport, добавляя поясняющий комментарий. Метка для конкретной версии (например, v25.3-must-backport) всегда переопределяет это поведение. pr-must-backport-force отключает проверку rolling-out для всех веток.
4. Этап cherry-pick (ReleaseBranch.create_cherrypick)
Для каждой пары (исходный PR, ветка релиза), для которой PR cherry-pick еще не создан:
- Переключитесь на ветку релиза и создайте от нее ветку backport (
backport/release/X.Y/N). - Выполните
git merge -s oursс первым родителем merge-коммита, чтобы создать синтетическую merge base без изменений содержимого. - Принудительно создайте ветку cherry-pick (
cherrypick/release/X.Y/N), указывающую напрямую на merge-коммит исходного PR. - Попробуйте выполнить
git merge --no-commit --no-ffветки cherry-pick в ветку backport:- Если ветка уже в актуальном состоянии, значит изменение уже присутствует в ветке релиза — отметьте как завершенное и пропустите этот шаг.
- В противном случае (с конфликтами или без) выполните reset и отправьте обе ветки.
- Создайте PR cherry-pick из
cherrypick/release/X.Y/Nвbackport/release/X.Y/Nс меткамиpr-cherrypickиdo not test. - При необходимости перенесите
pr-bugfixилиpr-critical-bugfixиз исходного PR. - На этом этапе назначенные исполнители не назначаются; их добавляют только при обнаружении конфликтов.
5. Автоматическое слияние PR cherry-pick без конфликтов
Если PR cherry-pick можно слить (то есть конфликтов нет), бот автоматически выполняет слияние через GitHub API и сразу переходит к этапу бэкпорта.
6. Этап бэкпорта (ReleaseBranch.create_backport)
После слияния PR с cherry-pick:
- Переключитесь на ветку бэкпорта и выполните
pull. - Найдите merge-base между веткой релиза и веткой бэкпорта.
- Выполните
git reset --softдо merge-base, сведя все cherry-picked коммиты в один. - Создайте коммит, указав в качестве сообщения заголовок бэкпорт PR.
- Принудительно отправьте ветку бэкпорта и откройте бэкпорт PR в фактическую ветку релиза.
- Добавьте к PR метку
pr-backport(а такжеpr-bugfix/pr-critical-bugfix, если применимо). - Назначьте PR автору исходного PR, пользователю, выполнившему слияние, и уже назначенным исполнителям (кроме аккаунтов роботов).
7. Завершение
Когда бэкпорты исходного PR созданы для всех веток релиза, бот добавляет к исходному PR метку pr-backports-created.
8. Предварительная проверка
Перед началом любой работы над PR ReleaseBranch.pre_check запускает git merge-base --is-ancestor, чтобы проверить, что коммит слияния ещё не достижим из ветки релиза. Если достижим, считается, что PR уже бэкпортирован, и он пропускается.
Обработка устаревших PR cherry-pick
Класс CherryPickPRs запускается в начале каждого ежечасного выполнения и обрабатывает два сценария:
- Осиротевшие PR cherry-pick: если для ветки релиза, связанной с PR cherry-pick, больше нет открытого release PR (то есть релиз закрыт), PR cherry-pick закрывается автоматически.
- Повторно открытые PR cherry-pick: если у исходного PR уже есть метка
pr-backports-created, но связанный с ним PR cherry-pick всё ещё открыт, меткаpr-backports-createdудаляется из исходного PR, чтобы его можно было обработать повторно.
Для PR cherry-pick, ожидающих ручного разрешения конфликтов:
- Через 3 дня без обновлений бот публикует комментарий-пинг с упоминанием назначенных исполнителей.
- Через 7 дней без обновлений бот публикует комментарий о закрытии и закрывает PR.
Разрешение конфликтов
Если при cherry-pick возникают конфликты, PR cherry-pick остаётся открытым, чтобы их мог разрешить человек. Бот назначает его автору исходного PR, пользователю, выполнившему слияние, и всем назначенным исполнителям. После разрешения конфликтов и слияния PR cherry-pick бот создаст PR бэкпорта при следующем ежечасном запуске.
Чтобы полностью отказаться от бэкпорта, закройте PR cherry-pick. Бот посчитает его намеренно пропущенным.
Чтобы заново создать неисправный PR cherry-pick с нуля:
- Удалите метку
pr-cherrypickиз PR cherry-pick. - Удалите ветку
cherrypick/.... - Удалите
pr-backports-createdиз исходного PR, если она есть.
CI для бэкпорт PR
Бэкпорт PR нацелены на релизные ветки, поэтому для них используется отдельный CI-воркфлоу (BackportPR, определённый в ci/workflows/backport_branches.py) вместо стандартного воркфлоу для pull request. Этот воркфлоу запускает репрезентативное подмножество CI: сборки ASan/UBSan и TSan, release-сборки, сборки для macOS, функциональные тесты под ASan, stress-тесты под TSan и интеграционные тесты. Он проверяет, что в бэкпорт-ветке содержится от 1 до 50 коммитов и как минимум один изменённый файл (это обеспечивается скриптом check_backport_branch.py).
Аутентификация
В этом рабочем процессе для операций git push используется SSH-ключ (ROBOT_CLICKHOUSE_SSH_KEY). Вызовы GitHub API проходят аутентификацию через get_best_robot_token, который выбирает токен с наибольшей оставшейся квотой из пула, хранящегося в SSM (/github-tokens). ROBOT_CLICKHOUSE_COMMIT_TOKEN используется на шаге checkout в workflow Actions, а не для вызовов API. Аккаунты роботов (robot-clickhouse, clickhouse-gh) исключаются при назначении ответственного лица.
Кэш API GitHub
GitHubCache (из cache_utils.py) сохраняет кэш объектов PyGithub в S3, уменьшая число вызовов API при ежечасных запусках. Кэш загружается в начале и выгружается в конце каждого запуска.
Обработка ошибок
Ошибки при обработке отдельных PR перехватываются и записываются в журнал, но не останавливают выполнение. После обработки всех PR, если возникли какие-либо ошибки, генерируется BackportException. В CI это приводит к отправке уведомления через CIBuddy в командный чат.