We’ve encountered an issue on a self-hosted Discourse site in which the ReactionLikeSynchronizer seems to be triggering 500/504 errors for user actions.
The job appears to be repeatedly trashing the same 26,766 PostAction likes every hour, even though all of them have been marked as deleted. During these longer sync runs, normal user requests seem to block in PostgreSQL and hit Pitchfork’s 30-second timeout.
Environment
- Self-hosted Discourse on version v2026.10.0-latest-277-ge1f6bd561
- PostgreSQL 18.4
discourse_reactions_like_sync_enabled = true- This is an older site, so there is a lot of historical reaction data
User Reports
- Reactions sometimes fail and produce a “Something went wrong” message.
- Topics a user visited sometimes remain marked unread
- Reports are clustered around once per hour at the top of the hour.
Logs and Data
13:58:58 ReactionLikeSynchronizer starts
13:59:50 Pitchfork worker timeout / 500
14:00:02 Pitchfork worker timeout / 500
14:00:18 Pitchfork worker timeout / 500
14:00:48 ReactionLikeSynchronizer completes
15:00:09 sync starts
15:01:16 request timeout / 500
15:01:40 request timeout / 500
15:01:45 sync completes
Synchronizer Output
Inserted 0 post action likes, recovered 0 trashed post action likes. (0 total)
Trashed 26766 post action likes.
Notably, the same 26766 count shows up on every run I checked.
Database Contents
SELECT
COUNT(*) AS total,
COUNT(*) FILTER (WHERE pa.deleted_at IS NULL) AS currently_active,
COUNT(*) FILTER (WHERE pa.deleted_at IS NOT NULL) AS already_trashed
FROM post_actions pa
JOIN discourse_reactions_reaction_users ru
ON ru.user_id = pa.user_id
AND ru.post_id = pa.post_id
JOIN discourse_reactions_reactions r
ON r.id = ru.reaction_id
WHERE pa.post_action_type_id = 2
AND r.reaction_value IN (
'-1','poop','face_with_symbols_on_mouth','enraged_face',
'face_vomiting','nauseated_face','angry','kitchen_knife',
'water_pistol','bomb','clown_face','unamused_face','roll_eyes',
'confused','smirking_face','disappointed_face','pensive_face',
'lying_face','weary_face','tired_face','confounded_face',
'persevering_face','smiling_face_with_horns','angry_face_with_horns',
'cigarette','person_gesturing_no','prohibited','no_entry',
'dagger','cross_mark'
);
total | currently_active | already_trashed
-------+------------------+-----------------
26766 | 0 | 26766
Notes
For what it’s worth, it looks like ReactionLikeSynchronizer#trash_excluded_post_actions matches likes that have been excluded even if they have already been deleted. The query looks like this:
UPDATE post_actions
SET deleted_at = NOW()
FROM discourse_reactions_reaction_users ru
INNER JOIN discourse_reactions_reactions
ON discourse_reactions_reactions.id = ru.reaction_id
WHERE post_actions.user_id = ru.user_id
AND post_actions.post_id = ru.post_id
AND post_actions.post_action_type_id = :like
AND discourse_reactions_reactions.reaction_value IN (:excluded_from_like)
RETURNING post_actions.id
Notably, this doesn’t exclude deleted actions via something like AND post_actions.deleted_at IS NULL.
This appears to result in the same set of rows being reprocessed every hour. I can add a stack trace, if necessary.
As a workaround, I’ve disabled discourse_reactions_like_sync_enabled and am monitoring whether the errors stop occurring every hour.