Skip to content

Commit 4a7ab0a

Browse files
authored
Optimize heatmap query (#33853)
When there are over 5M records on `action` table, the heatmap on dashboard is very slow as below SQL. ``` 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" ``` This PR add a new index for `action` table with columns `user_id`, `act_user_id` and `created_unix` so that this query will become about 6 times faster than before.
1 parent 5407382 commit 4a7ab0a

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

Diff for: models/activities/action.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,10 @@ func (a *Action) TableIndices() []*schemas.Index {
172172
cuIndex := schemas.NewIndex("c_u", schemas.IndexType)
173173
cuIndex.AddColumn("user_id", "is_deleted")
174174

175-
indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex}
175+
actUserUserIndex := schemas.NewIndex("au_c_u", schemas.IndexType)
176+
actUserUserIndex.AddColumn("act_user_id", "created_unix", "user_id")
177+
178+
indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex, actUserUserIndex}
176179

177180
return indices
178181
}

Diff for: models/migrations/migrations.go

+1
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ func prepareMigrationTasks() []*migration {
377377
newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables),
378378
newMigration(315, "Add Ephemeral to ActionRunner", v1_24.AddEphemeralToActionRunner),
379379
newMigration(316, "Add description for secrets and variables", v1_24.AddDescriptionForSecretsAndVariables),
380+
newMigration(317, "Add new index for action for heatmap", v1_24.AddNewIndexForUserDashboard),
380381
}
381382
return preparedMigrations
382383
}

Diff for: models/migrations/v1_24/v317.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_24 //nolint
5+
6+
import (
7+
"code.gitea.io/gitea/modules/timeutil"
8+
9+
"xorm.io/xorm"
10+
"xorm.io/xorm/schemas"
11+
)
12+
13+
type improveActionTableIndicesAction struct {
14+
ID int64 `xorm:"pk autoincr"`
15+
UserID int64 `xorm:"INDEX"` // Receiver user id.
16+
OpType int
17+
ActUserID int64 // Action user id.
18+
RepoID int64
19+
CommentID int64 `xorm:"INDEX"`
20+
IsDeleted bool `xorm:"NOT NULL DEFAULT false"`
21+
RefName string
22+
IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
23+
Content string `xorm:"TEXT"`
24+
CreatedUnix timeutil.TimeStamp `xorm:"created"`
25+
}
26+
27+
// TableName sets the name of this table
28+
func (*improveActionTableIndicesAction) TableName() string {
29+
return "action"
30+
}
31+
32+
// TableIndices implements xorm's TableIndices interface
33+
func (a *improveActionTableIndicesAction) TableIndices() []*schemas.Index {
34+
repoIndex := schemas.NewIndex("r_u_d", schemas.IndexType)
35+
repoIndex.AddColumn("repo_id", "user_id", "is_deleted")
36+
37+
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
38+
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
39+
40+
cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
41+
cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
42+
43+
cuIndex := schemas.NewIndex("c_u", schemas.IndexType)
44+
cuIndex.AddColumn("user_id", "is_deleted")
45+
46+
actUserUserIndex := schemas.NewIndex("au_c_u", schemas.IndexType)
47+
actUserUserIndex.AddColumn("act_user_id", "created_unix", "user_id")
48+
49+
indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex, actUserUserIndex}
50+
51+
return indices
52+
}
53+
54+
func AddNewIndexForUserDashboard(x *xorm.Engine) error {
55+
return x.Sync(new(improveActionTableIndicesAction))
56+
}

0 commit comments

Comments
 (0)