Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[performance] overhaul struct (+ result) caching library for simplicity, performance and multiple-result lookups #2535

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/gotosocial/action/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
time.Time{}, // start
time.Minute, // freq
func(context.Context, time.Time) {
state.Caches.Sweep(80)
state.Caches.Sweep(60)
NyaaaWhatsUpDoc marked this conversation as resolved.
Show resolved Hide resolved
},
)

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
codeberg.org/gruf/go-runners v1.6.2
codeberg.org/gruf/go-sched v1.2.3
codeberg.org/gruf/go-store/v2 v2.2.4
codeberg.org/gruf/go-structr v0.1.1
codeberg.org/superseriousbusiness/exif-terminator v0.7.0
github.com/DmitriyVTitov/size v1.5.0
github.com/KimMachineGun/automemlimit v0.4.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ codeberg.org/gruf/go-sched v1.2.3 h1:H5ViDxxzOBR3uIyGBCf0eH8b1L8wMybOXcdtUUTXZHk
codeberg.org/gruf/go-sched v1.2.3/go.mod h1:vT9uB6KWFIIwnG9vcPY2a0alYNoqdL1mSzRM8I+PK7A=
codeberg.org/gruf/go-store/v2 v2.2.4 h1:8HO1Jh2gg7boQKA3hsDAIXd9zwieu5uXwDXEcTOD9js=
codeberg.org/gruf/go-store/v2 v2.2.4/go.mod h1:zI4VWe5CpXAktYMtaBMrgA5QmO0sQH53LBRvfn1huys=
codeberg.org/gruf/go-structr v0.1.1 h1:nR6EcZjXn+oby2nH1Mi6i8S5GWhyjUknkQMXsjbbK0g=
codeberg.org/gruf/go-structr v0.1.1/go.mod h1:OBajB6wcz0BbX0Ns88w2rdUF52rgIej471NJgV0GCW4=
codeberg.org/superseriousbusiness/exif-terminator v0.7.0 h1:Y6VApSXhKqExG0H2hZ2JelRK4xmWdjDQjn13CpEfzko=
codeberg.org/superseriousbusiness/exif-terminator v0.7.0/go.mod h1:gCWKduudUWFzsnixoMzu0FYVdxHWG+AbXnZ50DqxsUE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
Expand Down
30 changes: 0 additions & 30 deletions internal/cache/ap.go

This file was deleted.

278 changes: 70 additions & 208 deletions internal/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
package cache

import (
"time"

"github.com/superseriousbusiness/gotosocial/internal/cache/headerfilter"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
)

Expand Down Expand Up @@ -49,198 +50,59 @@ type Caches struct {
func (c *Caches) Init() {
log.Infof(nil, "init: %p", c)

c.GTS.Init()
c.Visibility.Init()

// Setup cache invalidate hooks.
// !! READ THE METHOD COMMENT
c.setuphooks()
c.initAccount()
tsmethurst marked this conversation as resolved.
Show resolved Hide resolved
c.initAccountNote()
c.initApplication()
c.initBlock()
c.initBlockIDs()
c.initBoostOfIDs()
c.initDomainAllow()
c.initDomainBlock()
c.initEmoji()
c.initEmojiCategory()
c.initFollow()
c.initFollowIDs()
c.initFollowRequest()
c.initFollowRequestIDs()
c.initInReplyToIDs()
c.initInstance()
c.initList()
c.initListEntry()
c.initMarker()
c.initMedia()
c.initMention()
c.initNotification()
c.initPoll()
c.initPollVote()
c.initPollVoteIDs()
c.initReport()
c.initStatus()
c.initStatusFave()
c.initTag()
c.initThreadMute()
c.initStatusFaveIDs()
c.initTombstone()
c.initUser()
c.initWebfinger()
c.initVisibility()
}

// Start will start both the GTS and AP cache collections.
// Start will start any caches that require a background
// routine, which usually means any kind of TTL caches.
func (c *Caches) Start() {
log.Infof(nil, "start: %p", c)

c.GTS.Start()
c.Visibility.Start()
tryUntil("starting *gtsmodel.Webfinger cache", 5, func() bool {
return c.GTS.Webfinger.Start(5 * time.Minute)
})
}

// Stop will stop both the GTS and AP cache collections.
// Stop will stop any caches that require a background
// routine, which usually means any kind of TTL caches.
func (c *Caches) Stop() {
log.Infof(nil, "stop: %p", c)

c.GTS.Stop()
c.Visibility.Stop()
}

// setuphooks sets necessary cache invalidation hooks between caches,
tsmethurst marked this conversation as resolved.
Show resolved Hide resolved
// as an invalidation indicates a database INSERT / UPDATE / DELETE.
// NOTE THEY ARE ONLY CALLED WHEN THE ITEM IS IN THE CACHE, SO FOR
// HOOKS TO BE CALLED ON DELETE YOU MUST FIRST POPULATE IT IN THE CACHE.
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)

// Invalidate this account's
// following / follower lists.
// (see FollowIDs() comment for details).
c.GTS.FollowIDs().InvalidateAll(
">"+account.ID,
"l>"+account.ID,
"<"+account.ID,
"l<"+account.ID,
)

// Invalidate this account's
// follow requesting / request lists.
// (see FollowRequestIDs() comment for details).
c.GTS.FollowRequestIDs().InvalidateAll(
">"+account.ID,
"<"+account.ID,
)

// Invalidate this account's block lists.
c.GTS.BlockIDs().Invalidate(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)

// Invalidate source account's block lists.
c.GTS.BlockIDs().Invalidate(block.AccountID)
})

c.GTS.EmojiCategory().SetInvalidateCallback(func(category *gtsmodel.EmojiCategory) {
// Invalidate any emoji in this category.
c.GTS.Emoji().Invalidate("CategoryID", category.ID)
})

c.GTS.Follow().SetInvalidateCallback(func(follow *gtsmodel.Follow) {
// Invalidate follow request with this same ID.
c.GTS.FollowRequest().Invalidate("ID", follow.ID)

// Invalidate any related list entries.
c.GTS.ListEntry().Invalidate("FollowID", follow.ID)

// 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)

// Invalidate source account's following
// lists, and destination's follwer lists.
// (see FollowIDs() comment for details).
c.GTS.FollowIDs().InvalidateAll(
">"+follow.AccountID,
"l>"+follow.AccountID,
"<"+follow.AccountID,
"l<"+follow.AccountID,
"<"+follow.TargetAccountID,
"l<"+follow.TargetAccountID,
">"+follow.TargetAccountID,
"l>"+follow.TargetAccountID,
)
})

c.GTS.FollowRequest().SetInvalidateCallback(func(followReq *gtsmodel.FollowRequest) {
// Invalidate follow with this same ID.
c.GTS.Follow().Invalidate("ID", followReq.ID)

// Invalidate source account's followreq
// lists, and destinations follow req lists.
// (see FollowRequestIDs() comment for details).
c.GTS.FollowRequestIDs().InvalidateAll(
">"+followReq.AccountID,
"<"+followReq.AccountID,
">"+followReq.TargetAccountID,
"<"+followReq.TargetAccountID,
)
})

c.GTS.List().SetInvalidateCallback(func(list *gtsmodel.List) {
// Invalidate all cached entries of this list.
c.GTS.ListEntry().Invalidate("ListID", list.ID)
})

c.GTS.Media().SetInvalidateCallback(func(media *gtsmodel.MediaAttachment) {
if *media.Avatar || *media.Header {
// Invalidate cache of attaching account.
c.GTS.Account().Invalidate("ID", media.AccountID)
}

if media.StatusID != "" {
// Invalidate cache of attaching status.
c.GTS.Status().Invalidate("ID", media.StatusID)
}
})

c.GTS.Poll().SetInvalidateCallback(func(poll *gtsmodel.Poll) {
// Invalidate all cached votes of this poll.
c.GTS.PollVote().Invalidate("PollID", poll.ID)

// Invalidate cache of poll vote IDs.
c.GTS.PollVoteIDs().Invalidate(poll.ID)
})

c.GTS.PollVote().SetInvalidateCallback(func(vote *gtsmodel.PollVote) {
// Invalidate cached poll (contains no. votes).
c.GTS.Poll().Invalidate("ID", vote.PollID)

// Invalidate cache of poll vote IDs.
c.GTS.PollVoteIDs().Invalidate(vote.PollID)
})

c.GTS.Status().SetInvalidateCallback(func(status *gtsmodel.Status) {
// Invalidate status ID cached visibility.
c.Visibility.Invalidate("ItemID", status.ID)

for _, id := range status.AttachmentIDs {
// Invalidate each media by the IDs we're aware of.
// This must be done as the status table is aware of
// the media IDs in use before the media table is
// aware of the status ID they are linked to.
//
// c.GTS.Media().Invalidate("StatusID") will not work.
c.GTS.Media().Invalidate("ID", id)
}

if status.BoostOfID != "" {
// Invalidate boost ID list of the original status.
c.GTS.BoostOfIDs().Invalidate(status.BoostOfID)
}

if status.InReplyToID != "" {
// Invalidate in reply to ID list of original status.
c.GTS.InReplyToIDs().Invalidate(status.InReplyToID)
}

if status.PollID != "" {
// Invalidate cache of attached poll ID.
c.GTS.Poll().Invalidate("ID", status.PollID)
}
})

c.GTS.StatusFave().SetInvalidateCallback(func(fave *gtsmodel.StatusFave) {
// Invalidate status fave ID list for this status.
c.GTS.StatusFaveIDs().Invalidate(fave.StatusID)
})

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)
})
tryUntil("stopping *gtsmodel.Webfinger cache", 5, c.GTS.Webfinger.Stop)
}

// Sweep will sweep all the available caches to ensure none
Expand All @@ -250,30 +112,30 @@ func (c *Caches) setuphooks() {
// require an eviction on every single write, which adds
// significant overhead to all cache writes.
func (c *Caches) Sweep(threshold float64) {
c.GTS.Account().Trim(threshold)
c.GTS.AccountNote().Trim(threshold)
c.GTS.Block().Trim(threshold)
c.GTS.BlockIDs().Trim(threshold)
c.GTS.Emoji().Trim(threshold)
c.GTS.EmojiCategory().Trim(threshold)
c.GTS.Follow().Trim(threshold)
c.GTS.FollowIDs().Trim(threshold)
c.GTS.FollowRequest().Trim(threshold)
c.GTS.FollowRequestIDs().Trim(threshold)
c.GTS.Instance().Trim(threshold)
c.GTS.List().Trim(threshold)
c.GTS.ListEntry().Trim(threshold)
c.GTS.Marker().Trim(threshold)
c.GTS.Media().Trim(threshold)
c.GTS.Mention().Trim(threshold)
c.GTS.Notification().Trim(threshold)
c.GTS.Poll().Trim(threshold)
c.GTS.Report().Trim(threshold)
c.GTS.Status().Trim(threshold)
c.GTS.StatusFave().Trim(threshold)
c.GTS.Tag().Trim(threshold)
c.GTS.ThreadMute().Trim(threshold)
c.GTS.Tombstone().Trim(threshold)
c.GTS.User().Trim(threshold)
c.GTS.Account.Trim(threshold)
c.GTS.AccountNote.Trim(threshold)
c.GTS.Block.Trim(threshold)
c.GTS.BlockIDs.Trim(threshold)
c.GTS.Emoji.Trim(threshold)
c.GTS.EmojiCategory.Trim(threshold)
c.GTS.Follow.Trim(threshold)
c.GTS.FollowIDs.Trim(threshold)
c.GTS.FollowRequest.Trim(threshold)
c.GTS.FollowRequestIDs.Trim(threshold)
c.GTS.Instance.Trim(threshold)
c.GTS.List.Trim(threshold)
c.GTS.ListEntry.Trim(threshold)
c.GTS.Marker.Trim(threshold)
c.GTS.Media.Trim(threshold)
c.GTS.Mention.Trim(threshold)
c.GTS.Notification.Trim(threshold)
c.GTS.Poll.Trim(threshold)
c.GTS.Report.Trim(threshold)
c.GTS.Status.Trim(threshold)
c.GTS.StatusFave.Trim(threshold)
c.GTS.Tag.Trim(threshold)
c.GTS.ThreadMute.Trim(threshold)
c.GTS.Tombstone.Trim(threshold)
c.GTS.User.Trim(threshold)
c.Visibility.Trim(threshold)
}
Loading