gts.go (14120B)
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 cache 19 20 import ( 21 "codeberg.org/gruf/go-cache/v3/result" 22 "codeberg.org/gruf/go-cache/v3/ttl" 23 "github.com/superseriousbusiness/gotosocial/internal/cache/domain" 24 "github.com/superseriousbusiness/gotosocial/internal/config" 25 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 26 ) 27 28 type GTSCaches struct { 29 account *result.Cache[*gtsmodel.Account] 30 block *result.Cache[*gtsmodel.Block] 31 // TODO: maybe should be moved out of here since it's 32 // not actually doing anything with gtsmodel.DomainBlock. 33 domainBlock *domain.BlockCache 34 emoji *result.Cache[*gtsmodel.Emoji] 35 emojiCategory *result.Cache[*gtsmodel.EmojiCategory] 36 follow *result.Cache[*gtsmodel.Follow] 37 followRequest *result.Cache[*gtsmodel.FollowRequest] 38 list *result.Cache[*gtsmodel.List] 39 listEntry *result.Cache[*gtsmodel.ListEntry] 40 media *result.Cache[*gtsmodel.MediaAttachment] 41 mention *result.Cache[*gtsmodel.Mention] 42 notification *result.Cache[*gtsmodel.Notification] 43 report *result.Cache[*gtsmodel.Report] 44 status *result.Cache[*gtsmodel.Status] 45 statusFave *result.Cache[*gtsmodel.StatusFave] 46 tombstone *result.Cache[*gtsmodel.Tombstone] 47 user *result.Cache[*gtsmodel.User] 48 // TODO: move out of GTS caches since not using database models. 49 webfinger *ttl.Cache[string, string] 50 } 51 52 // Init will initialize all the gtsmodel caches in this collection. 53 // NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe. 54 func (c *GTSCaches) Init() { 55 c.initAccount() 56 c.initBlock() 57 c.initDomainBlock() 58 c.initEmoji() 59 c.initEmojiCategory() 60 c.initFollow() 61 c.initFollowRequest() 62 c.initList() 63 c.initListEntry() 64 c.initMedia() 65 c.initMention() 66 c.initNotification() 67 c.initReport() 68 c.initStatus() 69 c.initStatusFave() 70 c.initTombstone() 71 c.initUser() 72 c.initWebfinger() 73 } 74 75 // Start will attempt to start all of the gtsmodel caches, or panic. 76 func (c *GTSCaches) Start() { 77 tryStart(c.account, config.GetCacheGTSAccountSweepFreq()) 78 tryStart(c.block, config.GetCacheGTSBlockSweepFreq()) 79 tryStart(c.emoji, config.GetCacheGTSEmojiSweepFreq()) 80 tryStart(c.emojiCategory, config.GetCacheGTSEmojiCategorySweepFreq()) 81 tryStart(c.follow, config.GetCacheGTSFollowSweepFreq()) 82 tryStart(c.followRequest, config.GetCacheGTSFollowRequestSweepFreq()) 83 tryStart(c.list, config.GetCacheGTSListSweepFreq()) 84 tryStart(c.listEntry, config.GetCacheGTSListEntrySweepFreq()) 85 tryStart(c.media, config.GetCacheGTSMediaSweepFreq()) 86 tryStart(c.mention, config.GetCacheGTSMentionSweepFreq()) 87 tryStart(c.notification, config.GetCacheGTSNotificationSweepFreq()) 88 tryStart(c.report, config.GetCacheGTSReportSweepFreq()) 89 tryStart(c.status, config.GetCacheGTSStatusSweepFreq()) 90 tryStart(c.statusFave, config.GetCacheGTSStatusFaveSweepFreq()) 91 tryStart(c.tombstone, config.GetCacheGTSTombstoneSweepFreq()) 92 tryStart(c.user, config.GetCacheGTSUserSweepFreq()) 93 tryUntil("starting *gtsmodel.Webfinger cache", 5, func() bool { 94 if sweep := config.GetCacheGTSWebfingerSweepFreq(); sweep > 0 { 95 return c.webfinger.Start(sweep) 96 } 97 return true 98 }) 99 } 100 101 // Stop will attempt to stop all of the gtsmodel caches, or panic. 102 func (c *GTSCaches) Stop() { 103 tryStop(c.account, config.GetCacheGTSAccountSweepFreq()) 104 tryStop(c.block, config.GetCacheGTSBlockSweepFreq()) 105 tryStop(c.emoji, config.GetCacheGTSEmojiSweepFreq()) 106 tryStop(c.emojiCategory, config.GetCacheGTSEmojiCategorySweepFreq()) 107 tryStop(c.follow, config.GetCacheGTSFollowSweepFreq()) 108 tryStop(c.followRequest, config.GetCacheGTSFollowRequestSweepFreq()) 109 tryStop(c.list, config.GetCacheGTSListSweepFreq()) 110 tryStop(c.listEntry, config.GetCacheGTSListEntrySweepFreq()) 111 tryStop(c.media, config.GetCacheGTSMediaSweepFreq()) 112 tryStop(c.mention, config.GetCacheGTSNotificationSweepFreq()) 113 tryStop(c.notification, config.GetCacheGTSNotificationSweepFreq()) 114 tryStop(c.report, config.GetCacheGTSReportSweepFreq()) 115 tryStop(c.status, config.GetCacheGTSStatusSweepFreq()) 116 tryStop(c.statusFave, config.GetCacheGTSStatusFaveSweepFreq()) 117 tryStop(c.tombstone, config.GetCacheGTSTombstoneSweepFreq()) 118 tryStop(c.user, config.GetCacheGTSUserSweepFreq()) 119 tryUntil("stopping *gtsmodel.Webfinger cache", 5, c.webfinger.Stop) 120 } 121 122 // Account provides access to the gtsmodel Account database cache. 123 func (c *GTSCaches) Account() *result.Cache[*gtsmodel.Account] { 124 return c.account 125 } 126 127 // Block provides access to the gtsmodel Block (account) database cache. 128 func (c *GTSCaches) Block() *result.Cache[*gtsmodel.Block] { 129 return c.block 130 } 131 132 // DomainBlock provides access to the domain block database cache. 133 func (c *GTSCaches) DomainBlock() *domain.BlockCache { 134 return c.domainBlock 135 } 136 137 // Emoji provides access to the gtsmodel Emoji database cache. 138 func (c *GTSCaches) Emoji() *result.Cache[*gtsmodel.Emoji] { 139 return c.emoji 140 } 141 142 // EmojiCategory provides access to the gtsmodel EmojiCategory database cache. 143 func (c *GTSCaches) EmojiCategory() *result.Cache[*gtsmodel.EmojiCategory] { 144 return c.emojiCategory 145 } 146 147 // Follow provides access to the gtsmodel Follow database cache. 148 func (c *GTSCaches) Follow() *result.Cache[*gtsmodel.Follow] { 149 return c.follow 150 } 151 152 // FollowRequest provides access to the gtsmodel FollowRequest database cache. 153 func (c *GTSCaches) FollowRequest() *result.Cache[*gtsmodel.FollowRequest] { 154 return c.followRequest 155 } 156 157 // List provides access to the gtsmodel List database cache. 158 func (c *GTSCaches) List() *result.Cache[*gtsmodel.List] { 159 return c.list 160 } 161 162 // ListEntry provides access to the gtsmodel ListEntry database cache. 163 func (c *GTSCaches) ListEntry() *result.Cache[*gtsmodel.ListEntry] { 164 return c.listEntry 165 } 166 167 // Media provides access to the gtsmodel Media database cache. 168 func (c *GTSCaches) Media() *result.Cache[*gtsmodel.MediaAttachment] { 169 return c.media 170 } 171 172 // Mention provides access to the gtsmodel Mention database cache. 173 func (c *GTSCaches) Mention() *result.Cache[*gtsmodel.Mention] { 174 return c.mention 175 } 176 177 // Notification provides access to the gtsmodel Notification database cache. 178 func (c *GTSCaches) Notification() *result.Cache[*gtsmodel.Notification] { 179 return c.notification 180 } 181 182 // Report provides access to the gtsmodel Report database cache. 183 func (c *GTSCaches) Report() *result.Cache[*gtsmodel.Report] { 184 return c.report 185 } 186 187 // Status provides access to the gtsmodel Status database cache. 188 func (c *GTSCaches) Status() *result.Cache[*gtsmodel.Status] { 189 return c.status 190 } 191 192 // StatusFave provides access to the gtsmodel StatusFave database cache. 193 func (c *GTSCaches) StatusFave() *result.Cache[*gtsmodel.StatusFave] { 194 return c.statusFave 195 } 196 197 // Tombstone provides access to the gtsmodel Tombstone database cache. 198 func (c *GTSCaches) Tombstone() *result.Cache[*gtsmodel.Tombstone] { 199 return c.tombstone 200 } 201 202 // User provides access to the gtsmodel User database cache. 203 func (c *GTSCaches) User() *result.Cache[*gtsmodel.User] { 204 return c.user 205 } 206 207 // Webfinger provides access to the webfinger URL cache. 208 func (c *GTSCaches) Webfinger() *ttl.Cache[string, string] { 209 return c.webfinger 210 } 211 212 func (c *GTSCaches) initAccount() { 213 c.account = result.New([]result.Lookup{ 214 {Name: "ID"}, 215 {Name: "URI"}, 216 {Name: "URL"}, 217 {Name: "Username.Domain"}, 218 {Name: "PublicKeyURI"}, 219 {Name: "InboxURI"}, 220 {Name: "OutboxURI"}, 221 {Name: "FollowersURI"}, 222 {Name: "FollowingURI"}, 223 }, func(a1 *gtsmodel.Account) *gtsmodel.Account { 224 a2 := new(gtsmodel.Account) 225 *a2 = *a1 226 return a2 227 }, config.GetCacheGTSAccountMaxSize()) 228 c.account.SetTTL(config.GetCacheGTSAccountTTL(), true) 229 c.account.IgnoreErrors(ignoreErrors) 230 } 231 232 func (c *GTSCaches) initBlock() { 233 c.block = result.New([]result.Lookup{ 234 {Name: "ID"}, 235 {Name: "URI"}, 236 {Name: "AccountID.TargetAccountID"}, 237 }, func(b1 *gtsmodel.Block) *gtsmodel.Block { 238 b2 := new(gtsmodel.Block) 239 *b2 = *b1 240 return b2 241 }, config.GetCacheGTSBlockMaxSize()) 242 c.block.SetTTL(config.GetCacheGTSBlockTTL(), true) 243 c.block.IgnoreErrors(ignoreErrors) 244 } 245 246 func (c *GTSCaches) initDomainBlock() { 247 c.domainBlock = new(domain.BlockCache) 248 } 249 250 func (c *GTSCaches) initEmoji() { 251 c.emoji = result.New([]result.Lookup{ 252 {Name: "ID"}, 253 {Name: "URI"}, 254 {Name: "Shortcode.Domain"}, 255 {Name: "ImageStaticURL"}, 256 }, func(e1 *gtsmodel.Emoji) *gtsmodel.Emoji { 257 e2 := new(gtsmodel.Emoji) 258 *e2 = *e1 259 return e2 260 }, config.GetCacheGTSEmojiMaxSize()) 261 c.emoji.SetTTL(config.GetCacheGTSEmojiTTL(), true) 262 c.emoji.IgnoreErrors(ignoreErrors) 263 } 264 265 func (c *GTSCaches) initEmojiCategory() { 266 c.emojiCategory = result.New([]result.Lookup{ 267 {Name: "ID"}, 268 {Name: "Name"}, 269 }, func(c1 *gtsmodel.EmojiCategory) *gtsmodel.EmojiCategory { 270 c2 := new(gtsmodel.EmojiCategory) 271 *c2 = *c1 272 return c2 273 }, config.GetCacheGTSEmojiCategoryMaxSize()) 274 c.emojiCategory.SetTTL(config.GetCacheGTSEmojiCategoryTTL(), true) 275 c.emojiCategory.IgnoreErrors(ignoreErrors) 276 } 277 278 func (c *GTSCaches) initFollow() { 279 c.follow = result.New([]result.Lookup{ 280 {Name: "ID"}, 281 {Name: "URI"}, 282 {Name: "AccountID.TargetAccountID"}, 283 }, func(f1 *gtsmodel.Follow) *gtsmodel.Follow { 284 f2 := new(gtsmodel.Follow) 285 *f2 = *f1 286 return f2 287 }, config.GetCacheGTSFollowMaxSize()) 288 c.follow.SetTTL(config.GetCacheGTSFollowTTL(), true) 289 } 290 291 func (c *GTSCaches) initFollowRequest() { 292 c.followRequest = result.New([]result.Lookup{ 293 {Name: "ID"}, 294 {Name: "URI"}, 295 {Name: "AccountID.TargetAccountID"}, 296 }, func(f1 *gtsmodel.FollowRequest) *gtsmodel.FollowRequest { 297 f2 := new(gtsmodel.FollowRequest) 298 *f2 = *f1 299 return f2 300 }, config.GetCacheGTSFollowRequestMaxSize()) 301 c.followRequest.SetTTL(config.GetCacheGTSFollowRequestTTL(), true) 302 } 303 304 func (c *GTSCaches) initList() { 305 c.list = result.New([]result.Lookup{ 306 {Name: "ID"}, 307 }, func(l1 *gtsmodel.List) *gtsmodel.List { 308 l2 := new(gtsmodel.List) 309 *l2 = *l1 310 return l2 311 }, config.GetCacheGTSListMaxSize()) 312 c.list.SetTTL(config.GetCacheGTSListTTL(), true) 313 c.list.IgnoreErrors(ignoreErrors) 314 } 315 316 func (c *GTSCaches) initListEntry() { 317 c.listEntry = result.New([]result.Lookup{ 318 {Name: "ID"}, 319 }, func(l1 *gtsmodel.ListEntry) *gtsmodel.ListEntry { 320 l2 := new(gtsmodel.ListEntry) 321 *l2 = *l1 322 return l2 323 }, config.GetCacheGTSListEntryMaxSize()) 324 c.list.SetTTL(config.GetCacheGTSListEntryTTL(), true) 325 c.list.IgnoreErrors(ignoreErrors) 326 } 327 328 func (c *GTSCaches) initMedia() { 329 c.media = result.New([]result.Lookup{ 330 {Name: "ID"}, 331 }, func(m1 *gtsmodel.MediaAttachment) *gtsmodel.MediaAttachment { 332 m2 := new(gtsmodel.MediaAttachment) 333 *m2 = *m1 334 return m2 335 }, config.GetCacheGTSMediaMaxSize()) 336 c.media.SetTTL(config.GetCacheGTSMediaTTL(), true) 337 c.media.IgnoreErrors(ignoreErrors) 338 } 339 340 func (c *GTSCaches) initMention() { 341 c.mention = result.New([]result.Lookup{ 342 {Name: "ID"}, 343 }, func(m1 *gtsmodel.Mention) *gtsmodel.Mention { 344 m2 := new(gtsmodel.Mention) 345 *m2 = *m1 346 return m2 347 }, config.GetCacheGTSMentionMaxSize()) 348 c.mention.SetTTL(config.GetCacheGTSMentionTTL(), true) 349 c.mention.IgnoreErrors(ignoreErrors) 350 } 351 352 func (c *GTSCaches) initNotification() { 353 c.notification = result.New([]result.Lookup{ 354 {Name: "ID"}, 355 {Name: "NotificationType.TargetAccountID.OriginAccountID.StatusID"}, 356 }, func(n1 *gtsmodel.Notification) *gtsmodel.Notification { 357 n2 := new(gtsmodel.Notification) 358 *n2 = *n1 359 return n2 360 }, config.GetCacheGTSNotificationMaxSize()) 361 c.notification.SetTTL(config.GetCacheGTSNotificationTTL(), true) 362 c.notification.IgnoreErrors(ignoreErrors) 363 } 364 365 func (c *GTSCaches) initReport() { 366 c.report = result.New([]result.Lookup{ 367 {Name: "ID"}, 368 }, func(r1 *gtsmodel.Report) *gtsmodel.Report { 369 r2 := new(gtsmodel.Report) 370 *r2 = *r1 371 return r2 372 }, config.GetCacheGTSReportMaxSize()) 373 c.report.SetTTL(config.GetCacheGTSReportTTL(), true) 374 c.report.IgnoreErrors(ignoreErrors) 375 } 376 377 func (c *GTSCaches) initStatus() { 378 c.status = result.New([]result.Lookup{ 379 {Name: "ID"}, 380 {Name: "URI"}, 381 {Name: "URL"}, 382 }, func(s1 *gtsmodel.Status) *gtsmodel.Status { 383 s2 := new(gtsmodel.Status) 384 *s2 = *s1 385 return s2 386 }, config.GetCacheGTSStatusMaxSize()) 387 c.status.SetTTL(config.GetCacheGTSStatusTTL(), true) 388 c.status.IgnoreErrors(ignoreErrors) 389 } 390 391 func (c *GTSCaches) initStatusFave() { 392 c.statusFave = result.New([]result.Lookup{ 393 {Name: "ID"}, 394 {Name: "AccountID.StatusID"}, 395 }, func(f1 *gtsmodel.StatusFave) *gtsmodel.StatusFave { 396 f2 := new(gtsmodel.StatusFave) 397 *f2 = *f1 398 return f2 399 }, config.GetCacheGTSStatusFaveMaxSize()) 400 c.status.SetTTL(config.GetCacheGTSStatusFaveTTL(), true) 401 c.status.IgnoreErrors(ignoreErrors) 402 } 403 404 func (c *GTSCaches) initTombstone() { 405 c.tombstone = result.New([]result.Lookup{ 406 {Name: "ID"}, 407 {Name: "URI"}, 408 }, func(t1 *gtsmodel.Tombstone) *gtsmodel.Tombstone { 409 t2 := new(gtsmodel.Tombstone) 410 *t2 = *t1 411 return t2 412 }, config.GetCacheGTSTombstoneMaxSize()) 413 c.tombstone.SetTTL(config.GetCacheGTSTombstoneTTL(), true) 414 c.tombstone.IgnoreErrors(ignoreErrors) 415 } 416 417 func (c *GTSCaches) initUser() { 418 c.user = result.New([]result.Lookup{ 419 {Name: "ID"}, 420 {Name: "AccountID"}, 421 {Name: "Email"}, 422 {Name: "ConfirmationToken"}, 423 {Name: "ExternalID"}, 424 }, func(u1 *gtsmodel.User) *gtsmodel.User { 425 u2 := new(gtsmodel.User) 426 *u2 = *u1 427 return u2 428 }, config.GetCacheGTSUserMaxSize()) 429 c.user.SetTTL(config.GetCacheGTSUserTTL(), true) 430 c.user.IgnoreErrors(ignoreErrors) 431 } 432 433 func (c *GTSCaches) initWebfinger() { 434 c.webfinger = ttl.New[string, string]( 435 0, 436 config.GetCacheGTSWebfingerMaxSize(), 437 config.GetCacheGTSWebfingerTTL()) 438 }