diff options
author | 2023-03-28 14:03:14 +0100 | |
---|---|---|
committer | 2023-03-28 14:03:14 +0100 | |
commit | de6e3e5f2a8ea639d76e310a11cb9bc093fef3a9 (patch) | |
tree | e2b7044e22c943425a4d351a02f862fbde783657 /internal/db/bundb/account.go | |
parent | [feature] Add list command to admin account (#1648) (diff) | |
download | gotosocial-de6e3e5f2a8ea639d76e310a11cb9bc093fef3a9.tar.xz |
[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>
Diffstat (limited to 'internal/db/bundb/account.go')
-rw-r--r-- | internal/db/bundb/account.go | 147 |
1 files changed, 122 insertions, 25 deletions
diff --git a/internal/db/bundb/account.go b/internal/db/bundb/account.go index df73168e2..ccf7aaa46 100644 --- a/internal/db/bundb/account.go +++ b/internal/db/bundb/account.go @@ -20,11 +20,13 @@ package bundb import ( "context" "errors" + "fmt" "strings" "time" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/state" @@ -37,18 +39,15 @@ type accountDB struct { state *state.State } -func (a *accountDB) newAccountQ(account *gtsmodel.Account) *bun.SelectQuery { - return a.conn. - NewSelect(). - Model(account) -} - func (a *accountDB) GetAccountByID(ctx context.Context, id string) (*gtsmodel.Account, db.Error) { return a.getAccount( ctx, "ID", func(account *gtsmodel.Account) error { - return a.newAccountQ(account).Where("? = ?", bun.Ident("account.id"), id).Scan(ctx) + return a.conn.NewSelect(). + Model(account). + Where("? = ?", bun.Ident("account.id"), id). + Scan(ctx) }, id, ) @@ -59,7 +58,10 @@ func (a *accountDB) GetAccountByURI(ctx context.Context, uri string) (*gtsmodel. ctx, "URI", func(account *gtsmodel.Account) error { - return a.newAccountQ(account).Where("? = ?", bun.Ident("account.uri"), uri).Scan(ctx) + return a.conn.NewSelect(). + Model(account). + Where("? = ?", bun.Ident("account.uri"), uri). + Scan(ctx) }, uri, ) @@ -70,7 +72,10 @@ func (a *accountDB) GetAccountByURL(ctx context.Context, url string) (*gtsmodel. ctx, "URL", func(account *gtsmodel.Account) error { - return a.newAccountQ(account).Where("? = ?", bun.Ident("account.url"), url).Scan(ctx) + return a.conn.NewSelect(). + Model(account). + Where("? = ?", bun.Ident("account.url"), url). + Scan(ctx) }, url, ) @@ -81,7 +86,8 @@ func (a *accountDB) GetAccountByUsernameDomain(ctx context.Context, username str ctx, "Username.Domain", func(account *gtsmodel.Account) error { - q := a.newAccountQ(account) + q := a.conn.NewSelect(). + Model(account) if domain != "" { q = q. @@ -105,12 +111,71 @@ func (a *accountDB) GetAccountByPubkeyID(ctx context.Context, id string) (*gtsmo ctx, "PublicKeyURI", func(account *gtsmodel.Account) error { - return a.newAccountQ(account).Where("? = ?", bun.Ident("account.public_key_uri"), id).Scan(ctx) + return a.conn.NewSelect(). + Model(account). + Where("? = ?", bun.Ident("account.public_key_uri"), id). + Scan(ctx) }, id, ) } +func (a *accountDB) GetAccountByInboxURI(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) { + return a.getAccount( + ctx, + "InboxURI", + func(account *gtsmodel.Account) error { + return a.conn.NewSelect(). + Model(account). + Where("? = ?", bun.Ident("account.inbox_uri"), uri). + Scan(ctx) + }, + uri, + ) +} + +func (a *accountDB) GetAccountByOutboxURI(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) { + return a.getAccount( + ctx, + "OutboxURI", + func(account *gtsmodel.Account) error { + return a.conn.NewSelect(). + Model(account). + Where("? = ?", bun.Ident("account.outbox_uri"), uri). + Scan(ctx) + }, + uri, + ) +} + +func (a *accountDB) GetAccountByFollowersURI(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) { + return a.getAccount( + ctx, + "FollowersURI", + func(account *gtsmodel.Account) error { + return a.conn.NewSelect(). + Model(account). + Where("? = ?", bun.Ident("account.followers_uri"), uri). + Scan(ctx) + }, + uri, + ) +} + +func (a *accountDB) GetAccountByFollowingURI(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) { + return a.getAccount( + ctx, + "FollowingURI", + func(account *gtsmodel.Account) error { + return a.conn.NewSelect(). + Model(account). + Where("? = ?", bun.Ident("account.following_uri"), uri). + Scan(ctx) + }, + uri, + ) +} + func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gtsmodel.Account, db.Error) { var username string @@ -141,31 +206,56 @@ func (a *accountDB) getAccount(ctx context.Context, lookup string, dbQuery func( return nil, err } - if account.AvatarMediaAttachmentID != "" { - // Set the account's related avatar - account.AvatarMediaAttachment, err = a.state.DB.GetAttachmentByID(ctx, account.AvatarMediaAttachmentID) + if gtscontext.Barebones(ctx) { + // no need to fully populate. + return account, nil + } + + // Further populate the account fields where applicable. + if err := a.PopulateAccount(ctx, account); err != nil { + return nil, err + } + + return account, nil +} + +func (a *accountDB) PopulateAccount(ctx context.Context, account *gtsmodel.Account) error { + var err error + + if account.AvatarMediaAttachment == nil && account.AvatarMediaAttachmentID != "" { + // Account avatar attachment is not set, fetch from database. + account.AvatarMediaAttachment, err = a.state.DB.GetAttachmentByID( + ctx, // these are already barebones + account.AvatarMediaAttachmentID, + ) if err != nil { - log.Errorf(ctx, "error getting account %s avatar: %v", account.ID, err) + return fmt.Errorf("error populating account avatar: %w", err) } } - if account.HeaderMediaAttachmentID != "" { - // Set the account's related header - account.HeaderMediaAttachment, err = a.state.DB.GetAttachmentByID(ctx, account.HeaderMediaAttachmentID) + if account.HeaderMediaAttachment == nil && account.HeaderMediaAttachmentID != "" { + // Account header attachment is not set, fetch from database. + account.HeaderMediaAttachment, err = a.state.DB.GetAttachmentByID( + ctx, // these are already barebones + account.HeaderMediaAttachmentID, + ) if err != nil { - log.Errorf(ctx, "error getting account %s header: %v", account.ID, err) + return fmt.Errorf("error populating account header: %w", err) } } - if len(account.EmojiIDs) > 0 { - // Set the account's related emojis - account.Emojis, err = a.state.DB.GetEmojisByIDs(ctx, account.EmojiIDs) + if !account.EmojisPopulated() { + // Account emojis are out-of-date with IDs, repopulate. + account.Emojis, err = a.state.DB.GetEmojisByIDs( + ctx, // these are already barebones + account.EmojiIDs, + ) if err != nil { - log.Errorf(ctx, "error getting account %s emojis: %v", account.ID, err) + return fmt.Errorf("error populating account emojis: %w", err) } } - return account, nil + return nil } func (a *accountDB) PutAccount(ctx context.Context, account *gtsmodel.Account) db.Error { @@ -198,7 +288,7 @@ func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account columns = append(columns, "updated_at") } - return a.state.Caches.GTS.Account().Store(account, func() error { + err := a.state.Caches.GTS.Account().Store(account, func() error { // It is safe to run this database transaction within cache.Store // as the cache does not attempt a mutex lock until AFTER hook. // @@ -234,6 +324,11 @@ func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account return err }) }) + if err != nil { + return err + } + + return nil } func (a *accountDB) DeleteAccount(ctx context.Context, id string) db.Error { @@ -258,7 +353,9 @@ func (a *accountDB) DeleteAccount(ctx context.Context, id string) db.Error { return err } + // Invalidate account from database lookups. a.state.Caches.GTS.Account().Invalidate("ID", id) + return nil } |