diff options
author | 2024-01-19 12:57:29 +0000 | |
---|---|---|
committer | 2024-01-19 12:57:29 +0000 | |
commit | 7ec1e1332e7d04e74451acef18b41f389722b698 (patch) | |
tree | 9c69eca7fc664ab5564279a2e065dfd5c2ddd17b /internal/db/bundb/statusfave.go | |
parent | [chore] chore rationalise http return codes for activitypub handlers (#2540) (diff) | |
download | gotosocial-7ec1e1332e7d04e74451acef18b41f389722b698.tar.xz |
[performance] overhaul struct (+ result) caching library for simplicity, performance and multiple-result lookups (#2535)
* rewrite cache library as codeberg.org/gruf/go-structr, implement in gotosocial
* use actual go-structr release version (not just commit hash)
* revert go toolchain changes (damn you go for auto changing this)
* fix go mod woes
* ensure %w is used in calls to errs.Appendf()
* fix error checking
* fix possible panic
* remove unnecessary start/stop functions, move to main Cache{} struct, add note regarding which caches require start/stop
* fix copy-paste artifact... :innocent:
* fix all comment copy-paste artifacts
* remove dropID() function, now we can just use slices.DeleteFunc()
* use util.Deduplicate() instead of collate(), move collate to util
* move orderByIDs() to util package and "generify"
* add a util.DeleteIf() function, use this to delete entries on failed population
* use slices.DeleteFunc() instead of util.DeleteIf() (i had the logic mixed up in my head somehow lol)
* add note about how collate differs from deduplicate
Diffstat (limited to 'internal/db/bundb/statusfave.go')
-rw-r--r-- | internal/db/bundb/statusfave.go | 91 |
1 files changed, 67 insertions, 24 deletions
diff --git a/internal/db/bundb/statusfave.go b/internal/db/bundb/statusfave.go index 73ac62fe7..e0f018b68 100644 --- a/internal/db/bundb/statusfave.go +++ b/internal/db/bundb/statusfave.go @@ -22,6 +22,7 @@ import ( "database/sql" "errors" "fmt" + "slices" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtscontext" @@ -29,6 +30,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/state" + "github.com/superseriousbusiness/gotosocial/internal/util" "github.com/uptrace/bun" ) @@ -40,7 +42,7 @@ type statusFaveDB struct { func (s *statusFaveDB) GetStatusFave(ctx context.Context, accountID string, statusID string) (*gtsmodel.StatusFave, error) { return s.getStatusFave( ctx, - "AccountID.StatusID", + "AccountID,StatusID", func(fave *gtsmodel.StatusFave) error { return s.db. NewSelect(). @@ -77,7 +79,7 @@ func (s *statusFaveDB) GetStatusFaveByID(ctx context.Context, id string) (*gtsmo func (s *statusFaveDB) getStatusFave(ctx context.Context, lookup string, dbQuery func(*gtsmodel.StatusFave) error, keyParts ...any) (*gtsmodel.StatusFave, error) { // Fetch status fave from database cache with loader callback - fave, err := s.state.Caches.GTS.StatusFave().Load(lookup, func() (*gtsmodel.StatusFave, error) { + fave, err := s.state.Caches.GTS.StatusFave.LoadOne(lookup, func() (*gtsmodel.StatusFave, error) { var fave gtsmodel.StatusFave // Not cached! Perform database query. @@ -111,19 +113,62 @@ func (s *statusFaveDB) GetStatusFaves(ctx context.Context, statusID string) ([]* return nil, err } - // Preallocate a slice of expected status fave capacity. - faves := make([]*gtsmodel.StatusFave, 0, len(faveIDs)) + // Preallocate at-worst possible length. + uncached := make([]string, 0, len(faveIDs)) - for _, id := range faveIDs { - // Fetch status fave model for each ID. - fave, err := s.GetStatusFaveByID(ctx, id) - if err != nil { - log.Errorf(ctx, "error getting status fave %q: %v", id, err) - continue - } - faves = append(faves, fave) + // Load all fave IDs via cache loader callbacks. + faves, err := s.state.Caches.GTS.StatusFave.Load("ID", + + // Load cached + check for uncached. + func(load func(keyParts ...any) bool) { + for _, id := range faveIDs { + if !load(id) { + uncached = append(uncached, id) + } + } + }, + + // Uncached status faves loader function. + func() ([]*gtsmodel.StatusFave, error) { + // Preallocate expected length of uncached faves. + faves := make([]*gtsmodel.StatusFave, 0, len(uncached)) + + // Perform database query scanning + // the remaining (uncached) fave IDs. + if err := s.db.NewSelect(). + Model(&faves). + Where("? IN (?)", bun.Ident("id"), bun.In(uncached)). + Scan(ctx); err != nil { + return nil, err + } + + return faves, nil + }, + ) + if err != nil { + return nil, err } + // Reorder the statuses by their + // IDs to ensure in correct order. + getID := func(f *gtsmodel.StatusFave) string { return f.ID } + util.OrderBy(faves, faveIDs, getID) + + if gtscontext.Barebones(ctx) { + // no need to fully populate. + return faves, nil + } + + // Populate all loaded faves, removing those we fail to + // populate (removes needing so many nil checks everywhere). + faves = slices.DeleteFunc(faves, func(fave *gtsmodel.StatusFave) bool { + if err := s.PopulateStatusFave(ctx, fave); err != nil { + log.Errorf(ctx, "error populating fave %s: %v", fave.ID, err) + return true + } + return false + }) + return faves, nil } @@ -141,7 +186,7 @@ func (s *statusFaveDB) CountStatusFaves(ctx context.Context, statusID string) (i } func (s *statusFaveDB) getStatusFaveIDs(ctx context.Context, statusID string) ([]string, error) { - return s.state.Caches.GTS.StatusFaveIDs().Load(statusID, func() ([]string, error) { + return s.state.Caches.GTS.StatusFaveIDs.Load(statusID, func() ([]string, error) { var faveIDs []string // Status fave IDs not in cache, perform DB query! @@ -201,7 +246,7 @@ func (s *statusFaveDB) PopulateStatusFave(ctx context.Context, statusFave *gtsmo } func (s *statusFaveDB) PutStatusFave(ctx context.Context, fave *gtsmodel.StatusFave) error { - return s.state.Caches.GTS.StatusFave().Store(fave, func() error { + return s.state.Caches.GTS.StatusFave.Store(fave, func() error { _, err := s.db. NewInsert(). Model(fave). @@ -230,10 +275,10 @@ func (s *statusFaveDB) DeleteStatusFaveByID(ctx context.Context, id string) erro if statusID != "" { // Invalidate any cached status faves for this status. - s.state.Caches.GTS.StatusFave().Invalidate("ID", id) + s.state.Caches.GTS.StatusFave.Invalidate("ID", id) // Invalidate any cached status fave IDs for this status. - s.state.Caches.GTS.StatusFaveIDs().Invalidate(statusID) + s.state.Caches.GTS.StatusFaveIDs.Invalidate(statusID) } return nil @@ -270,17 +315,15 @@ func (s *statusFaveDB) DeleteStatusFaves(ctx context.Context, targetAccountID st return err } - // Collate (deduplicating) status IDs. - statusIDs = collate(func(i int) string { - return statusIDs[i] - }, len(statusIDs)) + // Deduplicate determined status IDs. + statusIDs = util.Deduplicate(statusIDs) for _, id := range statusIDs { // Invalidate any cached status faves for this status. - s.state.Caches.GTS.StatusFave().Invalidate("ID", id) + s.state.Caches.GTS.StatusFave.Invalidate("ID", id) // Invalidate any cached status fave IDs for this status. - s.state.Caches.GTS.StatusFaveIDs().Invalidate(id) + s.state.Caches.GTS.StatusFaveIDs.Invalidate(id) } return nil @@ -296,10 +339,10 @@ func (s *statusFaveDB) DeleteStatusFavesForStatus(ctx context.Context, statusID } // Invalidate any cached status faves for this status. - s.state.Caches.GTS.StatusFave().Invalidate("ID", statusID) + s.state.Caches.GTS.StatusFave.Invalidate("ID", statusID) // Invalidate any cached status fave IDs for this status. - s.state.Caches.GTS.StatusFaveIDs().Invalidate(statusID) + s.state.Caches.GTS.StatusFaveIDs.Invalidate(statusID) return nil } |