summaryrefslogtreecommitdiff
path: root/internal/processing/status/pin.go
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2024-04-16 13:10:13 +0200
committerLibravatar GitHub <noreply@github.com>2024-04-16 13:10:13 +0200
commit3cceed11b28b5f42a653d85ed779d652fd8c26ad (patch)
tree0a7f0994e477609ca705a45f382dfb62056b196e /internal/processing/status/pin.go
parent[performance] cached oauth database types (#2838) (diff)
downloadgotosocial-3cceed11b28b5f42a653d85ed779d652fd8c26ad.tar.xz
[feature/performance] Store account stats in separate table (#2831)
* [feature/performance] Store account stats in separate table, get stats from remote * test account stats * add some missing increment / decrement calls * change stats function signatures * rejig logging a bit * use lock when updating stats
Diffstat (limited to 'internal/processing/status/pin.go')
-rw-r--r--internal/processing/status/pin.go56
1 files changed, 52 insertions, 4 deletions
diff --git a/internal/processing/status/pin.go b/internal/processing/status/pin.go
index 9a4a4b266..d0688331b 100644
--- a/internal/processing/status/pin.go
+++ b/internal/processing/status/pin.go
@@ -82,18 +82,26 @@ func (p *Processor) PinCreate(ctx context.Context, requestingAccount *gtsmodel.A
return nil, errWithCode
}
+ // Get a lock on this account.
+ unlock := p.state.AccountLocks.Lock(requestingAccount.URI)
+ defer unlock()
+
if !targetStatus.PinnedAt.IsZero() {
err := errors.New("status already pinned")
return nil, gtserror.NewErrorUnprocessableEntity(err, err.Error())
}
- pinnedCount, err := p.state.DB.CountAccountPinned(ctx, requestingAccount.ID)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking number of pinned statuses: %w", err))
+ // Ensure account stats populated.
+ if requestingAccount.Stats == nil {
+ if err := p.state.DB.PopulateAccountStats(ctx, requestingAccount); err != nil {
+ err = gtserror.Newf("db error getting account stats: %w", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
}
+ pinnedCount := *requestingAccount.Stats.StatusesPinnedCount
if pinnedCount >= allowedPinnedCount {
- err = fmt.Errorf("status pin limit exceeded, you've already pinned %d status(es) out of %d", pinnedCount, allowedPinnedCount)
+ err := fmt.Errorf("status pin limit exceeded, you've already pinned %d status(es) out of %d", pinnedCount, allowedPinnedCount)
return nil, gtserror.NewErrorUnprocessableEntity(err, err.Error())
}
@@ -103,6 +111,17 @@ func (p *Processor) PinCreate(ctx context.Context, requestingAccount *gtsmodel.A
return nil, gtserror.NewErrorInternalError(err)
}
+ // Update account stats.
+ *requestingAccount.Stats.StatusesPinnedCount++
+ if err := p.state.DB.UpdateAccountStats(
+ ctx,
+ requestingAccount.Stats,
+ "statuses_pinned_count",
+ ); err != nil {
+ err = gtserror.Newf("db error updating stats: %w", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
if err := p.c.InvalidateTimelinedStatus(ctx, requestingAccount.ID, targetStatusID); err != nil {
err = gtserror.Newf("error invalidating status from timelines: %w", err)
return nil, gtserror.NewErrorInternalError(err)
@@ -128,16 +147,45 @@ func (p *Processor) PinRemove(ctx context.Context, requestingAccount *gtsmodel.A
return nil, errWithCode
}
+ // Get a lock on this account.
+ unlock := p.state.AccountLocks.Lock(requestingAccount.URI)
+ defer unlock()
+
if targetStatus.PinnedAt.IsZero() {
+ // Status already not pinned.
return p.c.GetAPIStatus(ctx, requestingAccount, targetStatus)
}
+ // Ensure account stats populated.
+ if requestingAccount.Stats == nil {
+ if err := p.state.DB.PopulateAccountStats(ctx, requestingAccount); err != nil {
+ err = gtserror.Newf("db error getting account stats: %w", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ }
+
targetStatus.PinnedAt = time.Time{}
if err := p.state.DB.UpdateStatus(ctx, targetStatus, "pinned_at"); err != nil {
err = gtserror.Newf("db error unpinning status: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
+ // Update account stats.
+ //
+ // Clamp to 0 to avoid funny business.
+ *requestingAccount.Stats.StatusesPinnedCount--
+ if *requestingAccount.Stats.StatusesPinnedCount < 0 {
+ *requestingAccount.Stats.StatusesPinnedCount = 0
+ }
+ if err := p.state.DB.UpdateAccountStats(
+ ctx,
+ requestingAccount.Stats,
+ "statuses_pinned_count",
+ ); err != nil {
+ err = gtserror.Newf("db error updating stats: %w", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
if err := p.c.InvalidateTimelinedStatus(ctx, requestingAccount.ID, targetStatusID); err != nil {
err = gtserror.Newf("error invalidating status from timelines: %w", err)
return nil, gtserror.NewErrorInternalError(err)