Girar/Parallel

Материал из ALT Linux Wiki


Эпиграф.

- С открытием технологии "Роботы" чудо "Сборка из git" в г. ALT Linux (Хакеры) УСТАРЕЛО!

- Здание "Сборочница II" в г. BaseALT (Хакеры) устарело.

- г. BaseALT (Хакеры) требуется здание "Сборочница III" для дальнейшего развития.

— Из логов одной свободной цивилизации


Abstract

Эта страница описывает предложения и алгоритмы для внедрению параллелизации в Girar.

Предисловие

Уважаемые господа!

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

Сборочница и сейчас тормозит, а с учетом скорого включения arm ее скорость еще упадет.

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


Параллельная сборочница. Введение.

Обозначения.

Tb (build time)
время, потраченное на сборку
Tt (testing time)
время, потраченное на тестирование
Tm (merge time)
время, потраченное на интеграцию в репозиторий.
ПСТП
сборочно-тестировочный процесс, запущенный параллельно.

Однопоточная сборочница.

По порядку транзакций собираем пакеты, проверяем, сливаем в репозиторий. По дизайну производительность такой сборочницы ограничена 1/(среднее(Tb+Tt)+Tm) транзакций за единицу времени. Далее ускорить ее могут только вложения в железо, дающие diminishing return на затраченные суммы.

Сборочница с параллельной сборкой и последовательной интеграцией.

Пример реализации - girar. Если у нас много потоков выполнения, сборку и тестирование (часть) можно вынести в параллельные потоки выполнения. Соответственно, к окончанию интеграции одной транзакции уже должна найтись собранная и проверенная другая транзакция, соответственно, максимальная скорость работы такой сборочницы 1/Tm.

Отсюда сразу ряд рецептов как можно ускорить наш girar -- выбросить из Tm (merge time) все лишнее.

  • Вынести все тесты в сборочные процессы.

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

  • Выбросить планировщик в отдельный процесс выполнения.

Пусть берет AWAITING и раздает сборочным потокам. а сборочно-тестировочные потоки пусть переводят транзакцию либо в статус FAILED, либо в статус BUILT(TESTED?) (добавить статус). А процесс, занятый выполнением мержа, будет брать из очереди транзакции в статусе BUILT(TESTED?) и мержить.

  • Выбросить из мержа все тесты, не связанные с поддержанием
 целостности репозитория.

Предлагаю оставить

    • (кешированный) тест на наследование
    • (кешированный) тест на символы в бинарниках
    • тест на unmets

Все это может ускорить производительность в несколько раз.

Однако все это паллатив. Из Tm (merge time) не выкинуть перегенерацию индексов и проверку на unmets, которые в зависимости от железа занимают 20-40 сек. что дает верхний теоретический потолок производительности сборочницы с последовательной интеграцией в 2000-3000 транзакций в сутки. По сравнению с текущими (500?) транзакций в сутки это хорошо тем, что и в старой схеме есть куда расти,

однако за счет смены алгоритма работы сборочницы можно добиться гораздо большего.

Параллельная сборочница.

Прототип параллельной сборочницы я реализовал для себя в autorepo-scripts.

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

В сегодняшнем girar лишние ПСТП либо простаивают, либо по нескольку раз пересобирают транзакцию, пока наконец подойдет ее очередь для интеграции. В оптимизированном рецептами выше girar при увеличении ПСТП лишние ПСТП только простаивают. Транзакции быстро пройдут фазы сборки и тестирования, но явно встанут в очередь на интеграцию.

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

Преимущество же --- полностью загруженные ПСТП и почти линейная масштабируемость на независимых транзакциях. Масксимальная скорость работы такой сборочницы уже не 1/Tm, а пропорциональна (количество ПСТП)/Tm.

К примеру, ту же мегатранзакцию на удаление python*setuptools-tests, которую наш girar обрабатывал трое суток, girar на 128 ядрах (сервер на 4 процессора по 32 ядра) обработал бы за 15 минут, а на 256 ядрах --- еще быстрее, но, естественно, не быстрее, чем сборка и тестирвание самого медленного пакета из мегатранзакции. Я уже писал, что на altair на 32 ядрах удавалось получить на прототипе скорость в 50.000 транзакций в сутки.

Вот что дает разница в алгоритмах на том же железе.

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

  • тест на наследование
  • тест на символы в бинарниках
  • тест на unmets

исключение по тесту на наследование просто, там кто первый встал, того и тапки, остальные транзакции в FAILED. с тестом на unmets и тестом на символы в бинарниках есть два пути (можно совместить). Либо генерировать в ПСТП доп. кеши по транзакциям, по которым разбирать, какие транзакции вызвали проблемы, и отправлять их на пересборку (желательно по итогам прописывать зависимости (аналог task deps) еще желательно, чтобы планировщик FAIL'ил циклические зависимости)

либо (или как fallback) искать сбойную транзакцию методом половинного деления:

Накопилось в очереди на мерж от ПСТП 128 транзакций - не получилось смержить 128 -- мержим 64, 32, 16 ... опять же метод половинного деления можно распараллелить, если под рукой много ядер. Сразу пускаем log_2(N)+1 процессов (в примере --- на мерж уйдет 8 ядер -- пытаемся смержить 128, 64, 32, 16, 8, 4, 2, 1 транзакцию, и называем следующим репозиторием самый крупный мерж из удавшихся).

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

В autoimports я с таким сталкивался раза 2 или 3 за всю жизнь. Так что можно ожидать, что очередей в incoming больше не будет, кроме последовательностей, вызванных необходимостью порядка сборки.


Часть II. Ответы на вопросы.

ответы на https://lists.altlinux.org/pipermail/devel/2018-February/203928.html


Хотел бы начать с этого как наиболее важного.

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

Хотел бы отметить, что параллельная сборочница, описанная ранее, также обладает воспроизводимым детерминизмом. И алгоритм воспроизводимого получения нового состояния репозитория из старого не настолько сложнее. Последовательность репозиториев R_i восстанавливается по начальному репозиторию R_0, последовательности транзакций T_j и последовательности операций MERGE_l( BuildTest(R_{l_1},T_{l_1}),..., BuildTest(R_{l_k},T_{l_k}))

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

А к чему приводит применение нескольких транзакций последовательно? В общем случае к тому, что результат применения отличается, в зависимости от случайного выбора сборочницы. Например, я залил транзакции 111222 и 111221. Последовательная сборочница может смержить сначала 111222, потом 111221, может сначала 111221, потом 111222. Параллельная еще добавляет 3-й вариант - 111222 и 111221 в одном мерже.

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

> Не лучше ли вместо этого (спекулятивно)
> объединять множество транзакций, готовых к применению, в полноценные
> мега-транзакции, обрабатываемые как обычные транзакции?

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

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


On Fri, Feb 16, 2018 at 04:33:23AM +0300, Dmitry V. Levin wrote:
> Предлагаю начинать разговор не с алгоритмов оптимизации сборочницы,
> а с задач, которые мы хотим решать с помощью сборочной системы
> следующего поколения.

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


к примеру

  • супербихайв: пересборка + тестирование пакетов из Сизифа,

отдача результатов в агрегатор супербихайва

  • подключение к autoimports внешней сборочницы

Часть III. Ответ на вопрос, зачем ускорять сборочницу.

Уважаемые господа,

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

В этом письме хочу рассмотреть вопрос, зачем ее надо ускорять.

Моими уважаемыми оппонентами в этом вопросе выступили Алексей и Дмитрий, которые привели аргументы 2-х классов.

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

2) не надо гнаться за производительностью, превышающей средний трафик. зачем сборочнице простаивать?

Начну разбор с 1-го класса возражений.

Более обще,

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

и более конкретно,

> Очевидно, что публиковать якобы новые сборки пакетов, в которых
> ничего не меняется, не просто не нужно, но и вредно.
[...]
> Слепо пересобирать чужие сборки из-за записи в %changelog'е вида
> - Rebuilt for https://fedoraproject.org/wiki/Fedora_NN_Mass_Rebuild
> не просто не нужно, но и вредно.

По поводу импорта, я уже много писал, не хочу повторяться. Однако, неужели в других дистрибутивах ни багов не чинят, ни пакетов не обновляют и не улучшают? В импорт только Mass_Rebuild и попадает? Да, есть недостаток индивидуального подхода к каждому кусту. Это как копать картошку трактором, по сравнению с ротой стройбата с лопатами.

Проблема в том, что нас мало, не тянем мы на роту, максимум на два взвода. Поэтому, tractor it is.

Другой критикуемый пример -- пересборка python-* без BR: python-modules-setuptools-tests. Кто-то предлагал, зачем рубить шашкой все пакеты, проще сделать хак и забыть.

Вот пример, когда-то сделал хак и забыл, а оно зажило своей жизнью и начало разрастаться по Сизифу. https://bugzilla.altlinux.org/show_bug.cgi?id=34535

Когда увидел, был в шоке! Когда понял, что это такое и зачем, облегченно вздохнул. /Да, это ужас, но не Ужас-Ужас./ Но шашкой или топором в будущем вырубить это надо.

Кто-то предлагал, давайте изменения накапливать в git-ах, а в сборочницу не отправлять. Так в этом случае крайним окажется giter - там места не хватит.

Отправлять в Сизиф, а не копить у себя - это сейчас хорошая, годная стратегия.

Это как "Вы скорбите по тем временам, когда мужчины были настоящими мужчинами и сами писали драйверы устройств?"

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

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

Что делать, такие времена, такие нравы.

Продолжу разбор по 2-му классу возражений.

2а)

> Понимаете, в чем дело.  Время всё равно уходит, а сборочица всё равно
> простаивает. И показатель несколько тысяч транзакций в сутки всё равно
> не нужен. То есть вы занимаетесь неправильной задачей оптимизации.

"Уже сейчас сборочница иногда простаивает. А если мы сборочницу оптимизируем, то она начнет простаивать гораздо чаще."

Будет простаивать --- хорошо, освободившиеся мощности можно занять под тот же супербихайв: пересборка + полное сборочностное тестирование пакетов из Сизифа. Роботы можно подключить, майнтайнеры потянутся, если сборка там будет быстрее, чем локально.

2b)

> Вряд ли мы хотим решать задачу принимать в Сизиф 60*60*24/2==43200
> транзакций в сутки (в среднем по 2 секунды на транзакцию), это число
> более чем в 2 раза превышает число исходных пакетов в нынешнем Сизифе.
> Такая задача сама по себе не выглядит осмысленной.

Как раз очень даже осмысленная задача. 43200 транзакций в сутки -- это не ожидаемый трафик, а желаемая пиковая мощность, следствием которой является повышение "отзывчивости" сборочницы, что приводит к повышению производительности труда ее пользователей.

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

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

И я понимаю сотрудников, которые в такой ситуации призывают

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

Но лучше все-таки прислушаться к тревожному звонку и обратиться к админу/сантехнику/разработчику. Заранее.


Часть IV. Рефакторинг

Уважаемые господа!

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

Повторю эти предложения.

  • Вынести все тесты в сборочные процессы.

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

  • Сделать merge явным отдельным этапом -

когда сборка закончится, выставить task'y статус BUILT(ожидает merge) Сделать merge отдельным скриптом, который можно будет запускать параллельно сборочным, который будет брать из очереди task'и в статус BUILT(ожидает merge) и последовательно мержить.

  • Выбросить из мержа все тесты, не связанные с поддержанием
 целостности репозитория.

Предлагаю оставить

  • (кешированный) тест на наследование
  • (кешированный) тест на символы в бинарниках
  • тест на unmets
> Неочевидный выбор.  По идее, все тесты, выполняемые на новом состоянии
> репозитория (после создания индексов для нового состояния репозитория) -
> это тесты на целостность репозитория.  Возьмём, например, install check.
> Если его выбросить из мержа, то мы даже не протестируем устанавливаемость
> собранных пакетов в том репозитории, в который мы их отправляем.

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

Почему именно их и почему не install check? Потому что эти тесты нам гарантируют __постоянное__ присутствие в репозитории некоторых свойств, еще и относительно дешево (за константное время). К примеру, потратив время при merge на тест на unmets, мы получим гарантию, что во всех итерациях репозитория unmets не будет.

В отличие от него, install check никаких гарантий на будущие репозитории не дает. Хочу это подчеркнуть.

Не прохождение install check -- достаточное основание, чтобы отсеять (не пропустить) пакет в репозиторий.

А прохождение install check -- не достаточное основание, чтобы гарантировать, что install check будет проходить успешно и в последующих репозиториях. Не буду приводить примеры, такие примеры легко построить всем слушателям, когда пакет A нормально устанавливается неделю, месяц, год, а потом в репозиторий попадает пакет B и ломает A. Поможет ли против пакета B (со средним ожиданием в год) повторный install check? С вероятностью 99.99999% -- не поможет. Да, формально, существует вероятность, 0.00001%, что пакет-злодей B будет отправлен в тот же день и час, при чем смержится после начала билда A но до мержа A. Но проверять это бессмысленно: даже не потому, что это в bottleneck-е, одна машина поперек дороги блокирует всю трассу, даже не потому, что 0.00001%, а потому, что слона то мы и не приметили. на случай 0.00001% -- тест есть, а на случай 99.99999% -- теста нет. Когда пакет B попадет в репозиторий, он сломает install check. (а он попадет, это же у A сломается install check, а не у B)

Вопрос, как мы узнаем об этом? Ответ - надо делать супербихайв=rebuild+check. Регулярно прогонять уже имеющиеся пакеты через install check,

Таким образом, логическая ошибка здесь:

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

собрали A с репозиторием R_1 и проверили 1-й раз. проверили A с репозиторием R_2 (куда A отправляем) отправили B в R_3. A сломался в R_3.

Помогла нам проверка A на R_2? нет, не помогла. По сути-бесполезная проверка.

install check по своей сути похож на rebuild check. Мы отправляем в Сизиф хорошие, годные, собирающиеся пакеты. А они там в Сизифе ломаются :(.

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

отсеивающие тесты надо выполнять сразу после сборки, чтобы они выполнялись параллельно и ускоряли сборочницу.

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


On Mon, Feb 19, 2018 at 11:56:58PM +0300, Leonid Krivoshein wrote:
> 19.02.2018 18:10, Igor Vlasenko пишет:
> > Предлагаю оставить
> > ** (кешированный) тест на наследование
> > ** (кешированный) тест на символы в бинарниках
>
> Здесь предлагается повторить тестирование на тех же узлах, на которых оно
> выполнялось до этого, предварительно размножив на все задействованные
> серверы промежуточные результаты? Если же нет, то что тогда означает слово
> "кэшированный"? Потому что, если тестирование перейдёт на единственный узел
> (с промежуточными) результатами, в кэше этого единственного узла ничего не
> будет.

Имелось в виду кеширование промеждуточных данных для теста. К примеру, чтобы выполнить тест на символы в бинарниках, надо распаковать rpm, пройтись по бинарникам и собрать из них зависимые символы.

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


V. Планировщик [процесс, запускающий task'и на сборку].

Я уже писал, что для повышения производительности планировщик и мержер надо разнести в полностью отдельные параллельно выполняемые независимые процессы. Пусть общаются только через смену статуса task'ов.

Однако кроме этого в текущем планировщике есть и другие недостатки.

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

С одной стороны, она не полная, т.е. если пользователь залил таски T1 T2 T3, то нет гарантии, что они соберутся именно в последователоьности T1 T2 T3. Могут и в T2 T3 T1.

С другой стороны, блокировку последовательной сборочницы планировщик честно эмулирует: если T1 собирается, то T2 и T3 никогда не будут отправлены на сборку, При этом, если T1 --- тяжелая транзакция, как существенное обновление python или texlive, то такая транзакция будет обрабатываться нашей сборочницей неделями, а то и месяцами.

Планировщик, эмулируя блокировку последовательной сборочницы, по сути, банит пользователя на эту неделю, а то и месяц. IMHO, так не правильно. Если есть свободные вычислитльные ноды и AWAITING task'и, планировщик просто обязан помочь найти им друг друга. Ведь сборочница не должна простаивать?

А проблема балансировки нагрузки решается, например, приоритетами. Чем больше у пользователя запущенных task'ов, тем ниже его приоритет у планировщика при запуске нового task'а.

Далее, раз эмуляция последовательной сборочницы востребована, я предлагаю сделать для нее улучшения в интерфейсе пользователя (syntax sugar).

К примеру, сейчас работа с task deps достаточно громоздка. Облегчить работу могла бы опция --after. Опция --after "<task id1>,<task id2>,...,<task idN>" в ssh girar build/run позволит быстро выставить task deps.

Для тех, кому task deps слишком сложны в обслуживании, можно реализовать полноценную эмуляцию последовательной сборочницы. добавить в ssh girar build/run опцию --seq[uential].

Эта опция внутри будет выставлять task'у опцию sequential, а планировщик будет запускать task с опцией sequential только тогда, когда все таски с меньшими номерами либо DONE, либо FAILED. --- очень легко в реализации и гораздо лучше, чем сейчас.


VI. Гибкость и настраиваемость.

Нашей сборочнице не хватает гибкости и настраиваемости. Есть только режим работы по умолчанию и --test-only.

Зачем нашей песочнице гибкость и настраиваемость?

Во-первых, чтобы решать с ее помощью больше задач.

Что хочу предложить:

A) Опция --force-tests. Выполнить все возможные тесты.

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

К примеру, готовлю major обновление, со сменой библиотеки, к примеру perl. Если я отправлю на сборку perl с минимальной обвязкой, то сборочница соберет пакеты и остановится на unmets. А я и так знаю, что perl с минимальной обвязкой не пройдет unmets тест. Мне хочется заранее протестировать новую сборку всеми имеющимися в сборочнице тестами перед тем, как создать большую транзакцию. Если --force-tests, то я узнаю, что в самой сборке perl проблем нет или выявлю проблемы заранее.

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

Другой пример для --force-tests -- пакет не собирается на одной из архитектур сборочницы (скажем, на arm). Но хочется узнать результаты всех тестов вплоть до symbols хотя бы на x86_64, чтобы сразу готовить все нужные исправления.

Б) Опции для гибкой настройки hasher'а. что-то вроде

set <task> [<subtask>] wlimit_time_elapsed <val>
set <task> [<subtask>] wlimit_time_idle <val>
set <task> [<subtask>] wlimit_bytes_written <val>

1) Бывают ситуации, когда стоит уменьшить wlimit_* до нескольких минут, например, при отладке зависающей сборки.

2) иногда текущее умолчание просто недостаточно, чтобы пакет собрался: https://bugzilla.altlinux.org/show_bug.cgi?id=31683 Bug 31683 - big packages: an option to adjust hasher-priv time elapsed limit in task run

В) возможность указать адрес для сообщений (при проблемах с email)

ssh git.alt task run --email <mail addr> option
https://bugzilla.altlinux.org/show_bug.cgi?id=27637

Г) возможность выбрать оптимальный для конкретной задачи алгоритм тестирования.

Последний пункт навеян моим горьким опытом.

Я хотел бы собрать обновление texlive так, как texlive собирается апстримом, т.е. в виде большого числа (6000+, 3000 исходных) пакетов. Но было ощущение, что такую транзакцию за осмысленное время в нашей сборочнице не собрать.

Игорь Зубков тогда привел пример

> А самой кровавой пересборкой был python2.6 -> python2.7. Там было
> больше 2000 пакетов. Наверно месяц пересобирали. :)

По аналогии с python для ее обработки понадобилась бы минимум полтора месяца. Поэтому я сосредоточился на компромиссном варианте, допилить импортированный пакет из Fedora, где все 6000+ пакетов собирались из одного srpm. После долгих и упорных оптимизаций мне удалось добиться, что сборка пакета проходила всего за 1 час. по техническим причинам (баги в rpmbuild на 2Gb) его еще пришлось аккуратно разделить на 2 меньших пакета.

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

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

В итоге с текущей сборочницей мне пришлось отказаться от этого варианта сборки texlive.

Сюрприз же заключался в том, что в моей сборочнице один из пакетов, texlive-doc (2500+ подпакетов), прошел install тест менее чем за два часа. Я не тестировал полную транзакцию, но ожидал, что сборочнице нужно будет не более 4-5 часов вместо 10 суток.

Разница оказалась в алгоритмах. У Дмитрия сборочница тестировала установку пакета в минимальный chroot, у меня - в общий chroot.

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

Первый выявляет тяжело другими способами выявляемые баги, но для пакетов с %post/un скриптами, которые становятся уже редкостью, вытесняемые файлтриггерами, и крайне медленный. Второй гораздо быстрее, ловит свои баги, которые не ловит первый тест, но не ловит те редкие баги, для которых писался первый тест.

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

При больших транзакциях, таких как упомянутая ранее пересборка python, прогоняя новую версию питона через медленный тест с --force-tests и включая быстрый тест на всю пересборочную транзакцию даже в нынешней сборочнице удалось бы ту же транзакцию с питоном собрать на 15 суток раньше.

VII. Алгоритмы ускорения сборки Больших Транзакций.

Большие транзакции естественно возникают при обновлении многих языковых экосистем. У нас на слуху perl и python, сейчас добавился texlive, но это только потому, что они хорошо представлены в Сизифе и регулярно обновляются.

На самом деле это типичная проблема. Просто ранее большинство языковых экосистем у нас были представлены только базовым компилятором или интерпретатором и ограниченным набором основных библиотек. На самом деле такая ситуация не очень хороша. Это как дать пользователю базовую систему (ядро+shell+cc) и сказать, а дальше собирайте сами. Тем не менее, большинство предпочтет полноценный дистрибутив. Но сейчас ALT вырастает из детских штанишек, и у него растут требования к поддержке, в. т. ч. различных языковых экосистем. Поэтому Большие Транзакции (как пересборка питона с 3000+ пакетов) все чаще и чаще посещают нашу сборочницу.

К сожалению, наша сборочница к ним не готова. 30 суток на обработку питона или 10 суток на обработку texlive нельзя назвать адекватным ответом задаче.

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

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

Именно, часть subtask'ов в task'е надо пометить как синхронные.

Синхронный subtask означает, что, пока синхронный subtask и все ему предыдущие не собраны, параллельная сборочница не пытается собрать subtask'и, следующие за синхронным.

К примеру, при обновлении perl я помечу самый первый subtask в транзакции, собственно сам perl, как синхронный. Все остальные subtask'и в обновлении perl я помечать (как синхронный) не буду, пусть сборочница пытается их собрать параллельно.

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

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

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

Зато вместо 10 суток результат будет через 10 часов.