From fe27dd59ad7f22eeaf0fa38b3a1d8e32ee50f3a3 Mon Sep 17 00:00:00 2001 From: kolaente Date: Sun, 3 Mar 2024 15:31:42 +0100 Subject: [PATCH] feat(subscription): use a recursive cte to fetch subscriptions of parent projects Testing this locally resulted in improved response times from ~50ms to ~20ms when creating a project. It looks like even though the code running these sql queries uses different go routines, they affect each other (caused by IO or context switching?) --- pkg/models/project.go | 55 ++++++++++++++------------------------ pkg/models/subscription.go | 14 +++++----- 2 files changed, 26 insertions(+), 43 deletions(-) diff --git a/pkg/models/project.go b/pkg/models/project.go index 8a74130c3..7958cbef5 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -540,19 +540,24 @@ func getSavedFilterProjects(s *xorm.Session, doer *user.User) (savedFiltersProje } // GetAllParentProjects returns all parents of a given project -func (p *Project) GetAllParentProjects(s *xorm.Session) (err error) { - if p.ParentProjectID == 0 { - return - } - - parent, err := GetProjectSimpleByID(s, p.ParentProjectID) - if err != nil { - return err - } - - p.ParentProject = parent - - return parent.GetAllParentProjects(s) +func GetAllParentProjects(s *xorm.Session, projectID int64) (allProjects map[int64]*Project, err error) { + allProjects = make(map[int64]*Project) + err = s.SQL(`WITH RECURSIVE all_projects AS ( + SELECT + p.* + FROM + projects p + WHERE + p.id = ? + UNION ALL + SELECT + p.* + FROM + projects p + INNER JOIN all_projects pc ON p.ID = pc.parent_project_id + ) + SELECT DISTINCT * FROM all_projects`, projectID).Find(&allProjects) + return } // addProjectDetails adds owner user objects and project tasks to all projects in the slice @@ -667,29 +672,9 @@ func checkProjectBeforeUpdateOrDelete(s *xorm.Session, project *Project) (err er } } - allProjects := make(map[int64]*Project) - err = s.SQL(`WITH RECURSIVE all_projects AS ( - SELECT - p.id, - p.parent_project_id - FROM - projects p - WHERE - p.id = ? - UNION ALL - SELECT - p.id, - p.parent_project_id - FROM - projects p - INNER JOIN all_projects pc ON p.ID = pc.parent_project_id - ) - SELECT - * - FROM - all_projects`, project.ParentProjectID).Find(&allProjects) + allProjects, err := GetAllParentProjects(s, project.ParentProjectID) if err != nil { - return + return err } var parent *Project diff --git a/pkg/models/subscription.go b/pkg/models/subscription.go index 020978b90..023956f36 100644 --- a/pkg/models/subscription.go +++ b/pkg/models/subscription.go @@ -301,20 +301,18 @@ func GetSubscriptionsForProjects(s *xorm.Session, projects []*Project, a web.Aut continue } - err = ps[p.ID].GetAllParentProjects(s) + parents, err := GetAllParentProjects(s, p.ID) if err != nil { return nil, err } - parentIDs := []int64{} - var parent = ps[p.ID].ParentProject + // Walk the tree up until we reach the top + var parent = parents[p.ParentProjectID] // parent now has a pointer… + ps[p.ID].ParentProject = parents[p.ParentProjectID] for parent != nil { - parentIDs = append(parentIDs, parent.ID) - parent = parent.ParentProject + allProjectIDs = append(allProjectIDs, parent.ID) + parent = parents[parent.ParentProjectID] // … which means we can update it here and then update the pointer in the map } - - // Now we have all parent ids - allProjectIDs = append(allProjectIDs, parentIDs...) // the child project id is already in there } var subscriptions []*Subscription