[devel] incoming/girar: проблема производительности.

Igor Vlasenko vlasenko на imath.kiev.ua
Чт Авг 27 05:29:53 MSK 2020


1. incoming/girar: проблема видна была издалека.
________________________________________________

Когда-то давно я столкнулся с проблемой, что тогдашний incoming
слишком медленно обрабатывал пакеты. Это стало одной из причин,
почему появился отдельный репозиторий autoimports.

Года 3 назад я написал ряд писем в devel@, которые описывали алгоритмы
параллелизации работы сборочницы, которые позволили бы существенно
(на порядки) повысить ее производительность.

Содержимое этих писем доступно на
https://www.altlinux.org/Girar/Parallel .

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

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

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

Задним числом мое предложение выглядит так:
разделить girar на серверную и локальную части.
Выделить из girar локальную сборочницу, с помощью которой каждый может
локально у себя сформировать таск, локально его собрать на локальных
архитектурах машины (i586/x86_64 или armh/aarch64) и пропустить через
полный набор тестов girar для таска,
при необходимости локально выпустить новый репозиторий из старого
репозитория и таска и тоже пропустить полученный репозиторий через
полный набор тестов girar для репозитория.

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

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

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

Чтобы показать, что можно сделать от переписывания локальной сборочницы,
я написал тогда письмо в devel@, где сравнил производительность
тогдашней сборочницы (1.000 пак./сут.) со своими скриптами для
autoimports, которые на той же задаче собрать-протестировать task
достигали производительности в пол пакета в секунду (40.000 пак./сут.)

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

Сейчас у меня, возможно, получится объяснить.
Компьютерный мир сверхмонополистичен. В платном п/о нишу, как правило,
занимает кто-то один. В открытом п/о нишу обычно занимают 2 мейнстримных пакета
и есть хвост экспериментов. 2 браузера, хромиум и фаерфокс, 2
консольных редактора, vim и emacs, 2 графических редактора, gimp и
cinepaint, ...

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

К сожалению, не всем это очевидно. Здесь я чувствую себя героем книг
про АИ и попаданцев. Типичный сеттинг --- АИ. в свое время князья и
бояре московские прощелкали клювом и теперь Москва --- районный центр
Владимирской области с 15 тысячами населения. Герой возвращается в
прошлое, которое можно еще изменить, и рассказывает про альтернативную
Москву, столицу необъятных территорий, мегаполис-миллионник.
А для народа это все сказки, ему в ответ
- Что ты несешь, блаженный. 15 тысяч пакетов по нынешним временам
это же очень и очень круто, а больше и не нужно :(

Каким боком здесь сборочница?
Сборочница выступает в роли логистического ограничителя.
В космосе можно содержать базу в 10 человек. В Антарктиде -- сотню.
В Сибири вдали от рек и дорог -- тысячу.
Если грузы можно доставить по реке или грунтовке, то еще на порядок больше.
Если там крупный морской порт и/ли железная дорога, то пределов росту
нет, была бы за что завозить и что вывозить.
Наша сборочница медленная, и для нас она выступает в роли грунтовки или
мощенного конного тракта. Вроде бы дорога и есть, но реально много пакетов
провезти по ней в дистрибутив не получится.

Что я могу с этим сделать? Полтора года назад, после упомянутого выше
письма в Сизиф, я начал переписывать свои скрипты для autoimports
в прототип локальной сборочницы. При этом нашел еще один резерв для
улучшения быстродействия сборочницы -- за счет оптимизации работы
hasher при работе с неизменным репозиторием.
Одна из таких оптимизаций ---
https://bugzilla.altlinux.org/show_bug.cgi?id=36531
экономия 10 секунд при создании chroot.
Какой это эффект дало бы для сборочницы?
Простой таск с 1 пакетом -- в среднем быстрее на 30 секунд (10 секунд на
сборочный чрут и по 10 секунд на install test chroot для 2-х бинарных
пакетов). На больших транзакциях больше.
К примеру, пересборка perl будет выполняться быстрее на 20 минут,
пересборка python -- быстрее на час.

#36531 -- тривиальный патч: сделать (кусок кода) отключаемым:
if(переменная) { (кусок кода) }.
Но я после недели обсуждений с Дмитрием так и не смог пробить
#36531 в апстрим hasher :(. В итоге форкнул свою копию hasher
и свернул свою разработку прототипа локальной сборочницы. :(

Возможно, проявив большую настойчивость, зудя Дмитрию,
как комар над ухом, еще за неделю и продавил бы патч в апстрим.
Но это было только начало работы над прототипом --
в скриптах от autoimports использовалась еще одна оптимизация
работы hasher -- кеширование рабочего каталога. Там это было
сделано хаком -- хардлинк-копией образца рабочего каталога hasher,
но по хорошему эту возможность нужно было тоже переносить
на уровень hasher. И сколько еще возникнет таких моментов...
А в перспективе, после начальной рабочей версии прототипа,
последовал бы рефакторинг сборочницы с выделением локальной сборочницы в
отдельный независимый от сервера пакет, где счет изменениям
пошел бы на сотни. Если каждый чих рассматривать неделю,
я бы таких задержек не выдержал.
Впрочем, год назад оказался в похожей ситуации с wm-select
https://bugzilla.altlinux.org/show_bug.cgi?id=37190
так что, возможно, такое отношение к патчам со сборочницей не связано.

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

За это время в русле "пчелы против меда" событий произошло мало,
Дмитрий ругал ruby-team, критиковал кого-то, кто отправил пакет
на пересборку зря, но меня не трогал.

Тем неожиданнее стали дальнейшие события.

2. incoming/girar: помощь подкралась незаметно.
_______________________________________________

Весной я взялся опять обновлять perl.
Подвоха я не чувствовал. Там в транзакции 420+ пакетов,
и оптимизировать там, как казалось, нечего, природу не
обмануть, 420+ пакетов придется пересобрать.

Небольшое отступление.
До этого зимой пробовал обновиться до 5.28, подготовил обновление,
но 5 пакетов из транзакции perl были в FTBFS, развешал баги
на починку, и отложил в сторону. К весне дождался,
но как раз вышел 5.30 и нужно было готовить обновление заново.
В отличие от обновления python, обновление perl плотно заскриптовано
и все шаги по обновлению расписаны в методичке прямо в git в папке
perl.git/altlinux/maintainer_notes
http://git.altlinux.org/people/viy/packages/?p=perl.git;a=tree;f=altlinux/maintainer_notes
Поэтому обновление perl проводить на порядок легче, чем вручную.
Нужно заметить, что логистика автоматизированного обновления
напоминает железную дорогу. Рельсы -- это заскриптованные пути
для обновления. Вне рельсов поезд (транзакция) не едет,
это то же самое, что разгрузить груз с поезда в мешки
и тащить вручную -- формально можно, но слишком затратно по сравнению
с доставкой по рельсах, никто так делать не захочет.
Вне заскриптованных маршрутов что угодно забирает сил и времени
на порядки больше. Если куда-то позарез ехать надо,
проще сначала проложить рельсы (заскриптовать).
Пакеты в FTBFS для транзакции perl как красный свет --
в истрии с обновлением 5.28 поезд на рельсах, транзакция загружена,
но путь занят -- поезд не сможет проехать,
пока хоть один пакет из транзакции не собирается.

Итак, занимался я обновлением perl. Отправил, наконец,
транзакцию с perl на сборку. Но утром пришел неожиданный результат --
сборка прервана, у в работу сборочницы вмешался Дмитрий,
который, как оказалось, пришел мне помочь,
а транзакцию остановил, так как ее можно оптимизировать
с использованием task rebuild.

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

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

В случае же с транзакциями все наоборот. Транзакция из сотен пакетов,
которая успешно соберется сегодня, завтра уже может и не соберется.
Один из пакетов устарел, или вдруг перестал собираться --
вся транзакция на свалку. Собралась транзакция --- хорошо, задача выполнена.
Не собралась (естественным образом) -- тоже хорошо, узнали
что-то новое, чего раньше не знали и что надо исправлять.

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

Эмоции в корректной форме можно было выразить анекдотом:

В детском саду случилось ЧП: после того, как два солдата починили электропроводку, дети стали материться. Руководитель детского сада – жалобу командиру части, тот вызывает солдат и спрашивает:
-Так, бойцы, на вас тут жалоба – дети после вашей работы воспитателей по матери и отцу склонять начали. Докладайте – это ваших рук дело?
-Никак нет! – отвечают бойцы. -Не ругались вовсе.
-Быть такого не может, – не верит командир. -А инциденты были?
-Был один, но все было благопристойно, товарищ командующий. – докладывает тот, который постарше. -Рядовой Сидоров стоял на стремянке и паял провод, а я держал стремянку, и тут мне на голову стало капать расплавленное олово...
-Ну и!.. – не терпится командиру.
-А я ему и говорю: рядовой Сидоров, разве ты не видишь, что на голову твоему боевому товарищу капает расплавленное олово?

В общем, приложило эмоциями меня сильно, но сдержался,
тем более, транзакцию уже не вернешь.
Постарался пойти навстречу. Предложение Дмитрия было
вполне здравое и не так сложно --- скрипты писались в расчете
и на такой вариант, осталось только явно его выписать
и протестировать (даже нашел баг в сборочнице
#38332 https://bugzilla.altlinux.org/show_bug.cgi?id=38332 )
Через неделю улучшенные скрипты были готовы, можно было продолжать.

Но вместо обновления получился цирк, так как Дмитрий
продолжил мне помогать, а я слова не мог сказать, поскольку
боялся поддаться эмоциям и начать выражаться нецензурно.
Получилась история, когда к Кролику пришел в гости Винни Пух.
Что по этому поводу подумал Кролик, так и осталось тайной.
Дмитрий в простоте своей умудрился еще раз на бис
прервать сборку транзакции, после чего сам смысл пытаться обновить perl
стал под большим вопросом, а потом и вообще "застрял в норе,
закупорив вход" --- обновление оказалось невозможным без починки пакета sdf,
пакета с жестким acl=ldv, о чем Дмитрий даже написал в рассылке,
но как-то чинить пакет или открывать acl не стал.

Проблема была в том, что и Дмитрий действовал не со зла и не нарочно,
но получилось по пословице "простота хуже воровства". И вообще эта
тема со сборочницей будет не понятна большинству майнтайнеров до тех пор,
пока они сами не столкнутся на практике с автоматизированной
обработкой большого числа пакетов.
А развернуто объяснить, вместо ругани, не так просто.

3. incoming/girar: истоки проблемы.
_______________________________________________

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

Со мной Дмитрий пытался оптимизировать майнтайнера к сборочнице,
а я хотел наоборот. Что же мне мешает оптимизироваться?

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

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

При автоматизированном подходе все наоборот.
Когда пакетов десятки тысяч, проще написать скрипт - корректор
тега License. Парсим тег License, считаем md5sum LICENSE COPYING
(хвоста файла для MIT лицензий), делаем выводы,
генерируем исправленные пакеты и список сложных случаев.
Очень существенно экономим человеческое время за счет машинного.
Р-р-раз и 100500 пакетов готово.
Второй плюс - полиси внедряется практически мгновенно.
Но нужна нормальная сборочница. Которая способна пересобрать
исправления менее чем за сутки.

Еще раз. Если здесь раздадутся голоса -- зачем насиловать сборочницу,
гонять лишний релиз ради тега License? То ответ -- зачем тогда
принимать полиси, если его внедрение не стоит релиза?
Здесь полиси по заполнению лицензий можно заменить
на shared libs policy. С которым у нас уже 10 лет внедрение все идет и
никак не дойдет. Вот напишу я преобразователь в shared libs policy ...

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


-- 

I V


Подробная информация о списке рассылки Devel