mirror of
https://github.com/go-gitea/gitea.git
synced 2025-04-20 08:28:58 +03:00
Optimize total count of feed when loading activities in user dashboard. (#33841)
Two SQLs are very slow when `action` table have over 5M records. ``` database duration=1.8881s db.sql="SELECT created_unix DIV 900 * 900 AS timestamp, count(user_id) as contributions FROM `action` WHERE user_id=? AND act_user_id=? AND (created_unix > ?) GROUP BY timestamp ORDER BY timestamp" database duration=1.5408s db.sql="SELECT count(*) FROM `action` WHERE (user_id = ?) AND (is_deleted = ?)" ``` This will cache the count for the first loading or when the activities changed.
This commit is contained in:
@ -445,6 +445,7 @@ type GetFeedsOptions struct {
|
|||||||
OnlyPerformedBy bool // only actions performed by requested user
|
OnlyPerformedBy bool // only actions performed by requested user
|
||||||
IncludeDeleted bool // include deleted actions
|
IncludeDeleted bool // include deleted actions
|
||||||
Date string // the day we want activity for: YYYY-MM-DD
|
Date string // the day we want activity for: YYYY-MM-DD
|
||||||
|
DontCount bool // do counting in GetFeeds
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActivityReadable return whether doer can read activities of user
|
// ActivityReadable return whether doer can read activities of user
|
||||||
|
@ -243,7 +243,11 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
|
|||||||
sess := db.GetEngine(ctx).Where(cond)
|
sess := db.GetEngine(ctx).Where(cond)
|
||||||
sess = db.SetSessionPagination(sess, &opts)
|
sess = db.SetSessionPagination(sess, &opts)
|
||||||
|
|
||||||
count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
|
if opts.DontCount {
|
||||||
|
err = sess.Desc("`action`.created_unix").Find(&actions)
|
||||||
|
} else {
|
||||||
|
count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
|
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
|
||||||
}
|
}
|
||||||
@ -257,11 +261,13 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
|
|||||||
return nil, 0, fmt.Errorf("Find(actionsIDs): %w", err)
|
return nil, 0, fmt.Errorf("Find(actionsIDs): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err = db.GetEngine(ctx).Where(cond).
|
if !opts.DontCount {
|
||||||
Table("action").
|
count, err = db.GetEngine(ctx).Where(cond).
|
||||||
Cols("`action`.id").Count()
|
Table("action").
|
||||||
if err != nil {
|
Cols("`action`.id").Count()
|
||||||
return nil, 0, fmt.Errorf("Count: %w", err)
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("Count: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.GetEngine(ctx).In("`action`.id", actionIDs).Desc("`action`.created_unix").Find(&actions); err != nil {
|
if err := db.GetEngine(ctx).In("`action`.id", actionIDs).Desc("`action`.created_unix").Find(&actions); err != nil {
|
||||||
@ -275,3 +281,9 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
|
|||||||
|
|
||||||
return actions, count, nil
|
return actions, count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CountUserFeeds(ctx context.Context, userID int64) (int64, error) {
|
||||||
|
return db.GetEngine(ctx).Where("user_id = ?", userID).
|
||||||
|
And("is_deleted = ?", false).
|
||||||
|
Count(&Action{})
|
||||||
|
}
|
||||||
|
@ -119,7 +119,7 @@ func Dashboard(ctx *context.Context) {
|
|||||||
ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
|
ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
feeds, count, err := feed_service.GetFeeds(ctx, activities_model.GetFeedsOptions{
|
feeds, count, err := feed_service.GetFeedsForDashboard(ctx, activities_model.GetFeedsOptions{
|
||||||
RequestedUser: ctxUser,
|
RequestedUser: ctxUser,
|
||||||
RequestedTeam: ctx.Org.Team,
|
RequestedTeam: ctx.Org.Team,
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
|
@ -13,9 +13,28 @@ import (
|
|||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/cache"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func userFeedCacheKey(userID int64) string {
|
||||||
|
return fmt.Sprintf("user_feed_%d", userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFeedsForDashboard(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int64, error) {
|
||||||
|
opts.DontCount = opts.RequestedTeam == nil && opts.Date == ""
|
||||||
|
results, cnt, err := activities_model.GetFeeds(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if opts.DontCount {
|
||||||
|
cnt, err = cache.GetInt64(userFeedCacheKey(opts.Actor.ID), func() (int64, error) {
|
||||||
|
return activities_model.CountUserFeeds(ctx, opts.Actor.ID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return results, cnt, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetFeeds returns actions according to the provided options
|
// GetFeeds returns actions according to the provided options
|
||||||
func GetFeeds(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int64, error) {
|
func GetFeeds(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int64, error) {
|
||||||
return activities_model.GetFeeds(ctx, opts)
|
return activities_model.GetFeeds(ctx, opts)
|
||||||
@ -68,6 +87,13 @@ func notifyWatchers(ctx context.Context, act *activities_model.Action, watchers
|
|||||||
if err := db.Insert(ctx, act); err != nil {
|
if err := db.Insert(ctx, act); err != nil {
|
||||||
return fmt.Errorf("insert new action: %w", err)
|
return fmt.Errorf("insert new action: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
total, err := activities_model.CountUserFeeds(ctx, act.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("count user feeds: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = cache.GetCache().Put(userFeedCacheKey(act.UserID), fmt.Sprintf("%d", total), setting.CacheService.TTLSeconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
Reference in New Issue
Block a user