Conversation
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
|
Hi @NobodysNightmare may I ask you for a review on this, given that you worked on the previous implementation and understand the problem scope already very well? |
658047f to
690e61e
Compare
NobodysNightmare
left a comment
There was a problem hiding this comment.
I believe to have spotted errors. Maybe they turn out to be errors in my understanding, let's see :D
Otherwise mostly questions and small improvement ideas from my side. I didn't click-test it yet.
| (projects + current.self_and_ancestors.visible.active.to_a).uniq(&:id).sort_by(&:lft) | ||
| ancestors = current.self_and_ancestors.visible.active.to_a | ||
| ancestor_children = (ancestors - [current]).flat_map do |ancestor| | ||
| Project.nearest_visible_descendants(ancestor, limit: MAX_NUMBER_OF_PROJECTS).to_a |
There was a problem hiding this comment.
Quite unfortunate that we have to loop over the ancestors here. This means one SQL query per depth of the current project.
I think we can't do anything here, but a question I have (and maybe can answer before submitting the review): Would it be possible for us to pass all the ancestors into nearest_visible_descendants to get the desired result?
There was a problem hiding this comment.
Mh, so if I understand nearest_visible_descendants correctly, it should be able to deal with multiple inputs for the "nearest visible" part. If we have an efficient equivalent to Array(boundary).flat_map(&:descendants), we'd be able to avoid the Ruby-side loop entirely.
| # Used to decide whether a node gets an expand arrow or renders as a leaf | ||
| # has_subprojects?/leaf? is not enough, as it only looks at lft/rgt and would count an archived or invisible | ||
| # descendant as "has children" too. | ||
| def with_visible_descendants(candidates) |
There was a problem hiding this comment.
🟢 I know naming is hard, but the first time I read the call to this method I thought it would return the projects together with their visible descendants until I realized that this would be the method called nearest_visible_descendants and then I was confused.
My only alternative idea would be Project.having_visible_descendants(projects). I tried to come up with more than one proposal, but I didn't like the others at all.
| def visible_active_ids_within(candidates) | ||
| min_lft = candidates.map(&:lft).min | ||
| max_rgt = candidates.map(&:rgt).max | ||
| Project.visible.active.where("lft > ? AND rgt < ?", min_lft, max_rgt).pluck(:id) |
There was a problem hiding this comment.
Can't this list of projects become uncomfortably large? From what I can see we resolve it directly into a Ruby array (pluck), so it's not going to stay a subquery in the database.
I guess there's a reason behind it, but can't we make sure to keep it as a subquery?
…lector. The initial state should only show the root level projects. Children are only loaded when the parent is expanded.
… elements within the lft/rgt ranges
Initially, the set of visible projects was in a CTE referenced twice in the query, which led to the CTE being manifested. In this case, this is iefficient as the index on lft/rgt cannot be used. But even when preventing the manifestation, the comparison within an EXIST led to the two instances of the set needing to be looped through (nested loop anti join) effectively resulting in the cross product being considered. The solution here is to rely on the awesome_nested_set's algorithm to effectively calculate whether a project has visible ancestors which can only be those preceeding in a list ordered by lft.
690e61e to
7494987
Compare
7494987 to
07c37ca
Compare
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
| end | ||
| end | ||
|
|
||
| it "respects the limit and orders by lft" do |
There was a problem hiding this comment.
Surprised that there's no rubocop-rspec rule for that: I find it surprising to have an it after a context declaration. We now have 3 its followed by a context then followed by an it.
Can we group the its together (usually at the top)?
| end | ||
|
|
||
| context "with a boundary" do | ||
| shared_let(:parent) { create(:project, name: "Parent") } |
There was a problem hiding this comment.
🟡 Considering the possible classes of errors, I'd probably also include an other_parent, just to make sure that this one doesn't pop up in the results.
There was a problem hiding this comment.
On a higher level: Theoretically we should be able to share the same hierarchy setup between the "with boundary" and "without boundary" test cases. This would essentially solve the problem as well.
|
|
||
| describe ".with_visible_descendants" do | ||
| shared_let(:parent) { create(:project, name: "Parent") } | ||
| shared_let(:child) { create(:project, name: "Child", parent:) } |
There was a problem hiding this comment.
I am surprised that we care to create child in the setup, but never check whether it could become part of the result. I think we should check that.
| end | ||
|
|
||
| context "when the only descendant is archived" do | ||
| shared_let(:archived_parent) { create(:project, name: "Archived Parent") } |
There was a problem hiding this comment.
🟡 Confusing name: The parent is not archived, its child is.
| shared_let(:archived_parent) { create(:project, name: "Archived Parent") } | |
| shared_let(:parent_of_archived) { create(:project, name: "Archived Parent") } |
Ticket
https://community.openproject.org/wp/OP-19772
What are you trying to accomplish?
Use async TreeView within the initial loading state of the project selector. The initial state should only show the root level projects. Children are only loaded when the parent is expanded.
AI involvement
Collaborative – AI generated a substantial part of the code; I reviewed and understand every line