20220305130328_database_optimizations.go (7427B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package migrations 19 20 import ( 21 "context" 22 23 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 24 "github.com/uptrace/bun" 25 ) 26 27 func init() { 28 up := func(ctx context.Context, db *bun.DB) error { 29 return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { 30 // ACCOUNTS are often selected by URI, username, or domain 31 if _, err := tx. 32 NewCreateIndex(). 33 Model(>smodel.Account{}). 34 Index("accounts_uri_idx"). 35 Column("uri"). 36 Exec(ctx); err != nil { 37 return err 38 } 39 40 if _, err := tx. 41 NewCreateIndex(). 42 Model(>smodel.Account{}). 43 Index("accounts_username_idx"). 44 Column("username"). 45 Exec(ctx); err != nil { 46 return err 47 } 48 49 if _, err := tx. 50 NewCreateIndex(). 51 Model(>smodel.Account{}). 52 Index("accounts_domain_idx"). 53 Column("domain"). 54 Exec(ctx); err != nil { 55 return err 56 } 57 58 if _, err := tx. 59 NewCreateIndex(). 60 Model(>smodel.Account{}). 61 Index("accounts_username_domain_idx"). // for selecting local accounts by username 62 Column("username", "domain"). 63 Exec(ctx); err != nil { 64 return err 65 } 66 67 // NOTIFICATIONS are commonly selected by their target_account_id 68 if _, err := tx. 69 NewCreateIndex(). 70 Model(>smodel.Notification{}). 71 Index("notifications_target_account_id_idx"). 72 Column("target_account_id"). 73 Exec(ctx); err != nil { 74 return err 75 } 76 77 // STATUSES are selected in many different ways, so we need quite few indexes 78 // to avoid queries becoming painfully slow as more statuses get stored 79 if _, err := tx. 80 NewCreateIndex(). 81 Model(>smodel.Status{}). 82 Index("statuses_uri_idx"). 83 Column("uri"). 84 Exec(ctx); err != nil { 85 return err 86 } 87 88 if _, err := tx. 89 NewCreateIndex(). 90 Model(>smodel.Status{}). 91 Index("statuses_account_id_idx"). 92 Column("account_id"). 93 Exec(ctx); err != nil { 94 return err 95 } 96 97 if _, err := tx. 98 NewCreateIndex(). 99 Model(>smodel.Status{}). 100 Index("statuses_in_reply_to_account_id_idx"). 101 Column("in_reply_to_account_id"). 102 Exec(ctx); err != nil { 103 return err 104 } 105 106 if _, err := tx. 107 NewCreateIndex(). 108 Model(>smodel.Status{}). 109 Index("statuses_boost_of_account_id_idx"). 110 Column("boost_of_account_id"). 111 Exec(ctx); err != nil { 112 return err 113 } 114 115 if _, err := tx. 116 NewCreateIndex(). 117 Model(>smodel.Status{}). 118 Index("statuses_in_reply_to_id_idx"). 119 Column("in_reply_to_id"). 120 Exec(ctx); err != nil { 121 return err 122 } 123 124 if _, err := tx. 125 NewCreateIndex(). 126 Model(>smodel.Status{}). 127 Index("statuses_boost_of_id_idx"). 128 Column("boost_of_id"). 129 Exec(ctx); err != nil { 130 return err 131 } 132 133 if _, err := tx. 134 NewCreateIndex(). 135 Model(>smodel.Status{}). 136 Index("statuses_visibility_idx"). 137 Column("visibility"). 138 Exec(ctx); err != nil { 139 return err 140 } 141 142 if _, err := tx. 143 NewCreateIndex(). 144 Model(>smodel.Status{}). 145 Index("statuses_local_idx"). 146 Column("local"). 147 Exec(ctx); err != nil { 148 return err 149 } 150 151 // DOMAIN BLOCKS are almost always selected by their domain 152 if _, err := tx. 153 NewCreateIndex(). 154 Model(>smodel.DomainBlock{}). 155 Index("domain_blocks_domain_idx"). 156 Column("domain"). 157 Exec(ctx); err != nil { 158 return err 159 } 160 161 // INSTANCES are usually selected by their domain 162 if _, err := tx. 163 NewCreateIndex(). 164 Model(>smodel.Instance{}). 165 Index("instances_domain_idx"). 166 Column("domain"). 167 Exec(ctx); err != nil { 168 return err 169 } 170 171 // STATUS FAVES are almost always selected by their target status 172 if _, err := tx. 173 NewCreateIndex(). 174 Model(>smodel.StatusFave{}). 175 Index("status_faves_status_id_idx"). 176 Column("status_id"). 177 Exec(ctx); err != nil { 178 return err 179 } 180 181 // MENTIONS are almost always selected by their originating status 182 if _, err := tx. 183 NewCreateIndex(). 184 Model(>smodel.Mention{}). 185 Index("mentions_status_id_idx"). 186 Column("status_id"). 187 Exec(ctx); err != nil { 188 return err 189 } 190 191 // FOLLOW_REQUESTS and FOLLOWS are usually selected by who they originate from, and who they target 192 if _, err := tx. 193 NewCreateIndex(). 194 Model(>smodel.FollowRequest{}). 195 Index("follow_requests_account_id_idx"). 196 Column("account_id"). 197 Exec(ctx); err != nil { 198 return err 199 } 200 201 if _, err := tx. 202 NewCreateIndex(). 203 Model(>smodel.FollowRequest{}). 204 Index("follow_requests_target_account_id_idx"). 205 Column("target_account_id"). 206 Exec(ctx); err != nil { 207 return err 208 } 209 210 if _, err := tx. 211 NewCreateIndex(). 212 Model(>smodel.Follow{}). 213 Index("follows_account_id_idx"). 214 Column("account_id"). 215 Exec(ctx); err != nil { 216 return err 217 } 218 219 if _, err := tx. 220 NewCreateIndex(). 221 Model(>smodel.Follow{}). 222 Index("follows_target_account_id_idx"). 223 Column("target_account_id"). 224 Exec(ctx); err != nil { 225 return err 226 } 227 228 // BLOCKS are usually selected simultaneously by who they originate from and who they target 229 if _, err := tx. 230 NewCreateIndex(). 231 Model(>smodel.Block{}). 232 Index("blocks_account_id_target_account_id_idx"). 233 Column("account_id", "target_account_id"). 234 Exec(ctx); err != nil { 235 return err 236 } 237 238 // MEDIA ATTACHMENTS are often selected by status ID, the account that owns the attachment 239 if _, err := tx. 240 NewCreateIndex(). 241 Model(>smodel.MediaAttachment{}). 242 Index("media_attachments_status_id_idx"). 243 Column("status_id"). 244 Exec(ctx); err != nil { 245 return err 246 } 247 248 if _, err := tx. 249 NewCreateIndex(). 250 Model(>smodel.MediaAttachment{}). 251 Index("media_attachments_account_id_idx"). 252 Column("account_id"). 253 Exec(ctx); err != nil { 254 return err 255 } 256 257 // for media cleanup jobs, attachments will be selected on a bunch of fields so make an index of this... 258 if _, err := tx. 259 NewCreateIndex(). 260 Model(>smodel.MediaAttachment{}). 261 Index("media_attachments_cleanup_idx"). 262 Column("cached", "avatar", "header", "created_at", "remote_url"). 263 Exec(ctx); err != nil { 264 return err 265 } 266 267 // TOKENS are usually selected via Access field for user-level tokens 268 if _, err := tx. 269 NewCreateIndex(). 270 Model(>smodel.Token{}). 271 Index("tokens_access_idx"). 272 Column("access"). 273 Exec(ctx); err != nil { 274 return err 275 } 276 277 return nil 278 }) 279 } 280 281 down := func(ctx context.Context, db *bun.DB) error { 282 return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { 283 return nil 284 }) 285 } 286 287 if err := Migrations.Register(up, down); err != nil { 288 panic(err) 289 } 290 }