mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility * invalidate visibility cache items on account / status deletes * fix requester ID passed to visibility cache nil ptr * de-interface caches, fix home / public timeline caching + visibility * finish adding code comments for visibility filter * fix angry goconst linter warnings * actually finish adding filter visibility code comments for timeline functions * move home timeline status author check to after visibility * remove now-unused code * add more code comments * add TODO code comment, update printed cache start names * update printed cache names on stop * start adding separate follow(request) delete db functions, add specific visibility cache tests * add relationship type caching * fix getting local account follows / followed-bys, other small codebase improvements * simplify invalidation using cache hooks, add more GetAccountBy___() functions * fix boosting to return 404 if not boostable but no error (to not leak status ID) * remove dead code * improved placement of cache invalidation * update license headers * add example follow, follow-request config entries * add example visibility cache configuration to config file * use specific PutFollowRequest() instead of just Put() * add tests for all GetAccountBy() * add GetBlockBy() tests * update block to check primitive fields * update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests * fix copy-pasted code * update envparsing test * whitespace * fix bun struct tag * add license header to gtscontext * fix old license header * improved error creation to not use fmt.Errorf() when not needed * fix various rebase conflicts, fix account test * remove commented-out code, fix-up mention caching * fix mention select bun statement * ensure mention target account populated, pass in context to customrenderer logging * remove more uncommented code, fix typeutil test * add statusfave database model caching * add status fave cache configuration * add status fave cache example config * woops, catch missed error. nice catch linter! * add back testrig panic on nil db * update example configuration to match defaults, slight tweak to cache configuration defaults * update envparsing test with new defaults * fetch followingget to use the follow target account * use accounnt.IsLocal() instead of empty domain check * use constants for the cache visibility type check * use bun.In() for notification type restriction in db query * include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable()) * use bun query building for nested select statements to ensure working with postgres * update public timeline future status checks to match visibility filter * same as previous, for home timeline * update public timeline tests to dynamically check for appropriate statuses * migrate accounts to allow unique constraint on public_key * provide minimal account with publicKey --------- Signed-off-by: kim <grufwub@gmail.com> Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
This commit is contained in:
29
internal/cache/ap.go
vendored
29
internal/cache/ap.go
vendored
@@ -17,27 +17,14 @@
|
||||
|
||||
package cache
|
||||
|
||||
type APCaches interface {
|
||||
// Init will initialize all the ActivityPub caches in this collection.
|
||||
// NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe.
|
||||
Init()
|
||||
type APCaches struct{}
|
||||
|
||||
// Start will attempt to start all of the ActivityPub caches, or panic.
|
||||
Start()
|
||||
// Init will initialize all the ActivityPub caches in this collection.
|
||||
// NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe.
|
||||
func (c *APCaches) Init() {}
|
||||
|
||||
// Stop will attempt to stop all of the ActivityPub caches, or panic.
|
||||
Stop()
|
||||
}
|
||||
// Start will attempt to start all of the ActivityPub caches, or panic.
|
||||
func (c *APCaches) Start() {}
|
||||
|
||||
// NewAP returns a new default implementation of APCaches.
|
||||
func NewAP() APCaches {
|
||||
return &apCaches{}
|
||||
}
|
||||
|
||||
type apCaches struct{}
|
||||
|
||||
func (c *apCaches) Init() {}
|
||||
|
||||
func (c *apCaches) Start() {}
|
||||
|
||||
func (c *apCaches) Stop() {}
|
||||
// Stop will attempt to stop all of the ActivityPub caches, or panic.
|
||||
func (c *APCaches) Stop() {}
|
||||
|
80
internal/cache/cache.go
vendored
80
internal/cache/cache.go
vendored
@@ -17,13 +17,23 @@
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
)
|
||||
|
||||
type Caches struct {
|
||||
// GTS provides access to the collection of gtsmodel object caches.
|
||||
// (used by the database).
|
||||
GTS GTSCaches
|
||||
|
||||
// AP provides access to the collection of ActivityPub object caches.
|
||||
// (planned to be used by the typeconverter).
|
||||
AP APCaches
|
||||
|
||||
// Visibility provides access to the item visibility cache.
|
||||
// (used by the visibility filter).
|
||||
Visibility VisibilityCache
|
||||
|
||||
// prevent pass-by-value.
|
||||
_ nocopy
|
||||
}
|
||||
@@ -31,29 +41,77 @@ type Caches struct {
|
||||
// Init will (re)initialize both the GTS and AP cache collections.
|
||||
// NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe.
|
||||
func (c *Caches) Init() {
|
||||
if c.GTS == nil {
|
||||
// use default impl
|
||||
c.GTS = NewGTS()
|
||||
}
|
||||
|
||||
if c.AP == nil {
|
||||
// use default impl
|
||||
c.AP = NewAP()
|
||||
}
|
||||
|
||||
// initialize caches
|
||||
c.GTS.Init()
|
||||
c.AP.Init()
|
||||
c.Visibility.Init()
|
||||
|
||||
// Setup cache invalidate hooks.
|
||||
// !! READ THE METHOD COMMENT
|
||||
c.setuphooks()
|
||||
}
|
||||
|
||||
// Start will start both the GTS and AP cache collections.
|
||||
func (c *Caches) Start() {
|
||||
c.GTS.Start()
|
||||
c.AP.Start()
|
||||
c.Visibility.Start()
|
||||
}
|
||||
|
||||
// Stop will stop both the GTS and AP cache collections.
|
||||
func (c *Caches) Stop() {
|
||||
c.GTS.Stop()
|
||||
c.AP.Stop()
|
||||
c.Visibility.Stop()
|
||||
}
|
||||
|
||||
// setuphooks sets necessary cache invalidation hooks between caches,
|
||||
// as an invalidation indicates a database UPDATE / DELETE. INSERT is
|
||||
// not handled by invalidation hooks and must be invalidated manually.
|
||||
func (c *Caches) setuphooks() {
|
||||
c.GTS.Account().SetInvalidateCallback(func(account *gtsmodel.Account) {
|
||||
// Invalidate account ID cached visibility.
|
||||
c.Visibility.Invalidate("ItemID", account.ID)
|
||||
c.Visibility.Invalidate("RequesterID", account.ID)
|
||||
})
|
||||
|
||||
c.GTS.Block().SetInvalidateCallback(func(block *gtsmodel.Block) {
|
||||
// Invalidate block origin account ID cached visibility.
|
||||
c.Visibility.Invalidate("ItemID", block.AccountID)
|
||||
c.Visibility.Invalidate("RequesterID", block.AccountID)
|
||||
|
||||
// Invalidate block target account ID cached visibility.
|
||||
c.Visibility.Invalidate("ItemID", block.TargetAccountID)
|
||||
c.Visibility.Invalidate("RequesterID", block.TargetAccountID)
|
||||
})
|
||||
|
||||
c.GTS.Follow().SetInvalidateCallback(func(follow *gtsmodel.Follow) {
|
||||
// Invalidate follow origin account ID cached visibility.
|
||||
c.Visibility.Invalidate("ItemID", follow.AccountID)
|
||||
c.Visibility.Invalidate("RequesterID", follow.AccountID)
|
||||
|
||||
// Invalidate follow target account ID cached visibility.
|
||||
c.Visibility.Invalidate("ItemID", follow.TargetAccountID)
|
||||
c.Visibility.Invalidate("RequesterID", follow.TargetAccountID)
|
||||
})
|
||||
|
||||
c.GTS.FollowRequest().SetInvalidateCallback(func(followReq *gtsmodel.FollowRequest) {
|
||||
// Invalidate follow request origin account ID cached visibility.
|
||||
c.Visibility.Invalidate("ItemID", followReq.AccountID)
|
||||
c.Visibility.Invalidate("RequesterID", followReq.AccountID)
|
||||
|
||||
// Invalidate follow request target account ID cached visibility.
|
||||
c.Visibility.Invalidate("ItemID", followReq.TargetAccountID)
|
||||
c.Visibility.Invalidate("RequesterID", followReq.TargetAccountID)
|
||||
})
|
||||
|
||||
c.GTS.Status().SetInvalidateCallback(func(status *gtsmodel.Status) {
|
||||
// Invalidate status ID cached visibility.
|
||||
c.Visibility.Invalidate("ItemID", status.ID)
|
||||
})
|
||||
|
||||
c.GTS.User().SetInvalidateCallback(func(user *gtsmodel.User) {
|
||||
// Invalidate local account ID cached visibility.
|
||||
c.Visibility.Invalidate("ItemID", user.AccountID)
|
||||
c.Visibility.Invalidate("RequesterID", user.AccountID)
|
||||
})
|
||||
}
|
||||
|
309
internal/cache/gts.go
vendored
309
internal/cache/gts.go
vendored
@@ -25,240 +25,221 @@ import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
)
|
||||
|
||||
type GTSCaches interface {
|
||||
// Init will initialize all the gtsmodel caches in this collection.
|
||||
// NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe.
|
||||
Init()
|
||||
|
||||
// Start will attempt to start all of the gtsmodel caches, or panic.
|
||||
Start()
|
||||
|
||||
// Stop will attempt to stop all of the gtsmodel caches, or panic.
|
||||
Stop()
|
||||
|
||||
// Account provides access to the gtsmodel Account database cache.
|
||||
Account() *result.Cache[*gtsmodel.Account]
|
||||
|
||||
// Block provides access to the gtsmodel Block (account) database cache.
|
||||
Block() *result.Cache[*gtsmodel.Block]
|
||||
|
||||
// DomainBlock provides access to the domain block database cache.
|
||||
DomainBlock() *domain.BlockCache
|
||||
|
||||
// Emoji provides access to the gtsmodel Emoji database cache.
|
||||
Emoji() *result.Cache[*gtsmodel.Emoji]
|
||||
|
||||
// EmojiCategory provides access to the gtsmodel EmojiCategory database cache.
|
||||
EmojiCategory() *result.Cache[*gtsmodel.EmojiCategory]
|
||||
|
||||
// Mention provides access to the gtsmodel Mention database cache.
|
||||
Mention() *result.Cache[*gtsmodel.Mention]
|
||||
|
||||
// Media provides access to the gtsmodel Media database cache.
|
||||
Media() *result.Cache[*gtsmodel.MediaAttachment]
|
||||
|
||||
// Notification provides access to the gtsmodel Notification database cache.
|
||||
Notification() *result.Cache[*gtsmodel.Notification]
|
||||
|
||||
// Report provides access to the gtsmodel Report database cache.
|
||||
Report() *result.Cache[*gtsmodel.Report]
|
||||
|
||||
// Status provides access to the gtsmodel Status database cache.
|
||||
Status() *result.Cache[*gtsmodel.Status]
|
||||
|
||||
// Tombstone provides access to the gtsmodel Tombstone database cache.
|
||||
Tombstone() *result.Cache[*gtsmodel.Tombstone]
|
||||
|
||||
// User provides access to the gtsmodel User database cache.
|
||||
User() *result.Cache[*gtsmodel.User]
|
||||
|
||||
// Webfinger
|
||||
Webfinger() *ttl.Cache[string, string]
|
||||
}
|
||||
|
||||
// NewGTS returns a new default implementation of GTSCaches.
|
||||
func NewGTS() GTSCaches {
|
||||
return >sCaches{}
|
||||
}
|
||||
|
||||
type gtsCaches struct {
|
||||
account *result.Cache[*gtsmodel.Account]
|
||||
block *result.Cache[*gtsmodel.Block]
|
||||
type GTSCaches struct {
|
||||
account *result.Cache[*gtsmodel.Account]
|
||||
block *result.Cache[*gtsmodel.Block]
|
||||
// TODO: maybe should be moved out of here since it's
|
||||
// not actually doing anything with gtsmodel.DomainBlock.
|
||||
domainBlock *domain.BlockCache
|
||||
emoji *result.Cache[*gtsmodel.Emoji]
|
||||
emojiCategory *result.Cache[*gtsmodel.EmojiCategory]
|
||||
follow *result.Cache[*gtsmodel.Follow]
|
||||
followRequest *result.Cache[*gtsmodel.FollowRequest]
|
||||
media *result.Cache[*gtsmodel.MediaAttachment]
|
||||
mention *result.Cache[*gtsmodel.Mention]
|
||||
notification *result.Cache[*gtsmodel.Notification]
|
||||
report *result.Cache[*gtsmodel.Report]
|
||||
status *result.Cache[*gtsmodel.Status]
|
||||
statusFave *result.Cache[*gtsmodel.StatusFave]
|
||||
tombstone *result.Cache[*gtsmodel.Tombstone]
|
||||
user *result.Cache[*gtsmodel.User]
|
||||
webfinger *ttl.Cache[string, string]
|
||||
// TODO: move out of GTS caches since not using database models.
|
||||
webfinger *ttl.Cache[string, string]
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Init() {
|
||||
// Init will initialize all the gtsmodel caches in this collection.
|
||||
// NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe.
|
||||
func (c *GTSCaches) Init() {
|
||||
c.initAccount()
|
||||
c.initBlock()
|
||||
c.initDomainBlock()
|
||||
c.initEmoji()
|
||||
c.initEmojiCategory()
|
||||
c.initFollow()
|
||||
c.initFollowRequest()
|
||||
c.initMedia()
|
||||
c.initMention()
|
||||
c.initNotification()
|
||||
c.initReport()
|
||||
c.initStatus()
|
||||
c.initStatusFave()
|
||||
c.initTombstone()
|
||||
c.initUser()
|
||||
c.initWebfinger()
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Start() {
|
||||
tryUntil("starting gtsmodel.Account cache", 5, func() bool {
|
||||
return c.account.Start(config.GetCacheGTSAccountSweepFreq())
|
||||
// Start will attempt to start all of the gtsmodel caches, or panic.
|
||||
func (c *GTSCaches) Start() {
|
||||
tryStart(c.account, config.GetCacheGTSAccountSweepFreq())
|
||||
tryStart(c.block, config.GetCacheGTSBlockSweepFreq())
|
||||
tryUntil("starting domain block cache", 5, func() bool {
|
||||
if sweep := config.GetCacheGTSDomainBlockSweepFreq(); sweep > 0 {
|
||||
return c.domainBlock.Start(sweep)
|
||||
}
|
||||
return true
|
||||
})
|
||||
tryUntil("starting gtsmodel.Block cache", 5, func() bool {
|
||||
return c.block.Start(config.GetCacheGTSBlockSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.DomainBlock cache", 5, func() bool {
|
||||
return c.domainBlock.Start(config.GetCacheGTSDomainBlockSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.Emoji cache", 5, func() bool {
|
||||
return c.emoji.Start(config.GetCacheGTSEmojiSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.EmojiCategory cache", 5, func() bool {
|
||||
return c.emojiCategory.Start(config.GetCacheGTSEmojiCategorySweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.MediaAttachment cache", 5, func() bool {
|
||||
return c.media.Start(config.GetCacheGTSMediaSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.Mention cache", 5, func() bool {
|
||||
return c.mention.Start(config.GetCacheGTSMentionSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.Notification cache", 5, func() bool {
|
||||
return c.notification.Start(config.GetCacheGTSNotificationSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.Report cache", 5, func() bool {
|
||||
return c.report.Start(config.GetCacheGTSReportSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.Status cache", 5, func() bool {
|
||||
return c.status.Start(config.GetCacheGTSStatusSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.Tombstone cache", 5, func() bool {
|
||||
return c.tombstone.Start(config.GetCacheGTSTombstoneSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.User cache", 5, func() bool {
|
||||
return c.user.Start(config.GetCacheGTSUserSweepFreq())
|
||||
})
|
||||
tryUntil("starting gtsmodel.Webfinger cache", 5, func() bool {
|
||||
return c.webfinger.Start(config.GetCacheGTSWebfingerSweepFreq())
|
||||
tryStart(c.emoji, config.GetCacheGTSEmojiSweepFreq())
|
||||
tryStart(c.emojiCategory, config.GetCacheGTSEmojiCategorySweepFreq())
|
||||
tryStart(c.follow, config.GetCacheGTSFollowSweepFreq())
|
||||
tryStart(c.followRequest, config.GetCacheGTSFollowRequestSweepFreq())
|
||||
tryStart(c.media, config.GetCacheGTSMediaSweepFreq())
|
||||
tryStart(c.mention, config.GetCacheGTSMentionSweepFreq())
|
||||
tryStart(c.notification, config.GetCacheGTSNotificationSweepFreq())
|
||||
tryStart(c.report, config.GetCacheGTSReportSweepFreq())
|
||||
tryStart(c.status, config.GetCacheGTSStatusSweepFreq())
|
||||
tryStart(c.statusFave, config.GetCacheGTSStatusFaveSweepFreq())
|
||||
tryStart(c.tombstone, config.GetCacheGTSTombstoneSweepFreq())
|
||||
tryStart(c.user, config.GetCacheGTSUserSweepFreq())
|
||||
tryUntil("starting *gtsmodel.Webfinger cache", 5, func() bool {
|
||||
if sweep := config.GetCacheGTSWebfingerSweepFreq(); sweep > 0 {
|
||||
return c.webfinger.Start(sweep)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Stop() {
|
||||
tryUntil("stopping gtsmodel.Account cache", 5, c.account.Stop)
|
||||
tryUntil("stopping gtsmodel.Block cache", 5, c.block.Stop)
|
||||
tryUntil("stopping gtsmodel.DomainBlock cache", 5, c.domainBlock.Stop)
|
||||
tryUntil("stopping gtsmodel.Emoji cache", 5, c.emoji.Stop)
|
||||
tryUntil("stopping gtsmodel.EmojiCategory cache", 5, c.emojiCategory.Stop)
|
||||
tryUntil("stopping gtsmodel.MediaAttachment cache", 5, c.media.Stop)
|
||||
tryUntil("stopping gtsmodel.Mention cache", 5, c.mention.Stop)
|
||||
tryUntil("stopping gtsmodel.Notification cache", 5, c.notification.Stop)
|
||||
tryUntil("stopping gtsmodel.Report cache", 5, c.report.Stop)
|
||||
tryUntil("stopping gtsmodel.Status cache", 5, c.status.Stop)
|
||||
tryUntil("stopping gtsmodel.Tombstone cache", 5, c.tombstone.Stop)
|
||||
tryUntil("stopping gtsmodel.User cache", 5, c.user.Stop)
|
||||
tryUntil("stopping gtsmodel.Webfinger cache", 5, c.webfinger.Stop)
|
||||
// Stop will attempt to stop all of the gtsmodel caches, or panic.
|
||||
func (c *GTSCaches) Stop() {
|
||||
tryStop(c.account, config.GetCacheGTSAccountSweepFreq())
|
||||
tryStop(c.block, config.GetCacheGTSBlockSweepFreq())
|
||||
tryUntil("stopping domain block cache", 5, c.domainBlock.Stop)
|
||||
tryStop(c.emoji, config.GetCacheGTSEmojiSweepFreq())
|
||||
tryStop(c.emojiCategory, config.GetCacheGTSEmojiCategorySweepFreq())
|
||||
tryStop(c.follow, config.GetCacheGTSFollowSweepFreq())
|
||||
tryStop(c.followRequest, config.GetCacheGTSFollowRequestSweepFreq())
|
||||
tryStop(c.media, config.GetCacheGTSMediaSweepFreq())
|
||||
tryStop(c.mention, config.GetCacheGTSNotificationSweepFreq())
|
||||
tryStop(c.notification, config.GetCacheGTSNotificationSweepFreq())
|
||||
tryStop(c.report, config.GetCacheGTSReportSweepFreq())
|
||||
tryStop(c.status, config.GetCacheGTSStatusSweepFreq())
|
||||
tryStop(c.statusFave, config.GetCacheGTSStatusFaveSweepFreq())
|
||||
tryStop(c.tombstone, config.GetCacheGTSTombstoneSweepFreq())
|
||||
tryStop(c.user, config.GetCacheGTSUserSweepFreq())
|
||||
tryUntil("stopping *gtsmodel.Webfinger cache", 5, c.webfinger.Stop)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Account() *result.Cache[*gtsmodel.Account] {
|
||||
// Account provides access to the gtsmodel Account database cache.
|
||||
func (c *GTSCaches) Account() *result.Cache[*gtsmodel.Account] {
|
||||
return c.account
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Block() *result.Cache[*gtsmodel.Block] {
|
||||
// Block provides access to the gtsmodel Block (account) database cache.
|
||||
func (c *GTSCaches) Block() *result.Cache[*gtsmodel.Block] {
|
||||
return c.block
|
||||
}
|
||||
|
||||
func (c *gtsCaches) DomainBlock() *domain.BlockCache {
|
||||
// DomainBlock provides access to the domain block database cache.
|
||||
func (c *GTSCaches) DomainBlock() *domain.BlockCache {
|
||||
return c.domainBlock
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Emoji() *result.Cache[*gtsmodel.Emoji] {
|
||||
// Emoji provides access to the gtsmodel Emoji database cache.
|
||||
func (c *GTSCaches) Emoji() *result.Cache[*gtsmodel.Emoji] {
|
||||
return c.emoji
|
||||
}
|
||||
|
||||
func (c *gtsCaches) EmojiCategory() *result.Cache[*gtsmodel.EmojiCategory] {
|
||||
// EmojiCategory provides access to the gtsmodel EmojiCategory database cache.
|
||||
func (c *GTSCaches) EmojiCategory() *result.Cache[*gtsmodel.EmojiCategory] {
|
||||
return c.emojiCategory
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Media() *result.Cache[*gtsmodel.MediaAttachment] {
|
||||
// Follow provides access to the gtsmodel Follow database cache.
|
||||
func (c *GTSCaches) Follow() *result.Cache[*gtsmodel.Follow] {
|
||||
return c.follow
|
||||
}
|
||||
|
||||
// FollowRequest provides access to the gtsmodel FollowRequest database cache.
|
||||
func (c *GTSCaches) FollowRequest() *result.Cache[*gtsmodel.FollowRequest] {
|
||||
return c.followRequest
|
||||
}
|
||||
|
||||
// Media provides access to the gtsmodel Media database cache.
|
||||
func (c *GTSCaches) Media() *result.Cache[*gtsmodel.MediaAttachment] {
|
||||
return c.media
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Mention() *result.Cache[*gtsmodel.Mention] {
|
||||
// Mention provides access to the gtsmodel Mention database cache.
|
||||
func (c *GTSCaches) Mention() *result.Cache[*gtsmodel.Mention] {
|
||||
return c.mention
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Notification() *result.Cache[*gtsmodel.Notification] {
|
||||
// Notification provides access to the gtsmodel Notification database cache.
|
||||
func (c *GTSCaches) Notification() *result.Cache[*gtsmodel.Notification] {
|
||||
return c.notification
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Report() *result.Cache[*gtsmodel.Report] {
|
||||
// Report provides access to the gtsmodel Report database cache.
|
||||
func (c *GTSCaches) Report() *result.Cache[*gtsmodel.Report] {
|
||||
return c.report
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Status() *result.Cache[*gtsmodel.Status] {
|
||||
// Status provides access to the gtsmodel Status database cache.
|
||||
func (c *GTSCaches) Status() *result.Cache[*gtsmodel.Status] {
|
||||
return c.status
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Tombstone() *result.Cache[*gtsmodel.Tombstone] {
|
||||
// StatusFave provides access to the gtsmodel StatusFave database cache.
|
||||
func (c *GTSCaches) StatusFave() *result.Cache[*gtsmodel.StatusFave] {
|
||||
return c.statusFave
|
||||
}
|
||||
|
||||
// Tombstone provides access to the gtsmodel Tombstone database cache.
|
||||
func (c *GTSCaches) Tombstone() *result.Cache[*gtsmodel.Tombstone] {
|
||||
return c.tombstone
|
||||
}
|
||||
|
||||
func (c *gtsCaches) User() *result.Cache[*gtsmodel.User] {
|
||||
// User provides access to the gtsmodel User database cache.
|
||||
func (c *GTSCaches) User() *result.Cache[*gtsmodel.User] {
|
||||
return c.user
|
||||
}
|
||||
|
||||
func (c *gtsCaches) Webfinger() *ttl.Cache[string, string] {
|
||||
// Webfinger provides access to the webfinger URL cache.
|
||||
func (c *GTSCaches) Webfinger() *ttl.Cache[string, string] {
|
||||
return c.webfinger
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initAccount() {
|
||||
func (c *GTSCaches) initAccount() {
|
||||
c.account = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "URI"},
|
||||
{Name: "URL"},
|
||||
{Name: "Username.Domain"},
|
||||
{Name: "PublicKeyURI"},
|
||||
{Name: "InboxURI"},
|
||||
{Name: "OutboxURI"},
|
||||
{Name: "FollowersURI"},
|
||||
{Name: "FollowingURI"},
|
||||
}, func(a1 *gtsmodel.Account) *gtsmodel.Account {
|
||||
a2 := new(gtsmodel.Account)
|
||||
*a2 = *a1
|
||||
return a2
|
||||
}, config.GetCacheGTSAccountMaxSize())
|
||||
c.account.SetTTL(config.GetCacheGTSAccountTTL(), true)
|
||||
c.account.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initBlock() {
|
||||
func (c *GTSCaches) initBlock() {
|
||||
c.block = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "AccountID.TargetAccountID"},
|
||||
{Name: "URI"},
|
||||
{Name: "AccountID.TargetAccountID"},
|
||||
}, func(b1 *gtsmodel.Block) *gtsmodel.Block {
|
||||
b2 := new(gtsmodel.Block)
|
||||
*b2 = *b1
|
||||
return b2
|
||||
}, config.GetCacheGTSBlockMaxSize())
|
||||
c.block.SetTTL(config.GetCacheGTSBlockTTL(), true)
|
||||
c.block.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initDomainBlock() {
|
||||
func (c *GTSCaches) initDomainBlock() {
|
||||
c.domainBlock = domain.New(
|
||||
config.GetCacheGTSDomainBlockMaxSize(),
|
||||
config.GetCacheGTSDomainBlockTTL(),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initEmoji() {
|
||||
func (c *GTSCaches) initEmoji() {
|
||||
c.emoji = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "URI"},
|
||||
@@ -270,9 +251,10 @@ func (c *gtsCaches) initEmoji() {
|
||||
return e2
|
||||
}, config.GetCacheGTSEmojiMaxSize())
|
||||
c.emoji.SetTTL(config.GetCacheGTSEmojiTTL(), true)
|
||||
c.emoji.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initEmojiCategory() {
|
||||
func (c *GTSCaches) initEmojiCategory() {
|
||||
c.emojiCategory = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "Name"},
|
||||
@@ -282,9 +264,36 @@ func (c *gtsCaches) initEmojiCategory() {
|
||||
return c2
|
||||
}, config.GetCacheGTSEmojiCategoryMaxSize())
|
||||
c.emojiCategory.SetTTL(config.GetCacheGTSEmojiCategoryTTL(), true)
|
||||
c.emojiCategory.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initMedia() {
|
||||
func (c *GTSCaches) initFollow() {
|
||||
c.follow = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "URI"},
|
||||
{Name: "AccountID.TargetAccountID"},
|
||||
}, func(f1 *gtsmodel.Follow) *gtsmodel.Follow {
|
||||
f2 := new(gtsmodel.Follow)
|
||||
*f2 = *f1
|
||||
return f2
|
||||
}, config.GetCacheGTSFollowMaxSize())
|
||||
c.follow.SetTTL(config.GetCacheGTSFollowTTL(), true)
|
||||
}
|
||||
|
||||
func (c *GTSCaches) initFollowRequest() {
|
||||
c.followRequest = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "URI"},
|
||||
{Name: "AccountID.TargetAccountID"},
|
||||
}, func(f1 *gtsmodel.FollowRequest) *gtsmodel.FollowRequest {
|
||||
f2 := new(gtsmodel.FollowRequest)
|
||||
*f2 = *f1
|
||||
return f2
|
||||
}, config.GetCacheGTSFollowRequestMaxSize())
|
||||
c.followRequest.SetTTL(config.GetCacheGTSFollowRequestTTL(), true)
|
||||
}
|
||||
|
||||
func (c *GTSCaches) initMedia() {
|
||||
c.media = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
}, func(m1 *gtsmodel.MediaAttachment) *gtsmodel.MediaAttachment {
|
||||
@@ -293,9 +302,10 @@ func (c *gtsCaches) initMedia() {
|
||||
return m2
|
||||
}, config.GetCacheGTSMediaMaxSize())
|
||||
c.media.SetTTL(config.GetCacheGTSMediaTTL(), true)
|
||||
c.media.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initMention() {
|
||||
func (c *GTSCaches) initMention() {
|
||||
c.mention = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
}, func(m1 *gtsmodel.Mention) *gtsmodel.Mention {
|
||||
@@ -304,9 +314,10 @@ func (c *gtsCaches) initMention() {
|
||||
return m2
|
||||
}, config.GetCacheGTSMentionMaxSize())
|
||||
c.mention.SetTTL(config.GetCacheGTSMentionTTL(), true)
|
||||
c.mention.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initNotification() {
|
||||
func (c *GTSCaches) initNotification() {
|
||||
c.notification = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
}, func(n1 *gtsmodel.Notification) *gtsmodel.Notification {
|
||||
@@ -315,9 +326,10 @@ func (c *gtsCaches) initNotification() {
|
||||
return n2
|
||||
}, config.GetCacheGTSNotificationMaxSize())
|
||||
c.notification.SetTTL(config.GetCacheGTSNotificationTTL(), true)
|
||||
c.notification.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initReport() {
|
||||
func (c *GTSCaches) initReport() {
|
||||
c.report = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
}, func(r1 *gtsmodel.Report) *gtsmodel.Report {
|
||||
@@ -326,9 +338,10 @@ func (c *gtsCaches) initReport() {
|
||||
return r2
|
||||
}, config.GetCacheGTSReportMaxSize())
|
||||
c.report.SetTTL(config.GetCacheGTSReportTTL(), true)
|
||||
c.report.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initStatus() {
|
||||
func (c *GTSCaches) initStatus() {
|
||||
c.status = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "URI"},
|
||||
@@ -339,10 +352,24 @@ func (c *gtsCaches) initStatus() {
|
||||
return s2
|
||||
}, config.GetCacheGTSStatusMaxSize())
|
||||
c.status.SetTTL(config.GetCacheGTSStatusTTL(), true)
|
||||
c.status.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *GTSCaches) initStatusFave() {
|
||||
c.statusFave = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "AccountID.StatusID"},
|
||||
}, func(f1 *gtsmodel.StatusFave) *gtsmodel.StatusFave {
|
||||
f2 := new(gtsmodel.StatusFave)
|
||||
*f2 = *f1
|
||||
return f2
|
||||
}, config.GetCacheGTSStatusFaveMaxSize())
|
||||
c.status.SetTTL(config.GetCacheGTSStatusFaveTTL(), true)
|
||||
c.status.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
// initTombstone will initialize the gtsmodel.Tombstone cache.
|
||||
func (c *gtsCaches) initTombstone() {
|
||||
func (c *GTSCaches) initTombstone() {
|
||||
c.tombstone = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "URI"},
|
||||
@@ -352,9 +379,10 @@ func (c *gtsCaches) initTombstone() {
|
||||
return t2
|
||||
}, config.GetCacheGTSTombstoneMaxSize())
|
||||
c.tombstone.SetTTL(config.GetCacheGTSTombstoneTTL(), true)
|
||||
c.tombstone.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initUser() {
|
||||
func (c *GTSCaches) initUser() {
|
||||
c.user = result.New([]result.Lookup{
|
||||
{Name: "ID"},
|
||||
{Name: "AccountID"},
|
||||
@@ -367,9 +395,10 @@ func (c *gtsCaches) initUser() {
|
||||
return u2
|
||||
}, config.GetCacheGTSUserMaxSize())
|
||||
c.user.SetTTL(config.GetCacheGTSUserTTL(), true)
|
||||
c.user.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
func (c *gtsCaches) initWebfinger() {
|
||||
func (c *GTSCaches) initWebfinger() {
|
||||
c.webfinger = ttl.New[string, string](
|
||||
0,
|
||||
config.GetCacheGTSWebfingerMaxSize(),
|
||||
|
45
internal/cache/util.go
vendored
45
internal/cache/util.go
vendored
@@ -17,7 +17,30 @@
|
||||
|
||||
package cache
|
||||
|
||||
import "github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"codeberg.org/gruf/go-cache/v3/result"
|
||||
errorsv2 "codeberg.org/gruf/go-errors/v2"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
)
|
||||
|
||||
// SentinelError is returned to indicate a non-permanent error return,
|
||||
// i.e. a situation in which we do not want a cache a negative result.
|
||||
var SentinelError = errors.New("BUG: error should not be returned") //nolint:revive
|
||||
|
||||
// ignoreErrors is an error ignoring function capable of being passed to
|
||||
// caches, which specifically catches and ignores our sentinel error type.
|
||||
func ignoreErrors(err error) bool {
|
||||
return errorsv2.Is(
|
||||
SentinelError,
|
||||
context.DeadlineExceeded,
|
||||
context.Canceled,
|
||||
)
|
||||
}
|
||||
|
||||
// nocopy when embedded will signal linter to
|
||||
// error on pass-by-value of parent struct.
|
||||
@@ -27,6 +50,26 @@ func (*nocopy) Lock() {}
|
||||
|
||||
func (*nocopy) Unlock() {}
|
||||
|
||||
// tryStart will attempt to start the given cache only if sweep duration > 0 (sweeping is enabled).
|
||||
func tryStart[ValueType any](cache *result.Cache[ValueType], sweep time.Duration) {
|
||||
if sweep > 0 {
|
||||
var z ValueType
|
||||
msg := fmt.Sprintf("starting %T cache", z)
|
||||
tryUntil(msg, 5, func() bool {
|
||||
return cache.Start(sweep)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// tryStop will attempt to stop the given cache only if sweep duration > 0 (sweeping is enabled).
|
||||
func tryStop[ValueType any](cache *result.Cache[ValueType], sweep time.Duration) {
|
||||
if sweep > 0 {
|
||||
var z ValueType
|
||||
msg := fmt.Sprintf("stopping %T cache", z)
|
||||
tryUntil(msg, 5, cache.Stop)
|
||||
}
|
||||
}
|
||||
|
||||
// tryUntil will attempt to call 'do' for 'count' attempts, before panicking with 'msg'.
|
||||
func tryUntil(msg string, count int, do func() bool) {
|
||||
for i := 0; i < count; i++ {
|
||||
|
81
internal/cache/visibility.go
vendored
Normal file
81
internal/cache/visibility.go
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"codeberg.org/gruf/go-cache/v3/result"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
)
|
||||
|
||||
type VisibilityCache struct {
|
||||
*result.Cache[*CachedVisibility]
|
||||
}
|
||||
|
||||
// Init will initialize the visibility cache in this collection.
|
||||
// NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe.
|
||||
func (c *VisibilityCache) Init() {
|
||||
c.Cache = result.New([]result.Lookup{
|
||||
{Name: "ItemID"},
|
||||
{Name: "RequesterID"},
|
||||
{Name: "Type.RequesterID.ItemID"},
|
||||
}, func(v1 *CachedVisibility) *CachedVisibility {
|
||||
v2 := new(CachedVisibility)
|
||||
*v2 = *v1
|
||||
return v2
|
||||
}, config.GetCacheVisibilityMaxSize())
|
||||
c.Cache.SetTTL(config.GetCacheVisibilityTTL(), true)
|
||||
c.Cache.IgnoreErrors(ignoreErrors)
|
||||
}
|
||||
|
||||
// Start will attempt to start the visibility cache, or panic.
|
||||
func (c *VisibilityCache) Start() {
|
||||
tryStart(c.Cache, config.GetCacheVisibilitySweepFreq())
|
||||
}
|
||||
|
||||
// Stop will attempt to stop the visibility cache, or panic.
|
||||
func (c *VisibilityCache) Stop() {
|
||||
tryStop(c.Cache, config.GetCacheVisibilitySweepFreq())
|
||||
}
|
||||
|
||||
// VisibilityType represents a visibility lookup type.
|
||||
// We use a byte type here to improve performance in the
|
||||
// result cache when generating the key.
|
||||
type VisibilityType byte
|
||||
|
||||
const (
|
||||
// Possible cache visibility lookup types.
|
||||
VisibilityTypeAccount = VisibilityType('a')
|
||||
VisibilityTypeStatus = VisibilityType('s')
|
||||
VisibilityTypeHome = VisibilityType('h')
|
||||
VisibilityTypePublic = VisibilityType('p')
|
||||
)
|
||||
|
||||
// CachedVisibility represents a cached visibility lookup value.
|
||||
type CachedVisibility struct {
|
||||
// ItemID is the ID of the item in question (status / account).
|
||||
ItemID string
|
||||
|
||||
// RequesterID is the ID of the requesting account for this visibility lookup.
|
||||
RequesterID string
|
||||
|
||||
// Type is the visibility lookup type.
|
||||
Type VisibilityType
|
||||
|
||||
// Value is the actual visibility value.
|
||||
Value bool
|
||||
}
|
Reference in New Issue
Block a user