From 5d653cc10d49350fb6f85e787b98142cdce8b5df Mon Sep 17 00:00:00 2001
From: zeripath <art27@cantab.net>
Date: Sat, 18 Jun 2022 09:46:50 +0100
Subject: [PATCH] Improve action table indices (#19472)

---
 models/action.go                | 26 +++++++++----
 models/migrations/migrations.go |  2 +
 models/migrations/v216.go       | 67 +++++++++++++++++++++++++++++++++
 3 files changed, 88 insertions(+), 7 deletions(-)
 create mode 100644 models/migrations/v216.go

diff --git a/models/action.go b/models/action.go
index 951328070d..78cc93e1a2 100644
--- a/models/action.go
+++ b/models/action.go
@@ -30,6 +30,7 @@ import (
 	"code.gitea.io/gitea/modules/util"
 
 	"xorm.io/builder"
+	"xorm.io/xorm/schemas"
 )
 
 // ActionType represents the type of an action.
@@ -70,25 +71,36 @@ const (
 // used in template render.
 type Action struct {
 	ID          int64 `xorm:"pk autoincr"`
-	UserID      int64 `xorm:"INDEX"` // Receiver user id.
+	UserID      int64 // Receiver user id.
 	OpType      ActionType
-	ActUserID   int64                  `xorm:"INDEX"` // Action user id.
-	ActUser     *user_model.User       `xorm:"-"`
-	RepoID      int64                  `xorm:"INDEX"`
+	ActUserID   int64            // Action user id.
+	ActUser     *user_model.User `xorm:"-"`
+	RepoID      int64
 	Repo        *repo_model.Repository `xorm:"-"`
 	CommentID   int64                  `xorm:"INDEX"`
 	Comment     *issues_model.Comment  `xorm:"-"`
-	IsDeleted   bool                   `xorm:"INDEX NOT NULL DEFAULT false"`
+	IsDeleted   bool                   `xorm:"NOT NULL DEFAULT false"`
 	RefName     string
-	IsPrivate   bool               `xorm:"INDEX NOT NULL DEFAULT false"`
+	IsPrivate   bool               `xorm:"NOT NULL DEFAULT false"`
 	Content     string             `xorm:"TEXT"`
-	CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+	CreatedUnix timeutil.TimeStamp `xorm:"created"`
 }
 
 func init() {
 	db.RegisterModel(new(Action))
 }
 
+// TableIndices implements xorm's TableIndices interface
+func (a *Action) TableIndices() []*schemas.Index {
+	actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
+	actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
+
+	repoIndex := schemas.NewIndex("r_c_u_d", schemas.IndexType)
+	repoIndex.AddColumn("repo_id", "created_unix", "user_id", "is_deleted")
+
+	return []*schemas.Index{actUserIndex, repoIndex}
+}
+
 // GetOpType gets the ActionType of this action.
 func (a *Action) GetOpType() ActionType {
 	return a.OpType
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 1bed4e2bc0..c843e33eb7 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -387,6 +387,8 @@ var migrations = []Migration{
 	NewMigration("Add auto merge table", addAutoMergeTable),
 	// v215 -> v216
 	NewMigration("allow to view files in PRs", addReviewViewedFiles),
+	// v216 -> v217
+	NewMigration("Improve Action table indices", improveActionTableIndices),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v216.go b/models/migrations/v216.go
new file mode 100644
index 0000000000..b011c11d95
--- /dev/null
+++ b/models/migrations/v216.go
@@ -0,0 +1,67 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"code.gitea.io/gitea/modules/timeutil"
+
+	"xorm.io/xorm"
+	"xorm.io/xorm/schemas"
+)
+
+type improveActionTableIndicesAction struct {
+	ID          int64 `xorm:"pk autoincr"`
+	UserID      int64 // Receiver user id.
+	OpType      int
+	ActUserID   int64 // Action user id.
+	RepoID      int64
+	CommentID   int64 `xorm:"INDEX"`
+	IsDeleted   bool  `xorm:"NOT NULL DEFAULT false"`
+	RefName     string
+	IsPrivate   bool               `xorm:"NOT NULL DEFAULT false"`
+	Content     string             `xorm:"TEXT"`
+	CreatedUnix timeutil.TimeStamp `xorm:"created"`
+}
+
+// TableName sets the name of this table
+func (a *improveActionTableIndicesAction) TableName() string {
+	return "action"
+}
+
+// TableIndices implements xorm's TableIndices interface
+func (a *improveActionTableIndicesAction) TableIndices() []*schemas.Index {
+	actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
+	actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
+
+	repoIndex := schemas.NewIndex("r_c_u_d", schemas.IndexType)
+	repoIndex.AddColumn("repo_id", "created_unix", "user_id", "is_deleted")
+
+	return []*schemas.Index{actUserIndex, repoIndex}
+}
+
+func improveActionTableIndices(x *xorm.Engine) error {
+	{
+		type Action struct {
+			ID          int64 `xorm:"pk autoincr"`
+			UserID      int64 `xorm:"INDEX"` // Receiver user id.
+			OpType      int
+			ActUserID   int64 `xorm:"INDEX"` // Action user id.
+			RepoID      int64 `xorm:"INDEX"`
+			CommentID   int64 `xorm:"INDEX"`
+			IsDeleted   bool  `xorm:"INDEX NOT NULL DEFAULT false"`
+			RefName     string
+			IsPrivate   bool               `xorm:"INDEX NOT NULL DEFAULT false"`
+			Content     string             `xorm:"TEXT"`
+			CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+		}
+		if err := x.Sync2(&Action{}); err != nil {
+			return err
+		}
+		if err := x.DropIndexes(&Action{}); err != nil {
+			return err
+		}
+	}
+	return x.Sync2(&improveActionTableIndicesAction{})
+}