Add a base_version filter#7844
Conversation
3b22794 to
ca2e6ba
Compare
ca2e6ba to
3c005b4
Compare
… repository versions This PR adds a base_version filter to the content viewset that lets repository_version_added and repository_version_removed compute the net content diff between two arbitray (non-adjacent) repository versions, rather than only the single-step diff against the immediate predecessor. Benchmarks on a synthetic 200k-unit system with 1000-unit diffs between repo versions show that implementing this base_filter vs running a complex q query is ~5-15x faster, with the query going from 1486 ms -> 99 ms for a 50k/50k repo version pair and 2403 ms -> 479 ms for a 199k/199k repo version pair. With base_version, planning time also decreases significantly and stays ~0.2 ms. When base_version is omitted, behavior is unchanged (single-step diff), preserving backward compatibility. If base_version is provided without repository_version_added or repository_version_removed, nothing happens. Closes pulp#7831 Assisted by: Claude Opus 4.8
3c005b4 to
423500f
Compare
| return Content.objects.filter(pk__in=self.content_ids_subquery()).exclude( | ||
| pk__in=base_version.content_ids_subquery() |
There was a problem hiding this comment.
This sounds like the real improvement here.
Can we even mark content_ids as deferred so that the humongous list never makes it to python in the first place? (Maybe that's already the case...)
There was a problem hiding this comment.
Also does this change already improve the q filter scenario?
There was a problem hiding this comment.
I'll try to sum what we talked about at our pulpcore meeting but generally we agree on investigating these two items here. For the latter, we think that the q filter could/should be using subqueries. If it's not, it's worth understanding why it's not. Fixing that should alleviate the performance problem.
There was a problem hiding this comment.
I'm not sure if this will correctly answer the question of whether the subquery I used here improves q performance, but:
- The filter we pass through
qoriginally does eventually reach theContentRepositoryVersionFilterlogic and callsget_content(). However, there's a condition there where a subquery strategy is only used if a version has more than 65535 units. Below that, it's still inlining the content id list. qalso has a different query shape compared to the implementation here that directly targets the repository version table. TheNOT repository_version=ypart becomesContent.objects.all() EXCEPT (content in Y), which means it's still running through the entire table- The
base_versionimplementation here works around the expression filter strategy that isq(which I presume has other uses) and goes straight tofilter(pk__in=X).exclude(pk__in=Y). The subquerying optimizes this filter path to be sure, but I'm hesitant in changing things related toqbecause I don't fully understand its other uses.
There was a problem hiding this comment.
- However, there's a condition there where a subquery strategy is only used if a version has more than 65535 units.
I always wondered why we should have this in the first place. I cannot think of the subquery on an small or empty table being an issue at all.
|
We have fixed a couple of DB performance issues by switching to using subqueries. I foresee us doing this more broadly throughout the codebase. |
|
Here are some added insights to some of the performance concerns raised. I'm mainly trying to answer these two questions:
Tl;dr - the To review, (X's content)
If the existing And here are some benchmarks comparing all three scenarios. These are measured on a 200k-unit dataset with 1000-unit diff between repo versions. Results are taken over a 3-run average.
As the results show, optimizing The worrying part is where we would implement the fix for • The As an aside, I've also explored removing the 65535 content unit conditional on |
|
I've opened a draft PR on what subqueries for |
📜 Checklist
See: Pull Request Walkthrough