diff options
author | 2024-04-16 13:10:13 +0200 | |
---|---|---|
committer | 2024-04-16 13:10:13 +0200 | |
commit | 3cceed11b28b5f42a653d85ed779d652fd8c26ad (patch) | |
tree | 0a7f0994e477609ca705a45f382dfb62056b196e /internal/processing/status | |
parent | [performance] cached oauth database types (#2838) (diff) | |
download | gotosocial-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')
-rw-r--r-- | internal/processing/status/pin.go | 56 |
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) |