Русская Википедия:Dependency hell

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Шаблон:ОРИСС Dependency hell (Шаблон:Lang-en) — это антипаттерн управления конфигурацией, разрастание графа взаимных зависимостей программных продуктов и библиотек, приводящее к сложности установки новых и удаления старых продуктов. В сложных случаях различные установленные программные продукты требуют наличия разных версий одной и той же библиотеки. В наиболее сложных случаях один продукт может косвенно потребовать сразу две версии одной и той же библиотеки.[1] Проблемы с зависимостями возникают у общих пакетов/библиотек, у которых некоторые другие пакеты имеют зависимости от несовместимых и различных версий общих пакетов. Если установлена одна версия общего пакета/библиотеки, для решения этой проблемы автоматизатору тестирования/программисту/администратору понадобится получить новые или старые версии зависимых пакетов. Это, в свою очередь, может нарушить работу других зависимых пакетов и добавить проблем в другой набор пакетов, таким образом образуя настоящий ад.

Обзор

Современные программные системы широко полагаются на существующие пакеты (модули, компоненты, библиотеки) для обеспечения возможности повторного использования и масштабируемости. Менеджеры пакетов на стороне клиента упростили процесс сборки и обеспечили автоматическую поддержку дальнейшей доработки пакетов. В последние годы многие системы перешли к модели, в которой пакеты хранятся в центральном репозитории, доступном через клиентский менеджер пакетов (например, pip для Python, Cargo для Rust, CPAN для Perl и т. д.). В этой модели определение подходящей версии для зависимости от заданного пакета является одной из проблем.Шаблон:Sfn

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

В современной разработке программного обеспечения широко используются системы сборки и соответствующие скрипты сборки для автоматизации преобразования исходного кода в исполняемое программное обеспечение. Системы сборки, такие как Make, GNU Autotools, Ninja, вместе со своими соответствующими скриптами сборки, применяются практически в каждом крупном проекте.Шаблон:Sfn

Ошибки, связанные с зависимостями, являются наиболее распространенными среди различных типов ошибок сборки (составляют от 52% до 65%). Эти ошибки можно разделить на две категории: отсутствующие зависимости и избыточные зависимости. Обе ошибки возникают из-за несоответствия между заявленной зависимостью и фактической зависимостью.Шаблон:Sfn

Отсутствующая зависимость возникает, когда фактическая зависимость не была объявлена, что приводит к сбоям в параллельной сборке или неправильным результатам в инкрементной сборке. Избыточная зависимость возникает, когда заявленная зависимость не имеет соответствующей фактической зависимости, что приводит к плохой производительности в параллельных или инкрементных сборках.Шаблон:Sfn

Виды проблем

«Dependency hell» принимает несколько форм[2]:

Множество зависимостей

Приложение зависит от большого числа объёмных библиотек, которые требуют длительных скачиваний, занимают много дискового пространства. Возможна ситуация, когда приложение построено на определённой платформе (например Java) и требует установки этой платформы, в то время, как 99% остальных ваших приложений поддержки этой платформы не требуют. Это частный случай проблемы когда либо приложение использует маленькую часть большой платформы и в конечном итоге требует установки всей платформы (что может быть решено только с помощью рефакторинга приложения), либо маленькое приложение опирается на большое число различных библиотек одновременно.

Длинные цепочки зависимостей

Приложение зависит от библиотеки "А", которая зависит от библиотеки "Б", ... , которая в свою очередь зависит от библиотеки "Я". Это частный случай проблемы множество зависимостей, с той разницей, что большое количество зависимостей осложнено их запутанной и длительной взаимосвязью, которая должна решаться вручную. (Пользователь устанавливает приложение, но ему выдаётся требование установить библиотеку "А", он устанавливает библиотеку "А", но при попытке это сделать библиотека "А" запрашивает установку библиотеки "Б" и т.д.

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

Конфликтующие зависимости

Если "Приложение 1" зависит от библиотеки "А" версии 1.2, а "Приложение 2" зависит от той же библиотеки "А", но уже версии 1.3, и различные версии библиотеки "А" не могут быть одновременно установлены, то "Приложение 1" и "Приложение 2" нельзя одновременно использовать (или даже установить, если установщики проверяют зависимости).

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

На Linux-системах эта проблема часто возникает при попытке установки в систему пакетов от разных разработчиков, которые не предназначены для этой версии системы. В таком случае удовлетворение долгой цепи зависимостей пакетов даже может привести, например, к запросу другой версии библиотеки glibc, одной из крайне важных, основополагающих системных библиотек. Если это случается пользователю будет предложено удалить тысячи пакетов, что по сути будет равноценно удалению, например, 80% системы, включая графические оболочки и сотни различных программ.

Циклические зависимости

Ситуация, когда приложение "А" версии 2 зависит от приложения "Б", которое зависит от приложения "В", которое в свою очередь зависит от приложения "А" , но версии 1. Это приводит к тому, что в пакетных системах типа RPM или DPKG, пользователь должен установить две версии одного и того же исходного приложения "А", что может оказаться недопустимо и не разрешено менеджером пакетов. Однако на Linux-системах наличие циклической зависимости, чаще всего является результатом непонимания пользователем того, как пользоваться операционной системой и её менеджером пакетов.

Решения

Зависимость от фиксированных версий обеспечивает детерминированность сборок, но может упустить важные исправления в последующих версиях. Зависимость от диапазонов версий позволяет автоматически включать исправления ошибок, обновления безопасности и улучшения производительности, но при этом возникает риск несовместимых изменений. Диапазоны версий позволяют менеджеру пакетов выбрать наилучшую версию на основе указанных ограничений. Конфликты версий могут возникать, когда пакеты имеют множественные (транзитивные) зависимости, что приводит к конфликтам и ошибкам во время выполнения. Некоторые техники, такие как загрузчики классов в OSGi или шаблоны программирования на JavaScript, пытаются смягчить конфликты версий во время выполнения, но многие системы все еще сталкиваются с проблемами, напоминающими ошибки типа «DLL-hell».

Нумерация версий

Наиболее очевидное (и часто используемое) решение проблемы — стандартизированная нумерация версий, при которой в программном обеспечении используется специфический номер для каждой версии (aka основная версия), а также дополнительное число для второстепенной версии (aka minor version), например: 10.1, или 5.7. Основная версия изменяется, только, когда программа, которая имеет эту версию, больше не будет совместима с обновленной программой с учётом внесенных в неё изменений. Вспомогательная версия может изменяться даже при небольших изменениях в коде, которые не блокируют стороннему программному обеспечению возможность работы с разрабатываемой программой. В случаях, таких как этот, сторонние программы могут просто запрашивать компонент, имеющий определенную основную версию, и произвольную младшую, второстепенную (больше либо равную определенной минорной версии). Все будет продолжать работать, и зависимости будут разрешены успешно, даже, если второстепенная версия изменилась.

Параллельная установка различных версий ПО

Решение с нумерацией версий можно улучшить, сделав нумерацию версий поддерживаемой на уровне операционной системы. Это позволит приложению запрашивать модуль/библиотеку по уникальному названию и задавать ограничения на номера версий, эффективно используя операционную систему. Общий модуль может быть помещен в центральное хранилище без риска отказа приложений, которые зависят от предыдущих или последующих версий этого модуля. Каждая версия получает свою собственную запись в хранилище, находясь рядом с другими версиями того же самого модуля. Такое решение используется в операционной системе Windows начиная с Windows Vista, где Global Assembly Cache является имплементацией такого центрального хранилища со связанными сервисами и интегрированным менеджером пакетов.

Хороший пакетный менеджер

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

Многие современные дистрибутивы Linux имеют пакетные менеджеры, основанные на хранилищах для решения проблемы зависимостей. Эти системы — слой поверх менеджеров пакетов RPM, dpkg, или других пакетных систем, и спроектированы для автоматического разрешения зависимостей путём поиска в предопределенном хранилище программного обеспечения. Обычно эти хранилища программного обеспечения представляют из себя FTP или web-сайты, каталоги на локальном компьютере или распределенные через компьютерные сети или, что встречается менее часто, каталоги на съёмных носителях, таких как CD или DVD. Это исключает ад зависимостей для пакетов программного обеспечения, хранящихся в тех репозиториях, которые обычно поддерживаются провайдерами дистрибутивов Linux и на зеркалах по всему миру. Также эти репозитории часто велики, невозможно иметь все части программного обеспечения в них сразу, поэтому ад зависимостей всё же может наступить. В любом случае, специалисты по обслуживанию репозиториев сталкиваются с адом зависимостей в той или иной мере.[3] Примерами таких систем являются APT, YUM, urpmi, Zypper, Portage, Pacman и другие.

PC-BSD (операционная система на базе FreeBSD) до версии 8.2 справляется с dependency hell путём размещения пакетов и зависимостей в самодостаточные каталоги-контейнеры, избегая таким образом повреждения системных библиотек при обновлениях или иных их изменениях. Система PC-BSD использует «PBI» (Push Button Installer) как основной пакетный менеджер.[4]

Настройки установщика

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

Лёгкая адаптивность в программировании

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

Программно-аппаратный комплекс

Другой подход к предотвращению проблем зависимостей состоит в развертывании программного обеспечения через программно-аппаратный комплекс. Зависимости инкапсулируются в модуль, позволяя пользователям не беспокоиться о зависимостях в программном обеспечении. Это — забота разработчиков программного обеспечения.

Портативные приложения

В данном случае подразумеваются приложения (либо версии стандартных приложений), работающие в собственной, замкнутой и самодостаточной, среде и имеющую минимальные зависимости от системных библиотек. Все требуемые для работы программы компоненты добавляются на этапе собственной разработки, кодирования и пакетирования, при этом важные для работы программы файлы и компоненты максимально инкапсулируются в независимой от остальной системы среде, в результате, путём минимального воздействия с остальной системой, удаётся избежать большинства проблем с неразрешёнными зависимостями. Зачастую может работать независимо от системы, на которой приложение запущено. Приложения в RISC OS и ROX Desktop в среде Linux используют application directories, таким образом работая по схожему принципу: программа со всеми своими зависимостями содержится в собственном самодостаточном каталоге (папке).[5]

На других платформах

На определенных программных платформах, понятие «dependency hell» получает своё собственное название, в зависимости от названия конфликтующих компонентов.

  • DLL hell — Процесс, подобный «dependency hell» для систем семейства Microsoft Windows.
  • JAR hell — Аналог «dependency hell» в среде Java Runtime Environment.
  • RPM hell — Подвид «dependency hell», присутствующий в Linux-дистрибутивах, использующих RPM-среду и соответствующие пакетные менеджеры (в частности, в дистрибутиве Red Hat).[6]
  • Циклическая перезагрузка обновления 3033929 для MS Windows 7[7]

См. также

Примечания

Шаблон:Примечания

Литература

Ссылки