Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

goal: Better handling of obsoleted pkg installation #354

Merged
merged 2 commits into from
Mar 21, 2023

Conversation

m-blaha
Copy link
Member

@m-blaha m-blaha commented Mar 15, 2023

In situation where a package exists in multiple versions and some older version is being obsoleted, any of obsoleters was considered a valid solution.

The result could be really misleading. For example let's have this package set:

systemd-udev-1.0

systemd-udev-2.0
Obsoletes: systemd-udev < 2

systemd-boot-unsigned-2.0
Obsoletes: systemd-udev < 2

In this case dnf install systemd-udev may lead to installation of systemd-boot-unsigned which is probably not what the user expected. The reason is the split in the upgrade-path created by obsolete and both branches - systemd-udev-2.0 and systemd-boot-unsigned-2.0 are considered valid.

With this patch install command takes into account only obsoleters of the best version of the package so the dnf install systemd-udev results in correct installation of systemd-udev-2.0 package.

Resolves: #339
Requires tests adjustment: rpm-software-management/ci-dnf-stack#1236

@m-blaha
Copy link
Member Author

m-blaha commented Mar 15, 2023

@m-blaha
Copy link
Member Author

m-blaha commented Mar 15, 2023

Oh, the test for this issue is already present so I'll only change it according to the changed behavior.

libdnf::rpm::PackageQuery data_query(base_query.get_base(), libdnf::sack::ExcludeFlags::APPLY_EXCLUDES, true);
data_query.update(data);
data_query.filter_priority();
data_query.filter_latest_evr();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking about two edge cases where we might have a problem. Both cases are related to situation when package name is installed.

  1. When vendor change is not allowed (configuration allow_vendor_change)
  2. We will have a different set of candidates in comparison to upgrade request with the same argument

I have a suggestion - if data contains installed package than use the original approach. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have test scenarios for these situations. I'll try to prepare them - it will definitely help me wrap my head around it.

@m-blaha m-blaha force-pushed the mblaha/split_install branch from e3b2fe1 to d50c2bc Compare March 20, 2023 12:38
@@ -45,8 +45,26 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
namespace {

void add_obsoletes_to_data(const libdnf::rpm::PackageQuery & base_query, libdnf::rpm::PackageSet & data) {
libdnf::rpm::PackageQuery data_query(base_query.get_base(), libdnf::sack::ExcludeFlags::APPLY_EXCLUDES, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a problem of the PR - The creation of an empty query was cryptic to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about to have query constructor with packageset and flags?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. The reason is to by-pass the missing constructor.

@j-mracek
Copy link
Contributor

j-mracek commented Mar 21, 2023

It looks like that one test is failing

   @dnf5 @bz1902279
  Scenario: Install of obsoleting package, even though higher version than obsoleted present  # dnf/obsoletes.feature:34
    Given I use repository "dnf-ci-obsoletes"                                                 # dnf/steps/repo.py:182
    When I execute dnf with args "install PackageE"                                           # dnf/steps/cmd.py:106
    Then the exit code is 0                                                                   # common/output.py:57
    And Transaction is following                                                              # dnf/steps/transaction.py:200
      | Action  | Package                           |
      | install | PackageA-Obsoleter-0:1.0-1.x86_64 |
      Assertion Failed: [rpmdb] Package PackageA-Obsoleter-0:1.0-1.x86_64 not 'install'; Possible candidates: PackageE-0:3.0-1.x86_64
      Captured stdout:

I looked at the bug and it looks like that test is wrong (keeping wrong behavior) and your patch fix it. May I ask you to modify the test?

m-blaha added 2 commits March 21, 2023 12:31
It's a shortcut instead of creating an empty PackageQuery and then
updating it with the content of PackageSet.
In situation where a package exists in multiple versions and some older
version is being obsoleted, any of obsoleters was considered a valid
solution.

The result could be really misleading. For example let's have this package set:

systemd-udev-1.0

systemd-udev-2.0
Obsoletes: systemd-udev < 2

systemd-boot-unsigned-2.0
Obsoletes: systemd-udev < 2

In this case `dnf install systemd-udev` may lead to installation of
systemd-boot-unsigned which is probably not what the user expected. The
reason is the split in the upgrade-path created by obsolete and both
branches - systemd-udev-2.0 and systemd-boot-unsigned-2.0 are considered
valid.

With this patch install command takes into account only obsoleters of
the best version of the package so the `dnf install systemd-udev`
results in correct installation of systemd-udev-2.0 package.
@m-blaha m-blaha force-pushed the mblaha/split_install branch from d50c2bc to 28ad621 Compare March 21, 2023 11:32
@@ -267,6 +267,11 @@ PackageQuery::PackageQuery(const BaseWeakPtr & base, ExcludeFlags flags, bool em
PackageQuery::PackageQuery(libdnf::Base & base, ExcludeFlags flags, bool empty)
: PackageQuery(base.get_weak_ptr(), flags, empty) {}

PackageQuery::PackageQuery(const PackageSet & pkgset, ExcludeFlags flags)
: PackageQuery(pkgset.get_base(), flags, true) {
update(pkgset);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is functional but it looks like a strange implementation. I have to check internals

Copy link
Contributor

@j-mracek j-mracek Mar 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor could look like following. But there is one small difference - creation of empty Map nad and operator vs copy of Map. And it is more code.

PackageQuery::PackageQuery(const PackageSet & pkgset, ExcludeFlags flags)
    : PackageSet(src), p_pq_impl(new PQImpl) {
    p_pq_impl->flags = flags;

    auto & pool = get_rpm_pool(base);
    switch (flags) {
        case ExcludeFlags::APPLY_EXCLUDES:
            // Considered map in Pool uses APPLY_EXCLUDES. It can be used in this case.
            base->get_rpm_package_sack()->p_impl->recompute_considered_in_pool();
            break;
        case ExcludeFlags::IGNORE_EXCLUDES:
            break;
        default: {
            auto considered = base->get_rpm_package_sack()->p_impl->compute_considered_map(flags);
            if (considered) {
                p_pq_impl->considered_cache = std::move(considered);
            }
            break;
        }
    }
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please the decision is up to you.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand your solution introduces code duplicity (it's basically a copy of current PackageQuery ctor).

I don't know, the difference in performance is one SolvMap | operation. Maybe if this constructor is massively used, then even this small performance increase may make a difference. But now there is only one place where this constructor is actually used.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or would it be better from your point of view to use directly something like *p_impl |= *pkgset.p_impl instead of update()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no performance difference between both solutions. Creation of Map and copy is cheap. And I agree that there is an overlap with other code.

@j-mracek
Copy link
Contributor

LGTM

@j-mracek j-mracek merged commit fef4d28 into main Mar 21, 2023
@j-mracek j-mracek deleted the mblaha/split_install branch March 21, 2023 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Do we need to add obsoletes for install action?
2 participants