Dependency hell
Dependency hell (англ. ад зависимостей) — это антипаттерн управления конфигурацией, разрастание графа взаимных зависимостей программных продуктов и библиотек, приводящее к сложности установки новых и удаления старых продуктов. В сложных случаях различные установленные программные продукты требуют наличия разных версий одной и той же библиотеки. В наиболее сложных случаях один продукт может косвенно потребовать сразу две версии одной и той же библиотеки.[1] Проблемы с зависимостями возникают у общих пакетов/библиотек, у которых некоторые другие пакеты имеют зависимости от несовместимых и различных версий общих пакетов. Если установлена одна версия общего пакета/библиотеки, для решения этой проблемы автоматизатору тестирования/программисту/администратору понадобится получить новые или старые версии зависимых пакетов. Это, в свою очередь, может нарушить работу других зависимых пакетов и добавить проблем в другой набор пакетов, таким образом образуя настоящий ад.
Обзор
Обычно, вместо того, чтобы «изобретать велосипед», ПО разрабатывается так, чтобы получать необходимые функции из других программных компонентов которые доступны разработчику или были разработаны в другом месте. Это можно сравнить с тем, как люди, которые строят дом, могут купить готовые материалы, такие как кирпичи, окна и двери, нежели создавать их самостоятельно.
Даже для строителя может оказаться проблемой, если строение сделано для определенного типа дверей, и доступны только двери с разными спецификациями. Однако в мире программного обеспечения, где компоненты развиваются очень быстро и сильно зависят друг от друга, эта проблема становится намного актуальнее.
Проблему «ада зависимостей» можно рассматривать как антипаттерн, где вина лежит не столько на поставщиках продукта, сколько на фреймворке, в рамки которого они должны входить.
Виды проблем
«Dependency hell» принимает несколько форм[2]:
Множество зависимостей
Приложение зависит от большого числа объёмных библиотек, которые требуют длительных скачиваний, занимают много дискового пространства. Возможна ситуация, когда приложение построено на определённой платформе (например Java) и требует установки этой платформы, в то время, как 99% остальных ваших приложений поддержки этой платформы не требуют. Это частный случай проблемы когда либо приложение использует маленькую часть большой платформы и в конечном итоге требует установки всей платформы (что может быть решено только с помощью рефакторинга приложения), либо маленькое приложение опирается на большое число различных библиотек одновременно.
Длинные цепочки зависимостей
Приложение зависит от библиотеки "А", которая зависит от библиотеки "Б", ... , которая в свою очередь зависит от библиотеки "Я". Это частный случай проблемы множество зависимостей, с той разницей, что большое количество зависимостей осложнено их запутанной и длительной взаимосвязью, которая должна решаться вручную. (Пользователь устанавливает приложение, но ему выдаётся требование установить библиотеку "А", он устанавливает библиотеку "А", но при попытке это сделать библиотека "А" запрашивает установку библиотеки "Б" и т.д.
Иногда наличие такой длинной цепочки зависимостей может приводить к возникновению конфликтов, когда разными компонентами цепочки требуются разные версии одного и того же пакета или библиотеки. (см. конфликтующие зависимости) Такие длинные цепочки зависимостей должны решаться пакетными менеджерами, которые делают это в автоматическом режиме, вместо того чтобы заставлять пользователя решать их ручным способом, при котором зависимости могут остаться частично неудовлетворёнными (не все инсталляторы библиотек постоянно напоминают о всех своих зависимостях пользователю).
Конфликтующие зависимости
Если "Приложение 1" зависит от библиотеки "А" версии 1.2, а "Приложение 2" зависит от той же библиотеки "А", но уже версии 1.3, и различные версии библиотеки "А" не могут быть одновременно установлены, то "Приложение 1" и "Приложение 2" нельзя одновременно использовать (или даже установить, если установщики проверяют зависимости).
Когда возможно одновременное использование разных версий библиотеки — это решают путём параллельных установок разных версий библиотеки. В противном же случае, установка с использованием новой версии библиотеки должна сопровождаться удалением старой версии библиотеки и, соответственно, всех программ, которые зависят от неё т.к. они будут неработоспособны.
На Linux-системах эта проблема часто возникает при попытке установки в систему пакетов от разных разработчиков, которые не предназначены для этой версии системы. В таком случае удовлетворение долгой цепи зависимостей пакетов даже может привести, например, к запросу другой версии библиотеки glibc, одной из крайне важных, основополагающих системных библиотек. Если это случается пользователю будет предложено удалить тысячи пакетов, что по сути будет равноценно удалению, например, 80% системы, включая графические оболочки и сотни различных программ.
Циклические зависимости
Ситуация, когда приложение "А" версии 2 зависит от приложения "Б", которое зависит от приложения "В", которое в свою очередь зависит от приложения "А" , но версии 1. Это приводит к тому, что в пакетных системах типа RPM или DPKG, пользователь должен установить две версии одного и того же исходного приложения "А", что может оказаться недопустимо и не разрешено менеджером пакетов. Однако на Linux-системах наличие циклической зависимости, чаще всего является результатом непонимания пользователем того, как пользоваться операционной системой и её менеджером пакетов.
Решения
Нумерация версий
Наиболее очевидное (и часто используемое) решение проблемы — стандартизированная нумерация версий, при которой в программном обеспечении используется специфический номер для каждой версии (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.
- Extension conflict — Аналог «dependency hell» для старых версий Mac OS.
- JAR hell — Аналог «dependency hell» в среде Java Runtime Environment.
- RPM hell — Подвид «dependency hell», присутствующий в Linux-дистрибутивах, использующих RPM-среду и соответствующие пакетные менеджеры (в частности, в дистрибутиве Red Hat).[6]
- Циклическая перезагрузка обновления 3033929 для MS Windows 7[7]
См. также
Примечания
- Michael Jang. Linux annoyances for geeks (неопр.). — O’Reilly Media, 2006. — ISBN 9780596552244.
- Dependency hell
- Pjotr Prins, Jeeva Suresh, and Eelco Dolstra. Nix fixes dependency hell on all Linux distributions . linux.com (22 декабря 2008). — «All popular package managers, including APT, RPM and the FreeBSD Ports Collection, suffer from the problem of destructive upgrades. When you perform an upgrade -- whether for a single application or your entire operating system — the package manager will overwrite the files that are currently on your system with newer versions. As long as packages are always perfectly backward-compatible, this is not a problem, but in the real world, packages are anything but perfectly backward-compatible. Suppose you upgrade Firefox, and your package manager decides that you need a newer version of GTK as well. If the new GTK is not quite backward-compatible, then other applications on your system might suddenly break. In the Windows world a similar problem is known as the DLL hell, but dependency hell is just as much a problem in the Unix world, if not a bigger one, because Unix programs tend to have many external dependencies.». Дата обращения: 22 мая 2013.
- pbiDIR . Дата обращения: 27 марта 2013. Архивировано 27 марта 2013 года.
- Application directories . Дата обращения: 7 сентября 2013.
- Weinstein, Paul Is Linux Annoying? . linuxdevcenter.com (11 сентября 2003). Дата обращения: 10 апреля 2010.
- Проблема циклической перезагрузки обновления 3033929 .