diff options
| author | 2025-12-01 13:19:47 +0100 | |
|---|---|---|
| committer | 2026-01-22 13:26:33 +0100 | |
| commit | a4aad7af1a48cc1f28420b64a44d82e023062a0e (patch) | |
| tree | 3f909b6669da5c0369ed7a8bad9447dfad5fcbff /internal/processing/status/boost.go | |
| parent | [bugfix] Return unboosted status with reblogged=false (#4593) (diff) | |
| download | gotosocial-a4aad7af1a48cc1f28420b64a44d82e023062a0e.tar.xz | |
[bugfix] potential race condition on status unboost (#4596)
Moves deletion of a status boost wrapper to the API handler itself, but leaves the side-effects in the worker processing function. Should prevent race conditions resulting from multiple attempted unboosts and brings it more inline with how we perform undo fave.
Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4596
Reviewed-by: tobi <kipvandenbos@noreply.codeberg.org>
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
Diffstat (limited to 'internal/processing/status/boost.go')
| -rw-r--r-- | internal/processing/status/boost.go | 47 |
1 files changed, 31 insertions, 16 deletions
diff --git a/internal/processing/status/boost.go b/internal/processing/status/boost.go index d3fa87255..0f4c020a8 100644 --- a/internal/processing/status/boost.go +++ b/internal/processing/status/boost.go @@ -199,26 +199,41 @@ func (p *Processor) BoostRemove( return nil, gtserror.NewErrorInternalError(err) } - if boost != nil { - // Status was boosted. Process unboost side effects asynchronously. - p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{ - APObjectType: ap.ActivityAnnounce, - APActivityType: ap.ActivityUndo, - GTSModel: boost, - Origin: requester, - Target: target.Account, - }) + // Convert the target status to an API status. + apiStatus, errWithCode := p.c.GetAPIStatus(ctx, + requester, + target, + ) + if err != nil { + return nil, errWithCode } - // For client convenience, mark the status as unboosted - // even though side effects probably haven't completed yet. - unboostedStatus, errWithCode := p.c.GetAPIStatus(ctx, requester, target) - if errWithCode != nil { - return nil, errWithCode + if boost == nil { + // Status wasn't boosted, + // simply return here. + return apiStatus, nil } - unboostedStatus.Reblogged = false - return unboostedStatus, nil + // Delete boost wrapper status from the database. + err = p.state.DB.DeleteStatusByID(ctx, boost.ID) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err := gtserror.Newf("db error deleting status: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + // Status was boosted. Process unboost side effects asynchronously. + p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{ + APObjectType: ap.ActivityAnnounce, + APActivityType: ap.ActivityUndo, + GTSModel: boost, + Origin: requester, + Target: target.Account, + }) + + // Unmark status as boosted. + apiStatus.Reblogged = false + + return apiStatus, nil } // StatusBoostedBy returns a slice of accounts that have boosted the given status, filtered according to privacy settings. |
