William Grant has proposed merging lp:~wgrant/launchpad/upcoming-optimisation 
into lp:launchpad.

Commit message:
Optimise Person.getAssignedSpecificationWorkItemsDueBefore main query.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #1692120 in Launchpad itself: "Upcoming work pages appear to time out a 
lot"
  https://bugs.launchpad.net/launchpad/+bug/1692120

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/upcoming-optimisation/+merge/325141

Optimise Person.getAssignedSpecificationWorkItemsDueBefore main query.

PostgreSQL was unable to effectively estimate the number of assigned
workitems when the conditions were combined. Moving that calculation
into a CTE acts as an optimisation barrier and forces PostgreSQL to
calculate the assigned set up front, making the rest much faster.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
lp:~wgrant/launchpad/upcoming-optimisation into lp:launchpad.
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py	2017-04-22 13:09:01 +0000
+++ lib/lp/registry/model/person.py	2017-06-06 08:18:21 +0000
@@ -1434,6 +1434,32 @@
         from lp.registry.model.distribution import Distribution
         store = Store.of(self)
         WorkItem = SpecificationWorkItem
+
+        # Since a workitem's assignee defaults to its specification's
+        # assignee, the PostgreSQL planner isn't always able to work out
+        # the selectivity of the filter. Put that in a CTE to force it
+        # to calculate the workitems up front, rather than doing a hash
+        # join over all of Specification and SpecificationWorkItem.
+        assigned_specificationworkitem = With(
+            'assigned_specificationworkitem',
+            Union(
+                Select(
+                    SpecificationWorkItem.id,
+                    where=And(
+                        SpecificationWorkItem.assignee_id.is_in(
+                            self.participant_ids),
+                        Not(SpecificationWorkItem.deleted))),
+                Select(
+                    SpecificationWorkItem.id,
+                    where=And(
+                        SpecificationWorkItem.specification_id.is_in(
+                            Select(
+                                Specification.id,
+                                where=Specification._assigneeID.is_in(
+                                    self.participant_ids))),
+                        Not(SpecificationWorkItem.deleted))),
+                all=True))
+
         origin = [Specification]
         productjoin, query = get_specification_active_product_filter(self)
         origin.extend(productjoin)
@@ -1448,10 +1474,10 @@
         today = datetime.today().date()
         query.extend([
             Milestone.dateexpected <= date, Milestone.dateexpected >= today,
-            WorkItem.deleted == False,
-            Or(WorkItem.assignee_id.is_in(self.participant_ids),
-               Specification._assigneeID.is_in(self.participant_ids))])
-        result = store.using(*origin).find(WorkItem, *query)
+            WorkItem.id.is_in(Select(
+                SQL('id'), tables='assigned_specificationworkitem'))])
+        result = store.with_(assigned_specificationworkitem).using(
+            *origin).find(WorkItem, *query)
         result.config(distinct=True)
 
         def eager_load(workitems):

_______________________________________________
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to     : launchpad-reviewers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp

Reply via email to