diff --git a/internal/processing/workers/fromclientapi.go b/internal/processing/workers/fromclientapi.go index 7f1b5780c..1a37341f8 100644 --- a/internal/processing/workers/fromclientapi.go +++ b/internal/processing/workers/fromclientapi.go @@ -251,10 +251,7 @@ func (p *clientAPI) CreateStatus(ctx context.Context, cMsg *messages.FromClientA // If pending approval is true then status must // reply to a status (either one of ours or a // remote) that requires approval for the reply. - pendingApproval := util.PtrOrValue( - status.PendingApproval, - false, - ) + pendingApproval := util.PtrOrZero(status.PendingApproval) switch { case pendingApproval && !status.PreApproved: @@ -816,7 +813,7 @@ func (p *clientAPI) UndoAnnounce(ctx context.Context, cMsg *messages.FromClientA } // Update stats for the origin account. - if err := p.utils.decrementStatusesCount(ctx, cMsg.Origin); err != nil { + if err := p.utils.decrementStatusesCount(ctx, cMsg.Origin, status); err != nil { log.Errorf(ctx, "error updating account stats: %v", err) } @@ -873,7 +870,7 @@ func (p *clientAPI) DeleteStatus(ctx context.Context, cMsg *messages.FromClientA } // Update stats for the origin account. - if err := p.utils.decrementStatusesCount(ctx, cMsg.Origin); err != nil { + if err := p.utils.decrementStatusesCount(ctx, cMsg.Origin, status); err != nil { log.Errorf(ctx, "error updating account stats: %v", err) } diff --git a/internal/processing/workers/fromfediapi.go b/internal/processing/workers/fromfediapi.go index 63d1f0d16..31df9d284 100644 --- a/internal/processing/workers/fromfediapi.go +++ b/internal/processing/workers/fromfediapi.go @@ -845,7 +845,7 @@ func (p *fediAPI) DeleteStatus(ctx context.Context, fMsg *messages.FromFediAPI) } // Update stats for the remote account. - if err := p.utils.decrementStatusesCount(ctx, fMsg.Requesting); err != nil { + if err := p.utils.decrementStatusesCount(ctx, fMsg.Requesting, status); err != nil { log.Errorf(ctx, "error updating account stats: %v", err) } diff --git a/internal/processing/workers/util.go b/internal/processing/workers/util.go index 7f6c259de..49c6183a4 100644 --- a/internal/processing/workers/util.go +++ b/internal/processing/workers/util.go @@ -256,17 +256,17 @@ func (u *utils) incrementStatusesCount( unlock := u.state.ProcessingLocks.Lock(account.URI) defer unlock() - // Populate stats. + // Ensure account stats are populated. if err := u.state.DB.PopulateAccountStats(ctx, account); err != nil { return gtserror.Newf("db error getting account stats: %w", err) } - // Update stats by incrementing status - // count by one and setting last posted. + // Update status meta for account. *account.Stats.StatusesCount++ account.Stats.LastStatusAt = status.CreatedAt - if err := u.state.DB.UpdateAccountStats( - ctx, + + // Update details in the database for stats. + if err := u.state.DB.UpdateAccountStats(ctx, account.Stats, "statuses_count", "last_status_at", @@ -280,28 +280,30 @@ func (u *utils) incrementStatusesCount( func (u *utils) decrementStatusesCount( ctx context.Context, account *gtsmodel.Account, + status *gtsmodel.Status, ) error { // Lock on this account since we're changing stats. unlock := u.state.ProcessingLocks.Lock(account.URI) defer unlock() - // Populate stats. + // Ensure account stats are populated. if err := u.state.DB.PopulateAccountStats(ctx, account); err != nil { return gtserror.Newf("db error getting account stats: %w", err) } - // Update stats by decrementing - // status count by one. - // - // Clamp to 0 to avoid funny business. - *account.Stats.StatusesCount-- - if *account.Stats.StatusesCount < 0 { - *account.Stats.StatusesCount = 0 + // Update status meta for account (safely checking for zero value). + *account.Stats.StatusesCount = util.Decr(*account.Stats.StatusesCount) + + if !status.PinnedAt.IsZero() { + // Update status pinned count for account (safely checking for zero value). + *account.Stats.StatusesPinnedCount = util.Decr(*account.Stats.StatusesPinnedCount) } - if err := u.state.DB.UpdateAccountStats( - ctx, + + // Update details in the database for stats. + if err := u.state.DB.UpdateAccountStats(ctx, account.Stats, "statuses_count", + "statuses_pinned_count", ); err != nil { return gtserror.Newf("db error updating account stats: %w", err) } @@ -317,7 +319,7 @@ func (u *utils) incrementFollowersCount( unlock := u.state.ProcessingLocks.Lock(account.URI) defer unlock() - // Populate stats. + // Ensure account stats are populated. if err := u.state.DB.PopulateAccountStats(ctx, account); err != nil { return gtserror.Newf("db error getting account stats: %w", err) } @@ -344,7 +346,7 @@ func (u *utils) decrementFollowersCount( unlock := u.state.ProcessingLocks.Lock(account.URI) defer unlock() - // Populate stats. + // Ensure account stats are populated. if err := u.state.DB.PopulateAccountStats(ctx, account); err != nil { return gtserror.Newf("db error getting account stats: %w", err) } @@ -376,7 +378,7 @@ func (u *utils) incrementFollowingCount( unlock := u.state.ProcessingLocks.Lock(account.URI) defer unlock() - // Populate stats. + // Ensure account stats are populated. if err := u.state.DB.PopulateAccountStats(ctx, account); err != nil { return gtserror.Newf("db error getting account stats: %w", err) } @@ -403,7 +405,7 @@ func (u *utils) decrementFollowingCount( unlock := u.state.ProcessingLocks.Lock(account.URI) defer unlock() - // Populate stats. + // Ensure account stats are populated. if err := u.state.DB.PopulateAccountStats(ctx, account); err != nil { return gtserror.Newf("db error getting account stats: %w", err) } @@ -435,7 +437,7 @@ func (u *utils) incrementFollowRequestsCount( unlock := u.state.ProcessingLocks.Lock(account.URI) defer unlock() - // Populate stats. + // Ensure account stats are populated. if err := u.state.DB.PopulateAccountStats(ctx, account); err != nil { return gtserror.Newf("db error getting account stats: %w", err) } @@ -462,7 +464,7 @@ func (u *utils) decrementFollowRequestsCount( unlock := u.state.ProcessingLocks.Lock(account.URI) defer unlock() - // Populate stats. + // Ensure account stats are populated. if err := u.state.DB.PopulateAccountStats(ctx, account); err != nil { return gtserror.Newf("db error getting account stats: %w", err) } diff --git a/internal/util/math.go b/internal/util/math.go index e1850f772..f6e78fa1c 100644 --- a/internal/util/math.go +++ b/internal/util/math.go @@ -23,6 +23,15 @@ type Number interface { ~uintptr | ~float32 | ~float64 } +// Decr performs a safe decrement of +// n, clamping minimum value at zero. +func Decr[N Number](n N) N { + if n <= 0 { + return 0 + } + return n - 1 +} + // Div performs a safe division of // n1 and n2, checking for zero n2. In the // case of zero n2, zero is returned.