자체 호스팅된 Discourse 사이트에서 ReactionLikeSynchronizer가 사용자 작업을 위해 500/504 에러를 반복적으로 트리거하는 문제가 발생했습니다.
해당 작업은 모든 항목이 삭제된 것으로 표시되어 있음에도 불구하고, 매시간 동일한 26,766개의 PostAction 좋아요를 반복적으로 폐기(trash)하고 있는 것으로 보입니다. 이러한 긴 동기화 실행 동안에는 일반적인 사용자 요청이 PostgreSQL에서 차단되어 Pitchfork의 30초 타임아웃에 도달하는 현상이 관찰됩니다.
환경
- 버전 v2026.10.0-latest-277-ge1f6bd561의 자체 호스팅 Discourse
- PostgreSQL 18.4
discourse_reactions_like_sync_enabled = true- 오래된 사이트이므로 과거의 리액션 데이터가 매우 많습니다
사용자 보고 사항
- 리액션이 가끔 실패하여 "무언가 잘못되었습니다"라는 메시지가 표시됩니다.
- 사용자가 방문한 토픽이 가끔 읽지 않음 상태로 남아 있습니다.
- 보고 사항은 매시간 정각 부근에 집중되어 있습니다.
로그 및 데이터
13:58:58 ReactionLikeSynchronizer 시작
13:59:50 Pitchfork 워커 타임아웃 / 500
14:00:02 Pitchfork 워커 타임아웃 / 500
14:00:18 Pitchfork 워커 타임아웃 / 500
14:00:48 ReactionLikeSynchronizer 완료
15:00:09 동기화 시작
15:01:16 요청 타임아웃 / 500
15:01:40 요청 타임아웃 / 500
15:01:45 동기화 완료
동기화기 출력
Inserted 0 post action likes, recovered 0 trashed post action likes. (0 total)
Trashed 26766 post action likes.
주목할 점은 제가 확인한 모든 실행에서 동일한 26,766이라는 숫자가 나타나는 것입니다.
데이터베이스 내용
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
참고 사항
참고로, ReactionLikeSynchronizer#trash_excluded_post_actions는 이미 삭제된 상태여도 제외된 좋아요를 매칭하는 것처럼 보입니다. 쿼리는 다음과 같습니다:
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
주목할 점은 AND post_actions.deleted_at IS NULL과 같은 조건으로 삭제된 작업을 제외하지 않는다는 것입니다.
이로 인해 매시간 동일한 행 집합이 재처리되는 것으로 보입니다. 필요 시 스택 트레이스를 추가할 수 있습니다.
임시 조치로 discourse_reactions_like_sync_enabled를 비활성화했으며, 매시간 발생하는 에러가 멈추는지 모니터링하고 있습니다.