refactor(tasks): drop in-memory task dedup, rely on unique index
The duplicate task rows getTasksForProjects deduplicated came from the LEFT JOIN multiplying when duplicate task_positions rows existed. The new unique index on (task_id, project_view_id) removes the root cause at the SQL layer (the migration also runs before serving), so the join can no longer multiply. Revert getTasksForProjects and getRawTasksForProjects to their pre-dedup shape.
This commit is contained in:
parent
efb103ce0f
commit
822554cf30
|
|
@ -257,7 +257,7 @@ func GetTasksInBucketsForView(s *xorm.Session, view *ProjectView, projects []*Pr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ts, total, err := getRawTasksForProjects(s, projects, auth, opts)
|
ts, _, total, err := getRawTasksForProjects(s, projects, auth, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1370,7 +1370,7 @@ func (p *Project) Delete(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
|
||||||
// Delete all tasks on that project
|
// Delete all tasks on that project
|
||||||
// Using the loop to make sure all related entities to all tasks are properly deleted as well.
|
// Using the loop to make sure all related entities to all tasks are properly deleted as well.
|
||||||
tasks, _, err := getRawTasksForProjects(s, []*Project{p}, a, &taskSearchOptions{})
|
tasks, _, _, err := getRawTasksForProjects(s, []*Project{p}, a, &taskSearchOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1898,7 +1898,7 @@ func TestTaskSearchWithExpandSubtasks(t *testing.T) {
|
||||||
expand: []TaskCollectionExpandable{TaskCollectionExpandSubtasks},
|
expand: []TaskCollectionExpandable{TaskCollectionExpandSubtasks},
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks, _, err := getRawTasksForProjects(s, []*Project{project}, &user.User{ID: 15}, opts)
|
tasks, _, _, err := getRawTasksForProjects(s, []*Project{project}, &user.User{ID: 15}, opts)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, tasks)
|
require.NotEmpty(t, tasks)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ func BenchmarkTaskSearch(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
resultSlice, _, err := getRawTasksForProjects(s, projects, auth, opts)
|
resultSlice, _, _, err := getRawTasksForProjects(s, projects, auth, opts)
|
||||||
if len(resultSlice) == 0 {
|
if len(resultSlice) == 0 {
|
||||||
b.Fatalf("no results found for needle %q", needle)
|
b.Fatalf("no results found for needle %q", needle)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -288,11 +288,11 @@ func getTaskIndexFromSearchString(s string) (index int64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRawTasksForProjects(s *xorm.Session, projects []*Project, a web.Auth, opts *taskSearchOptions) (tasks []*Task, totalItems int64, err error) {
|
func getRawTasksForProjects(s *xorm.Session, projects []*Project, a web.Auth, opts *taskSearchOptions) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
||||||
|
|
||||||
// If the user does not have any projects, don't try to get any tasks
|
// If the user does not have any projects, don't try to get any tasks
|
||||||
if len(projects) == 0 {
|
if len(projects) == 0 {
|
||||||
return nil, 0, nil
|
return nil, 0, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all project IDs and get the tasks
|
// Get all project IDs and get the tasks
|
||||||
|
|
@ -324,18 +324,17 @@ func getRawTasksForProjects(s *xorm.Session, projects []*Project, a web.Auth, op
|
||||||
}
|
}
|
||||||
tasks, totalItems, err = dbSearcher.Search(opts)
|
tasks, totalItems, err = dbSearcher.Search(opts)
|
||||||
|
|
||||||
return tasks, totalItems, err
|
return tasks, len(tasks), totalItems, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTasksForProjects(s *xorm.Session, projects []*Project, a web.Auth, opts *taskSearchOptions, view *ProjectView) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
func getTasksForProjects(s *xorm.Session, projects []*Project, a web.Auth, opts *taskSearchOptions, view *ProjectView) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
||||||
tasks, totalItems, err = getRawTasksForProjects(s, projects, a, opts)
|
tasks, resultCount, totalItems, err = getRawTasksForProjects(s, projects, a, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rawTasks := tasks
|
taskMap := make(map[int64]*Task, len(tasks))
|
||||||
taskMap := make(map[int64]*Task, len(rawTasks))
|
for _, t := range tasks {
|
||||||
for _, t := range rawTasks {
|
|
||||||
taskMap[t.ID] = t
|
taskMap[t.ID] = t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,22 +343,7 @@ func getTasksForProjects(s *xorm.Session, projects []*Project, a web.Auth, opts
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// A task can appear more than once in the raw result when it has duplicate
|
return tasks, resultCount, totalItems, err
|
||||||
// task_positions rows for the view (the LEFT JOIN multiplies it). Return one
|
|
||||||
// entry per task, in the original sort order, referencing the enriched map
|
|
||||||
// value so its identifier and other data are set. totalItems already counts
|
|
||||||
// distinct tasks, so this also aligns the page size with it.
|
|
||||||
tasks = make([]*Task, 0, len(taskMap))
|
|
||||||
seen := make(map[int64]bool, len(taskMap))
|
|
||||||
for _, t := range rawTasks {
|
|
||||||
if seen[t.ID] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[t.ID] = true
|
|
||||||
tasks = append(tasks, taskMap[t.ID])
|
|
||||||
}
|
|
||||||
|
|
||||||
return tasks, len(tasks), totalItems, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTaskByIDSimple returns a raw task without extra data by the task ID
|
// GetTaskByIDSimple returns a raw task without extra data by the task ID
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue