mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-07 17:58:07 -06:00
[performance] cache follow, follow request and block ID lists (#2027)
This commit is contained in:
parent
de148e9f9f
commit
ed2477ebea
29 changed files with 1283 additions and 335 deletions
|
|
@ -104,8 +104,6 @@ type Account interface {
|
|||
// In the case of no statuses, this function will return db.ErrNoEntries.
|
||||
GetAccountWebStatuses(ctx context.Context, accountID string, limit int, maxID string) ([]*gtsmodel.Status, error)
|
||||
|
||||
GetAccountBlocks(ctx context.Context, accountID string, maxID string, sinceID string, limit int) ([]*gtsmodel.Account, string, string, error)
|
||||
|
||||
// GetAccountLastPosted simply gets the timestamp of the most recent post by the account.
|
||||
//
|
||||
// If webOnly is true, then the time of the last non-reply, non-boost, public status of the account will be returned.
|
||||
|
|
|
|||
|
|
@ -694,46 +694,6 @@ func (a *accountDB) GetAccountWebStatuses(ctx context.Context, accountID string,
|
|||
return a.statusesFromIDs(ctx, statusIDs)
|
||||
}
|
||||
|
||||
func (a *accountDB) GetAccountBlocks(ctx context.Context, accountID string, maxID string, sinceID string, limit int) ([]*gtsmodel.Account, string, string, error) {
|
||||
blocks := []*gtsmodel.Block{}
|
||||
|
||||
fq := a.db.
|
||||
NewSelect().
|
||||
Model(&blocks).
|
||||
Where("? = ?", bun.Ident("block.account_id"), accountID).
|
||||
Relation("TargetAccount").
|
||||
Order("block.id DESC")
|
||||
|
||||
if maxID != "" {
|
||||
fq = fq.Where("? < ?", bun.Ident("block.id"), maxID)
|
||||
}
|
||||
|
||||
if sinceID != "" {
|
||||
fq = fq.Where("? > ?", bun.Ident("block.id"), sinceID)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
fq = fq.Limit(limit)
|
||||
}
|
||||
|
||||
if err := fq.Scan(ctx); err != nil {
|
||||
return nil, "", "", a.db.ProcessError(err)
|
||||
}
|
||||
|
||||
if len(blocks) == 0 {
|
||||
return nil, "", "", db.ErrNoEntries
|
||||
}
|
||||
|
||||
accounts := []*gtsmodel.Account{}
|
||||
for _, b := range blocks {
|
||||
accounts = append(accounts, b.TargetAccount)
|
||||
}
|
||||
|
||||
nextMaxID := blocks[len(blocks)-1].ID
|
||||
prevMinID := blocks[0].ID
|
||||
return accounts, nextMaxID, prevMinID, nil
|
||||
}
|
||||
|
||||
func (a *accountDB) statusesFromIDs(ctx context.Context, statusIDs []string) ([]*gtsmodel.Status, error) {
|
||||
// Catch case of no statuses early
|
||||
if len(statusIDs) == 0 {
|
||||
|
|
|
|||
|
|
@ -126,16 +126,12 @@ func (e *emojiDB) DeleteEmojiByID(ctx context.Context, id string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Prepare SELECT accounts query.
|
||||
aq := tx.NewSelect().
|
||||
Table("accounts").
|
||||
Column("id")
|
||||
|
||||
// Append a WHERE LIKE clause to the query
|
||||
// Prepare a SELECT query with a WHERE LIKE
|
||||
// that checks the `emoji` column for any
|
||||
// text containing this specific emoji ID.
|
||||
//
|
||||
// (see GetStatusesUsingEmoji() for details.)
|
||||
aq := tx.NewSelect().Table("accounts").Column("id")
|
||||
aq = whereLike(aq, "emojis", id)
|
||||
|
||||
// Select all accounts using this emoji into accountIDss.
|
||||
|
|
@ -170,16 +166,12 @@ func (e *emojiDB) DeleteEmojiByID(ctx context.Context, id string) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Prepare SELECT statuses query.
|
||||
sq := tx.NewSelect().
|
||||
Table("statuses").
|
||||
Column("id")
|
||||
|
||||
// Append a WHERE LIKE clause to the query
|
||||
// Prepare a SELECT query with a WHERE LIKE
|
||||
// that checks the `emoji` column for any
|
||||
// text containing this specific emoji ID.
|
||||
//
|
||||
// (see GetStatusesUsingEmoji() for details.)
|
||||
sq := tx.NewSelect().Table("statuses").Column("id")
|
||||
sq = whereLike(sq, "emojis", id)
|
||||
|
||||
// Select all statuses using this emoji into statusIDs.
|
||||
|
|
|
|||
|
|
@ -189,11 +189,10 @@ func (l *listDB) DeleteListByID(ctx context.Context, id string) error {
|
|||
gtscontext.SetBarebones(ctx),
|
||||
id,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, db.ErrNoEntries) {
|
||||
// Already gone.
|
||||
return nil
|
||||
}
|
||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||
// NOTE: even if db.ErrNoEntries is returned, we
|
||||
// still run the below transaction to ensure related
|
||||
// objects are appropriately deleted.
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,8 +106,6 @@ func (m *mediaDB) UpdateAttachment(ctx context.Context, media *gtsmodel.MediaAtt
|
|||
}
|
||||
|
||||
func (m *mediaDB) DeleteAttachment(ctx context.Context, id string) error {
|
||||
defer m.state.Caches.GTS.Media().Invalidate("ID", id)
|
||||
|
||||
// Load media into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -120,10 +118,8 @@ func (m *mediaDB) DeleteAttachment(ctx context.Context, id string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
invalidateAccount bool
|
||||
invalidateStatus bool
|
||||
)
|
||||
// On return, ensure that media with ID is invalidated.
|
||||
defer m.state.Caches.GTS.Media().Invalidate("ID", id)
|
||||
|
||||
// Delete media attachment in new transaction.
|
||||
err = m.db.RunInTx(ctx, func(tx bun.Tx) error {
|
||||
|
|
@ -161,9 +157,6 @@ func (m *mediaDB) DeleteAttachment(ctx context.Context, id string) error {
|
|||
if _, err := set(q).Exec(ctx); err != nil {
|
||||
return gtserror.Newf("error updating account: %w", err)
|
||||
}
|
||||
|
||||
// Mark as needing invalidate.
|
||||
invalidateAccount = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -178,33 +171,18 @@ func (m *mediaDB) DeleteAttachment(ctx context.Context, id string) error {
|
|||
return gtserror.Newf("error selecting status: %w", err)
|
||||
}
|
||||
|
||||
// Get length of attachments beforehand.
|
||||
before := len(status.AttachmentIDs)
|
||||
|
||||
for i := 0; i < len(status.AttachmentIDs); {
|
||||
if status.AttachmentIDs[i] == id {
|
||||
// Remove this reference to deleted attachment ID.
|
||||
copy(status.AttachmentIDs[i:], status.AttachmentIDs[i+1:])
|
||||
status.AttachmentIDs = status.AttachmentIDs[:len(status.AttachmentIDs)-1]
|
||||
continue
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
if before != len(status.AttachmentIDs) {
|
||||
// Note: this accounts for status not found.
|
||||
if updatedIDs := dropID(status.AttachmentIDs, id); // nocollapse
|
||||
len(updatedIDs) != len(status.AttachmentIDs) {
|
||||
// Note: this handles not found.
|
||||
//
|
||||
// Attachments changed, update the status.
|
||||
if _, err := tx.NewUpdate().
|
||||
Table("statuses").
|
||||
Where("? = ?", bun.Ident("id"), status.ID).
|
||||
Set("? = ?", bun.Ident("attachment_ids"), status.AttachmentIDs).
|
||||
Set("? = ?", bun.Ident("attachment_ids"), updatedIDs).
|
||||
Exec(ctx); err != nil {
|
||||
return gtserror.Newf("error updating status: %w", err)
|
||||
}
|
||||
|
||||
// Mark as needing invalidate.
|
||||
invalidateStatus = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,16 +197,6 @@ func (m *mediaDB) DeleteAttachment(ctx context.Context, id string) error {
|
|||
return nil
|
||||
})
|
||||
|
||||
if invalidateAccount {
|
||||
// The account for given ID will have been updated in transaction.
|
||||
m.state.Caches.GTS.Account().Invalidate("ID", media.AccountID)
|
||||
}
|
||||
|
||||
if invalidateStatus {
|
||||
// The status for given ID will have been updated in transaction.
|
||||
m.state.Caches.GTS.Status().Invalidate("ID", media.StatusID)
|
||||
}
|
||||
|
||||
return m.db.ProcessError(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@ package bundb
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
|
@ -45,7 +46,7 @@ func (r *relationshipDB) GetRelationship(ctx context.Context, requestingAccount
|
|||
targetAccount,
|
||||
)
|
||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||
return nil, fmt.Errorf("GetRelationship: error fetching follow: %w", err)
|
||||
return nil, gtserror.Newf("error fetching follow: %w", err)
|
||||
}
|
||||
|
||||
if follow != nil {
|
||||
|
|
@ -61,7 +62,7 @@ func (r *relationshipDB) GetRelationship(ctx context.Context, requestingAccount
|
|||
requestingAccount,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetRelationship: error checking followedBy: %w", err)
|
||||
return nil, gtserror.Newf("error checking followedBy: %w", err)
|
||||
}
|
||||
|
||||
// check if requesting has follow requested target
|
||||
|
|
@ -70,19 +71,19 @@ func (r *relationshipDB) GetRelationship(ctx context.Context, requestingAccount
|
|||
targetAccount,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetRelationship: error checking requested: %w", err)
|
||||
return nil, gtserror.Newf("error checking requested: %w", err)
|
||||
}
|
||||
|
||||
// check if the requesting account is blocking the target account
|
||||
rel.Blocking, err = r.IsBlocked(ctx, requestingAccount, targetAccount)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetRelationship: error checking blocking: %w", err)
|
||||
return nil, gtserror.Newf("error checking blocking: %w", err)
|
||||
}
|
||||
|
||||
// check if the requesting account is blocked by the target account
|
||||
rel.BlockedBy, err = r.IsBlocked(ctx, targetAccount, requestingAccount)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetRelationship: error checking blockedBy: %w", err)
|
||||
return nil, gtserror.Newf("error checking blockedBy: %w", err)
|
||||
}
|
||||
|
||||
// retrieve a note by the requesting account on the target account, if there is one
|
||||
|
|
@ -92,7 +93,7 @@ func (r *relationshipDB) GetRelationship(ctx context.Context, requestingAccount
|
|||
targetAccount,
|
||||
)
|
||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||
return nil, fmt.Errorf("GetRelationship: error fetching note: %w", err)
|
||||
return nil, gtserror.Newf("error fetching note: %w", err)
|
||||
}
|
||||
if note != nil {
|
||||
rel.Note = note.Comment
|
||||
|
|
@ -102,87 +103,186 @@ func (r *relationshipDB) GetRelationship(ctx context.Context, requestingAccount
|
|||
}
|
||||
|
||||
func (r *relationshipDB) GetAccountFollows(ctx context.Context, accountID string) ([]*gtsmodel.Follow, error) {
|
||||
var followIDs []string
|
||||
if err := newSelectFollows(r.db, accountID).
|
||||
Scan(ctx, &followIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
followIDs, err := r.getAccountFollowIDs(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.GetFollowsByIDs(ctx, followIDs)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) GetAccountLocalFollows(ctx context.Context, accountID string) ([]*gtsmodel.Follow, error) {
|
||||
var followIDs []string
|
||||
if err := newSelectLocalFollows(r.db, accountID).
|
||||
Scan(ctx, &followIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
followIDs, err := r.getAccountLocalFollowIDs(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.GetFollowsByIDs(ctx, followIDs)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) GetAccountFollowers(ctx context.Context, accountID string) ([]*gtsmodel.Follow, error) {
|
||||
var followIDs []string
|
||||
if err := newSelectFollowers(r.db, accountID).
|
||||
Scan(ctx, &followIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
followerIDs, err := r.getAccountFollowerIDs(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.GetFollowsByIDs(ctx, followIDs)
|
||||
return r.GetFollowsByIDs(ctx, followerIDs)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) GetAccountLocalFollowers(ctx context.Context, accountID string) ([]*gtsmodel.Follow, error) {
|
||||
var followIDs []string
|
||||
if err := newSelectLocalFollowers(r.db, accountID).
|
||||
Scan(ctx, &followIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
followerIDs, err := r.getAccountLocalFollowerIDs(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.GetFollowsByIDs(ctx, followIDs)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountFollows(ctx context.Context, accountID string) (int, error) {
|
||||
n, err := newSelectFollows(r.db, accountID).Count(ctx)
|
||||
return n, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountLocalFollows(ctx context.Context, accountID string) (int, error) {
|
||||
n, err := newSelectLocalFollows(r.db, accountID).Count(ctx)
|
||||
return n, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountFollowers(ctx context.Context, accountID string) (int, error) {
|
||||
n, err := newSelectFollowers(r.db, accountID).Count(ctx)
|
||||
return n, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountLocalFollowers(ctx context.Context, accountID string) (int, error) {
|
||||
n, err := newSelectLocalFollowers(r.db, accountID).Count(ctx)
|
||||
return n, r.db.ProcessError(err)
|
||||
return r.GetFollowsByIDs(ctx, followerIDs)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) GetAccountFollowRequests(ctx context.Context, accountID string) ([]*gtsmodel.FollowRequest, error) {
|
||||
var followReqIDs []string
|
||||
if err := newSelectFollowRequests(r.db, accountID).
|
||||
Scan(ctx, &followReqIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
followReqIDs, err := r.getAccountFollowRequestIDs(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.GetFollowRequestsByIDs(ctx, followReqIDs)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) GetAccountFollowRequesting(ctx context.Context, accountID string) ([]*gtsmodel.FollowRequest, error) {
|
||||
var followReqIDs []string
|
||||
if err := newSelectFollowRequesting(r.db, accountID).
|
||||
Scan(ctx, &followReqIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
followReqIDs, err := r.getAccountFollowRequestingIDs(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.GetFollowRequestsByIDs(ctx, followReqIDs)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) GetAccountBlocks(ctx context.Context, accountID string, page *paging.Pager) ([]*gtsmodel.Block, error) {
|
||||
// Load block IDs from cache with database loader callback.
|
||||
blockIDs, err := r.state.Caches.GTS.BlockIDs().LoadRange(accountID, func() ([]string, error) {
|
||||
var blockIDs []string
|
||||
|
||||
// Block IDs not in cache, perform DB query!
|
||||
q := newSelectBlocks(r.db, accountID)
|
||||
if _, err := q.Exec(ctx, &blockIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
return blockIDs, nil
|
||||
}, page.PageDesc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert these IDs to full block objects.
|
||||
return r.GetBlocksByIDs(ctx, blockIDs)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountFollows(ctx context.Context, accountID string) (int, error) {
|
||||
followIDs, err := r.getAccountFollowIDs(ctx, accountID)
|
||||
return len(followIDs), err
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountLocalFollows(ctx context.Context, accountID string) (int, error) {
|
||||
followIDs, err := r.getAccountLocalFollowIDs(ctx, accountID)
|
||||
return len(followIDs), err
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountFollowers(ctx context.Context, accountID string) (int, error) {
|
||||
followerIDs, err := r.getAccountFollowerIDs(ctx, accountID)
|
||||
return len(followerIDs), err
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountLocalFollowers(ctx context.Context, accountID string) (int, error) {
|
||||
followerIDs, err := r.getAccountLocalFollowerIDs(ctx, accountID)
|
||||
return len(followerIDs), err
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountFollowRequests(ctx context.Context, accountID string) (int, error) {
|
||||
n, err := newSelectFollowRequests(r.db, accountID).Count(ctx)
|
||||
return n, r.db.ProcessError(err)
|
||||
followReqIDs, err := r.getAccountFollowRequestIDs(ctx, accountID)
|
||||
return len(followReqIDs), err
|
||||
}
|
||||
|
||||
func (r *relationshipDB) CountAccountFollowRequesting(ctx context.Context, accountID string) (int, error) {
|
||||
n, err := newSelectFollowRequesting(r.db, accountID).Count(ctx)
|
||||
return n, r.db.ProcessError(err)
|
||||
followReqIDs, err := r.getAccountFollowRequestingIDs(ctx, accountID)
|
||||
return len(followReqIDs), err
|
||||
}
|
||||
|
||||
func (r *relationshipDB) getAccountFollowIDs(ctx context.Context, accountID string) ([]string, error) {
|
||||
return r.state.Caches.GTS.FollowIDs().Load(">"+accountID, func() ([]string, error) {
|
||||
var followIDs []string
|
||||
|
||||
// Follow IDs not in cache, perform DB query!
|
||||
q := newSelectFollows(r.db, accountID)
|
||||
if _, err := q.Exec(ctx, &followIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
return followIDs, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (r *relationshipDB) getAccountLocalFollowIDs(ctx context.Context, accountID string) ([]string, error) {
|
||||
return r.state.Caches.GTS.FollowIDs().Load("l>"+accountID, func() ([]string, error) {
|
||||
var followIDs []string
|
||||
|
||||
// Follow IDs not in cache, perform DB query!
|
||||
q := newSelectLocalFollows(r.db, accountID)
|
||||
if _, err := q.Exec(ctx, &followIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
return followIDs, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (r *relationshipDB) getAccountFollowerIDs(ctx context.Context, accountID string) ([]string, error) {
|
||||
return r.state.Caches.GTS.FollowIDs().Load("<"+accountID, func() ([]string, error) {
|
||||
var followIDs []string
|
||||
|
||||
// Follow IDs not in cache, perform DB query!
|
||||
q := newSelectFollowers(r.db, accountID)
|
||||
if _, err := q.Exec(ctx, &followIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
return followIDs, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (r *relationshipDB) getAccountLocalFollowerIDs(ctx context.Context, accountID string) ([]string, error) {
|
||||
return r.state.Caches.GTS.FollowIDs().Load("l<"+accountID, func() ([]string, error) {
|
||||
var followIDs []string
|
||||
|
||||
// Follow IDs not in cache, perform DB query!
|
||||
q := newSelectLocalFollowers(r.db, accountID)
|
||||
if _, err := q.Exec(ctx, &followIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
return followIDs, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (r *relationshipDB) getAccountFollowRequestIDs(ctx context.Context, accountID string) ([]string, error) {
|
||||
return r.state.Caches.GTS.FollowRequestIDs().Load(">"+accountID, func() ([]string, error) {
|
||||
var followReqIDs []string
|
||||
|
||||
// Follow request IDs not in cache, perform DB query!
|
||||
q := newSelectFollowRequests(r.db, accountID)
|
||||
if _, err := q.Exec(ctx, &followReqIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
return followReqIDs, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (r *relationshipDB) getAccountFollowRequestingIDs(ctx context.Context, accountID string) ([]string, error) {
|
||||
return r.state.Caches.GTS.FollowRequestIDs().Load("<"+accountID, func() ([]string, error) {
|
||||
var followReqIDs []string
|
||||
|
||||
// Follow request IDs not in cache, perform DB query!
|
||||
q := newSelectFollowRequesting(r.db, accountID)
|
||||
if _, err := q.Exec(ctx, &followReqIDs); err != nil {
|
||||
return nil, r.db.ProcessError(err)
|
||||
}
|
||||
|
||||
return followReqIDs, nil
|
||||
})
|
||||
}
|
||||
|
||||
// newSelectFollowRequests returns a new select query for all rows in the follow_requests table with target_account_id = accountID.
|
||||
|
|
@ -256,3 +356,12 @@ func newSelectLocalFollowers(db *WrappedDB, accountID string) *bun.SelectQuery {
|
|||
).
|
||||
OrderExpr("? DESC", bun.Ident("updated_at"))
|
||||
}
|
||||
|
||||
// newSelectBlocks returns a new select query for all rows in the blocks table with account_id = accountID.
|
||||
func newSelectBlocks(db *WrappedDB, accountID string) *bun.SelectQuery {
|
||||
return db.NewSelect().
|
||||
TableExpr("?", bun.Ident("blocks")).
|
||||
ColumnExpr("?", bun.Ident("?")).
|
||||
Where("? = ?", bun.Ident("account_id"), accountID).
|
||||
OrderExpr("? DESC", bun.Ident("updated_at"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"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/uptrace/bun"
|
||||
)
|
||||
|
||||
|
|
@ -97,6 +98,25 @@ func (r *relationshipDB) GetBlock(ctx context.Context, sourceAccountID string, t
|
|||
)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) GetBlocksByIDs(ctx context.Context, ids []string) ([]*gtsmodel.Block, error) {
|
||||
// Preallocate slice of expected length.
|
||||
blocks := make([]*gtsmodel.Block, 0, len(ids))
|
||||
|
||||
for _, id := range ids {
|
||||
// Fetch block model for this ID.
|
||||
block, err := r.GetBlockByID(ctx, id)
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "error getting block %q: %v", id, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Append to return slice.
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
func (r *relationshipDB) getBlock(ctx context.Context, lookup string, dbQuery func(*gtsmodel.Block) error, keyParts ...any) (*gtsmodel.Block, error) {
|
||||
// Fetch block from cache with loader callback
|
||||
block, err := r.state.Caches.GTS.Block().Load(lookup, func() (*gtsmodel.Block, error) {
|
||||
|
|
@ -148,8 +168,6 @@ func (r *relationshipDB) PutBlock(ctx context.Context, block *gtsmodel.Block) er
|
|||
}
|
||||
|
||||
func (r *relationshipDB) DeleteBlockByID(ctx context.Context, id string) error {
|
||||
defer r.state.Caches.GTS.Block().Invalidate("ID", id)
|
||||
|
||||
// Load block into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -162,6 +180,9 @@ func (r *relationshipDB) DeleteBlockByID(ctx context.Context, id string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Drop this now-cached block on return after delete.
|
||||
defer r.state.Caches.GTS.Block().Invalidate("ID", id)
|
||||
|
||||
// Finally delete block from DB.
|
||||
_, err = r.db.NewDelete().
|
||||
Table("blocks").
|
||||
|
|
@ -171,8 +192,6 @@ func (r *relationshipDB) DeleteBlockByID(ctx context.Context, id string) error {
|
|||
}
|
||||
|
||||
func (r *relationshipDB) DeleteBlockByURI(ctx context.Context, uri string) error {
|
||||
defer r.state.Caches.GTS.Block().Invalidate("URI", uri)
|
||||
|
||||
// Load block into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -185,6 +204,9 @@ func (r *relationshipDB) DeleteBlockByURI(ctx context.Context, uri string) error
|
|||
return err
|
||||
}
|
||||
|
||||
// Drop this now-cached block on return after delete.
|
||||
defer r.state.Caches.GTS.Block().Invalidate("URI", uri)
|
||||
|
||||
// Finally delete block from DB.
|
||||
_, err = r.db.NewDelete().
|
||||
Table("blocks").
|
||||
|
|
@ -211,10 +233,9 @@ func (r *relationshipDB) DeleteAccountBlocks(ctx context.Context, accountID stri
|
|||
}
|
||||
|
||||
defer func() {
|
||||
// Invalidate all IDs on return.
|
||||
for _, id := range blockIDs {
|
||||
r.state.Caches.GTS.Block().Invalidate("ID", id)
|
||||
}
|
||||
// Invalidate all account's incoming / outoing blocks on return.
|
||||
r.state.Caches.GTS.Block().Invalidate("AccountID", accountID)
|
||||
r.state.Caches.GTS.Block().Invalidate("TargetAccountID", accountID)
|
||||
}()
|
||||
|
||||
// Load all blocks into cache, this *really* isn't great
|
||||
|
|
|
|||
|
|
@ -233,8 +233,6 @@ func (r *relationshipDB) deleteFollow(ctx context.Context, id string) error {
|
|||
}
|
||||
|
||||
func (r *relationshipDB) DeleteFollow(ctx context.Context, sourceAccountID string, targetAccountID string) error {
|
||||
defer r.state.Caches.GTS.Follow().Invalidate("AccountID.TargetAccountID", sourceAccountID, targetAccountID)
|
||||
|
||||
// Load follow into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -251,13 +249,14 @@ func (r *relationshipDB) DeleteFollow(ctx context.Context, sourceAccountID strin
|
|||
return err
|
||||
}
|
||||
|
||||
// Drop this now-cached follow on return after delete.
|
||||
defer r.state.Caches.GTS.Follow().Invalidate("AccountID.TargetAccountID", sourceAccountID, targetAccountID)
|
||||
|
||||
// Finally delete follow from DB.
|
||||
return r.deleteFollow(ctx, follow.ID)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) DeleteFollowByID(ctx context.Context, id string) error {
|
||||
defer r.state.Caches.GTS.Follow().Invalidate("ID", id)
|
||||
|
||||
// Load follow into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -270,13 +269,14 @@ func (r *relationshipDB) DeleteFollowByID(ctx context.Context, id string) error
|
|||
return err
|
||||
}
|
||||
|
||||
// Drop this now-cached follow on return after delete.
|
||||
defer r.state.Caches.GTS.Follow().Invalidate("ID", id)
|
||||
|
||||
// Finally delete follow from DB.
|
||||
return r.deleteFollow(ctx, follow.ID)
|
||||
}
|
||||
|
||||
func (r *relationshipDB) DeleteFollowByURI(ctx context.Context, uri string) error {
|
||||
defer r.state.Caches.GTS.Follow().Invalidate("URI", uri)
|
||||
|
||||
// Load follow into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -289,6 +289,9 @@ func (r *relationshipDB) DeleteFollowByURI(ctx context.Context, uri string) erro
|
|||
return err
|
||||
}
|
||||
|
||||
// Drop this now-cached follow on return after delete.
|
||||
defer r.state.Caches.GTS.Follow().Invalidate("URI", uri)
|
||||
|
||||
// Finally delete follow from DB.
|
||||
return r.deleteFollow(ctx, follow.ID)
|
||||
}
|
||||
|
|
@ -312,10 +315,9 @@ func (r *relationshipDB) DeleteAccountFollows(ctx context.Context, accountID str
|
|||
}
|
||||
|
||||
defer func() {
|
||||
// Invalidate all IDs on return.
|
||||
for _, id := range followIDs {
|
||||
r.state.Caches.GTS.Follow().Invalidate("ID", id)
|
||||
}
|
||||
// Invalidate all account's incoming / outoing follows on return.
|
||||
r.state.Caches.GTS.Follow().Invalidate("AccountID", accountID)
|
||||
r.state.Caches.GTS.Follow().Invalidate("TargetAccountID", accountID)
|
||||
}()
|
||||
|
||||
// Load all follows into cache, this *really* isn't great
|
||||
|
|
|
|||
|
|
@ -208,9 +208,6 @@ func (r *relationshipDB) AcceptFollowRequest(ctx context.Context, sourceAccountI
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Invalidate follow request from cache lookups on return.
|
||||
defer r.state.Caches.GTS.FollowRequest().Invalidate("ID", followReq.ID)
|
||||
|
||||
// Delete original follow request.
|
||||
if _, err := r.db.
|
||||
NewDelete().
|
||||
|
|
@ -243,8 +240,6 @@ func (r *relationshipDB) RejectFollowRequest(ctx context.Context, sourceAccountI
|
|||
}
|
||||
|
||||
func (r *relationshipDB) DeleteFollowRequest(ctx context.Context, sourceAccountID string, targetAccountID string) error {
|
||||
defer r.state.Caches.GTS.FollowRequest().Invalidate("AccountID.TargetAccountID", sourceAccountID, targetAccountID)
|
||||
|
||||
// Load followreq into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -261,6 +256,9 @@ func (r *relationshipDB) DeleteFollowRequest(ctx context.Context, sourceAccountI
|
|||
return err
|
||||
}
|
||||
|
||||
// Drop this now-cached follow request on return after delete.
|
||||
defer r.state.Caches.GTS.FollowRequest().Invalidate("AccountID.TargetAccountID", sourceAccountID, targetAccountID)
|
||||
|
||||
// Finally delete followreq from DB.
|
||||
_, err = r.db.NewDelete().
|
||||
Table("follow_requests").
|
||||
|
|
@ -270,8 +268,6 @@ func (r *relationshipDB) DeleteFollowRequest(ctx context.Context, sourceAccountI
|
|||
}
|
||||
|
||||
func (r *relationshipDB) DeleteFollowRequestByID(ctx context.Context, id string) error {
|
||||
defer r.state.Caches.GTS.FollowRequest().Invalidate("ID", id)
|
||||
|
||||
// Load followreq into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -284,6 +280,9 @@ func (r *relationshipDB) DeleteFollowRequestByID(ctx context.Context, id string)
|
|||
return err
|
||||
}
|
||||
|
||||
// Drop this now-cached follow request on return after delete.
|
||||
defer r.state.Caches.GTS.FollowRequest().Invalidate("ID", id)
|
||||
|
||||
// Finally delete followreq from DB.
|
||||
_, err = r.db.NewDelete().
|
||||
Table("follow_requests").
|
||||
|
|
@ -293,8 +292,6 @@ func (r *relationshipDB) DeleteFollowRequestByID(ctx context.Context, id string)
|
|||
}
|
||||
|
||||
func (r *relationshipDB) DeleteFollowRequestByURI(ctx context.Context, uri string) error {
|
||||
defer r.state.Caches.GTS.FollowRequest().Invalidate("URI", uri)
|
||||
|
||||
// Load followreq into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -307,6 +304,9 @@ func (r *relationshipDB) DeleteFollowRequestByURI(ctx context.Context, uri strin
|
|||
return err
|
||||
}
|
||||
|
||||
// Drop this now-cached follow request on return after delete.
|
||||
defer r.state.Caches.GTS.FollowRequest().Invalidate("URI", uri)
|
||||
|
||||
// Finally delete followreq from DB.
|
||||
_, err = r.db.NewDelete().
|
||||
Table("follow_requests").
|
||||
|
|
@ -334,10 +334,9 @@ func (r *relationshipDB) DeleteAccountFollowRequests(ctx context.Context, accoun
|
|||
}
|
||||
|
||||
defer func() {
|
||||
// Invalidate all IDs on return.
|
||||
for _, id := range followReqIDs {
|
||||
r.state.Caches.GTS.FollowRequest().Invalidate("ID", id)
|
||||
}
|
||||
// Invalidate all account's incoming / outoing follow requests on return.
|
||||
r.state.Caches.GTS.FollowRequest().Invalidate("AccountID", accountID)
|
||||
r.state.Caches.GTS.FollowRequest().Invalidate("TargetAccountID", accountID)
|
||||
}()
|
||||
|
||||
// Load all followreqs into cache, this *really* isn't
|
||||
|
|
|
|||
|
|
@ -381,8 +381,6 @@ func (s *statusDB) UpdateStatus(ctx context.Context, status *gtsmodel.Status, co
|
|||
}
|
||||
|
||||
func (s *statusDB) DeleteStatusByID(ctx context.Context, id string) error {
|
||||
defer s.state.Caches.GTS.Status().Invalidate("ID", id)
|
||||
|
||||
// Load status into cache before attempting a delete,
|
||||
// as we need it cached in order to trigger the invalidate
|
||||
// callback. This in turn invalidates others.
|
||||
|
|
@ -397,6 +395,9 @@ func (s *statusDB) DeleteStatusByID(ctx context.Context, id string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// On return ensure status invalidated from cache.
|
||||
defer s.state.Caches.GTS.Status().Invalidate("ID", id)
|
||||
|
||||
return s.db.RunInTx(ctx, func(tx bun.Tx) error {
|
||||
// delete links between this status and any emojis it uses
|
||||
if _, err := tx.
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||
)
|
||||
|
||||
// Relationship contains functions for getting or modifying the relationship between two accounts.
|
||||
|
|
@ -166,6 +167,9 @@ type Relationship interface {
|
|||
// CountAccountFollowerRequests returns number of follow requests originating from the given account.
|
||||
CountAccountFollowRequesting(ctx context.Context, accountID string) (int, error)
|
||||
|
||||
// GetAccountBlocks returns all blocks originating from the given account, with given optional paging parameters.
|
||||
GetAccountBlocks(ctx context.Context, accountID string, paging *paging.Pager) ([]*gtsmodel.Block, error)
|
||||
|
||||
// GetNote gets a private note from a source account on a target account, if it exists.
|
||||
GetNote(ctx context.Context, sourceAccountID string, targetAccountID string) (*gtsmodel.AccountNote, error)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue