summaryrefslogtreecommitdiff
path: root/internal/db/bundb/status.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/db/bundb/status.go')
-rw-r--r--internal/db/bundb/status.go179
1 files changed, 127 insertions, 52 deletions
diff --git a/internal/db/bundb/status.go b/internal/db/bundb/status.go
index deec9a118..c2b5546f8 100644
--- a/internal/db/bundb/status.go
+++ b/internal/db/bundb/status.go
@@ -26,6 +26,7 @@ import (
"time"
"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"
@@ -41,7 +42,6 @@ func (s *statusDB) newStatusQ(status interface{}) *bun.SelectQuery {
return s.conn.
NewSelect().
Model(status).
- Relation("Attachments").
Relation("Tags").
Relation("CreatedWithApplication")
}
@@ -102,79 +102,141 @@ func (s *statusDB) getStatus(ctx context.Context, lookup string, dbQuery func(*g
status, err := s.state.Caches.GTS.Status().Load(lookup, func() (*gtsmodel.Status, error) {
var status gtsmodel.Status
- // Not cached! Perform database query
+ // Not cached! Perform database query.
if err := dbQuery(&status); err != nil {
return nil, s.conn.ProcessError(err)
}
- if status.InReplyToID != "" {
- // Also load in-reply-to status
- status.InReplyTo = new(gtsmodel.Status)
- err := s.conn.NewSelect().Model(status.InReplyTo).
- Where("? = ?", bun.Ident("status.id"), status.InReplyToID).
- Scan(ctx)
+ return &status, nil
+ }, keyParts...)
+ if err != nil {
+ return nil, err
+ }
+
+ if gtscontext.Barebones(ctx) {
+ // no need to fully populate.
+ return status, nil
+ }
+
+ // Further populate the status fields where applicable.
+ if err := s.PopulateStatus(ctx, status); err != nil {
+ return nil, err
+ }
+
+ return status, nil
+}
+
+func (s *statusDB) PopulateStatus(ctx context.Context, status *gtsmodel.Status) error {
+ var err error
+
+ if status.Account == nil {
+ // Status author is not set, fetch from database.
+ status.Account, err = s.state.DB.GetAccountByID(
+ gtscontext.SetBarebones(ctx),
+ status.AccountID,
+ )
+ if err != nil {
+ return fmt.Errorf("error populating status author: %w", err)
+ }
+ }
+
+ if status.InReplyToID != "" && status.InReplyTo == nil {
+ // Status parent is not set, fetch from database.
+ status.InReplyTo, err = s.GetStatusByID(
+ gtscontext.SetBarebones(ctx),
+ status.InReplyToID,
+ )
+ if err != nil {
+ return fmt.Errorf("error populating status parent: %w", err)
+ }
+ }
+
+ if status.InReplyToID != "" {
+ if status.InReplyTo == nil {
+ // Status parent is not set, fetch from database.
+ status.InReplyTo, err = s.GetStatusByID(
+ gtscontext.SetBarebones(ctx),
+ status.InReplyToID,
+ )
if err != nil {
- return nil, s.conn.ProcessError(err)
+ return fmt.Errorf("error populating status parent: %w", err)
}
}
- if status.BoostOfID != "" {
- // Also load original boosted status
- status.BoostOf = new(gtsmodel.Status)
- err := s.conn.NewSelect().Model(status.BoostOf).
- Where("? = ?", bun.Ident("status.id"), status.BoostOfID).
- Scan(ctx)
+ if status.InReplyToAccount == nil {
+ // Status parent author is not set, fetch from database.
+ status.InReplyToAccount, err = s.state.DB.GetAccountByID(
+ gtscontext.SetBarebones(ctx),
+ status.InReplyToAccountID,
+ )
if err != nil {
- return nil, s.conn.ProcessError(err)
+ return fmt.Errorf("error populating status parent author: %w", err)
}
}
-
- return &status, nil
- }, keyParts...)
- if err != nil {
- // error already processed
- return nil, err
}
- // Set the status author account
- status.Account, err = s.state.DB.GetAccountByID(ctx, status.AccountID)
- if err != nil {
- return nil, fmt.Errorf("error getting status account: %w", err)
- }
+ if status.BoostOfID != "" {
+ if status.BoostOf == nil {
+ // Status boost is not set, fetch from database.
+ status.BoostOf, err = s.GetStatusByID(
+ gtscontext.SetBarebones(ctx),
+ status.BoostOfID,
+ )
+ if err != nil {
+ return fmt.Errorf("error populating status boost: %w", err)
+ }
+ }
- if id := status.BoostOfAccountID; id != "" {
- // Set boost of status' author account
- status.BoostOfAccount, err = s.state.DB.GetAccountByID(ctx, id)
- if err != nil {
- return nil, fmt.Errorf("error getting boosted status account: %w", err)
+ if status.BoostOfAccount == nil {
+ // Status boost author is not set, fetch from database.
+ status.BoostOfAccount, err = s.state.DB.GetAccountByID(
+ gtscontext.SetBarebones(ctx),
+ status.BoostOfAccountID,
+ )
+ if err != nil {
+ return fmt.Errorf("error populating status boost author: %w", err)
+ }
}
}
- if id := status.InReplyToAccountID; id != "" {
- // Set in-reply-to status' author account
- status.InReplyToAccount, err = s.state.DB.GetAccountByID(ctx, id)
+ if !status.AttachmentsPopulated() {
+ // Status attachments are out-of-date with IDs, repopulate.
+ status.Attachments, err = s.state.DB.GetAttachmentsByIDs(
+ ctx, // these are already barebones
+ status.AttachmentIDs,
+ )
if err != nil {
- return nil, fmt.Errorf("error getting in reply to status account: %w", err)
+ return fmt.Errorf("error populating status attachments: %w", err)
}
}
- if len(status.EmojiIDs) > 0 {
- // Fetch status emojis
- status.Emojis, err = s.state.DB.GetEmojisByIDs(ctx, status.EmojiIDs)
+ // TODO: once we don't fetch using relations.
+ // if !status.TagsPopulated() {
+ // }
+
+ if !status.MentionsPopulated() {
+ // Status mentions are out-of-date with IDs, repopulate.
+ status.Mentions, err = s.state.DB.GetMentions(
+ ctx, // leave fully populated for now
+ status.MentionIDs,
+ )
if err != nil {
- return nil, fmt.Errorf("error getting status emojis: %w", err)
+ return fmt.Errorf("error populating status mentions: %w", err)
}
}
- if len(status.MentionIDs) > 0 {
- // Fetch status mentions
- status.Mentions, err = s.state.DB.GetMentions(ctx, status.MentionIDs)
+ if !status.EmojisPopulated() {
+ // Status emojis are out-of-date with IDs, repopulate.
+ status.Emojis, err = s.state.DB.GetEmojisByIDs(
+ ctx, // these are already barebones
+ status.EmojiIDs,
+ )
if err != nil {
- return nil, fmt.Errorf("error getting status mentions: %w", err)
+ return fmt.Errorf("error populating status emojis: %w", err)
}
}
- return status, nil
+ return nil
}
func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) db.Error {
@@ -239,12 +301,16 @@ func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) db.Er
})
})
if err != nil {
- // already processed
return err
}
for _, id := range status.AttachmentIDs {
- // Clear updated media attachment IDs from cache
+ // Invalidate media attachments from cache.
+ //
+ // NOTE: this is needed due to the way in which
+ // we upload status attachments, and only after
+ // update them with a known status ID. This is
+ // not the case for header/avatar attachments.
s.state.Caches.GTS.Media().Invalidate("ID", id)
}
@@ -322,14 +388,19 @@ func (s *statusDB) UpdateStatus(ctx context.Context, status *gtsmodel.Status, co
return err
}
+ // Invalidate status from database lookups.
+ s.state.Caches.GTS.Status().Invalidate("ID", status.ID)
+
for _, id := range status.AttachmentIDs {
- // Clear updated media attachment IDs from cache
+ // Invalidate media attachments from cache.
+ //
+ // NOTE: this is needed due to the way in which
+ // we upload status attachments, and only after
+ // update them with a known status ID. This is
+ // not the case for header/avatar attachments.
s.state.Caches.GTS.Media().Invalidate("ID", id)
}
- // Drop any old status value from cache by this ID
- s.state.Caches.GTS.Status().Invalidate("ID", status.ID)
-
return nil
}
@@ -367,8 +438,12 @@ func (s *statusDB) DeleteStatusByID(ctx context.Context, id string) db.Error {
return err
}
- // Drop any old value from cache by this ID
+ // Invalidate status from database lookups.
s.state.Caches.GTS.Status().Invalidate("ID", id)
+
+ // Invalidate status from all visibility lookups.
+ s.state.Caches.Visibility.Invalidate("ItemID", id)
+
return nil
}