mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-25 11:16:11 +01:00
14731a34ff
This fixes a performance bottleneck. It was discovered by Codeberg. Every where query on that table (which has grown big over time) uses this column, but there is no index on it. See this part of the log which was posted on Matrix: ``` 2023/09/10 00:52:01 ...rs/web/repo/issue.go:1446:ViewIssue() [W] [Slow SQL Query] UPDATE `issue_user` SET is_read=? WHERE uid=? AND issue_id=? [true x y] - 51.395434887s 2023/09/10 00:52:01 ...rs/web/repo/issue.go:1447:ViewIssue() [E] ReadBy: Error 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 2023/09/10 00:52:01 ...eb/routing/logger.go:102:func1() [I] router: completed GET /Codeberg/Community/issues/1201 for [::ffff:xxx]:0, 500 Internal Server Error in 52384.2ms @ repo/issue.go:1256(repo.ViewIssue) ```
97 lines
2.5 KiB
Go
97 lines
2.5 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package issues
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
)
|
|
|
|
// IssueUser represents an issue-user relation.
|
|
type IssueUser struct {
|
|
ID int64 `xorm:"pk autoincr"`
|
|
UID int64 `xorm:"INDEX"` // User ID.
|
|
IssueID int64 `xorm:"INDEX"`
|
|
IsRead bool
|
|
IsMentioned bool
|
|
}
|
|
|
|
func init() {
|
|
db.RegisterModel(new(IssueUser))
|
|
}
|
|
|
|
// NewIssueUsers inserts an issue related users
|
|
func NewIssueUsers(ctx context.Context, repo *repo_model.Repository, issue *Issue) error {
|
|
assignees, err := repo_model.GetRepoAssignees(ctx, repo)
|
|
if err != nil {
|
|
return fmt.Errorf("getAssignees: %w", err)
|
|
}
|
|
|
|
// Poster can be anyone, append later if not one of assignees.
|
|
isPosterAssignee := false
|
|
|
|
// Leave a seat for poster itself to append later, but if poster is one of assignee
|
|
// and just waste 1 unit is cheaper than re-allocate memory once.
|
|
issueUsers := make([]*IssueUser, 0, len(assignees)+1)
|
|
for _, assignee := range assignees {
|
|
issueUsers = append(issueUsers, &IssueUser{
|
|
IssueID: issue.ID,
|
|
UID: assignee.ID,
|
|
})
|
|
isPosterAssignee = isPosterAssignee || assignee.ID == issue.PosterID
|
|
}
|
|
if !isPosterAssignee {
|
|
issueUsers = append(issueUsers, &IssueUser{
|
|
IssueID: issue.ID,
|
|
UID: issue.PosterID,
|
|
})
|
|
}
|
|
|
|
return db.Insert(ctx, issueUsers)
|
|
}
|
|
|
|
// UpdateIssueUserByRead updates issue-user relation for reading.
|
|
func UpdateIssueUserByRead(ctx context.Context, uid, issueID int64) error {
|
|
_, err := db.GetEngine(ctx).Exec("UPDATE `issue_user` SET is_read=? WHERE uid=? AND issue_id=?", true, uid, issueID)
|
|
return err
|
|
}
|
|
|
|
// UpdateIssueUsersByMentions updates issue-user pairs by mentioning.
|
|
func UpdateIssueUsersByMentions(ctx context.Context, issueID int64, uids []int64) error {
|
|
for _, uid := range uids {
|
|
iu := &IssueUser{
|
|
UID: uid,
|
|
IssueID: issueID,
|
|
}
|
|
has, err := db.GetEngine(ctx).Get(iu)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iu.IsMentioned = true
|
|
if has {
|
|
_, err = db.GetEngine(ctx).ID(iu.ID).Cols("is_mentioned").Update(iu)
|
|
} else {
|
|
_, err = db.GetEngine(ctx).Insert(iu)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetIssueMentionIDs returns all mentioned user IDs of an issue.
|
|
func GetIssueMentionIDs(ctx context.Context, issueID int64) ([]int64, error) {
|
|
var ids []int64
|
|
return ids, db.GetEngine(ctx).Table(IssueUser{}).
|
|
Where("issue_id=?", issueID).
|
|
And("is_mentioned=?", true).
|
|
Select("uid").
|
|
Find(&ids)
|
|
}
|