ReactionLikeSynchronizer يعيد معالجة الإعجابات المنشورة في السلة كل ساعة

واجهنا مشكلة في موقع Discourse مُستضاف ذاتيًا، حيث يبدو أن ReactionLikeSynchronizer يتسبب في أخطاء 500/504 لإجراءات المستخدمين.

يبدو أن المهمة تقوم بإعادة رمي 26,766 إعجابًا من نوع PostAction في سلة المهملات كل ساعة، على الرغم من أن جميعها تم وضع علامة “محذوف” عليها بالفعل. خلال عمليات المزامنة الأطول هذه، يبدو أن طلبات المستخدمين العادية تتعطل في PostgreSQL وتصل إلى حد انتهاء المهلة الزمني (Timeout) الخاص بـ Pitchfork البالغ 30 ثانية.

البيئة (Environment)

  • موقع Discourse مُستضاف ذاتيًا بالإصدار v2026.10.0-latest-277-ge1f6bd561
  • PostgreSQL 18.4
  • discourse_reactions_like_sync_enabled = true
  • هذا موقع قديم، لذا يوجد الكثير من بيانات التفاعلات التاريخية

تقارير المستخدمين

  • تفشل التفاعلات أحيانًا وتنتج رسالة “حدث خطأ ما” (Something went wrong).
  • تظل بعض المواضيع التي زارها المستخدم موسومة كغير مقروءة
  • تتجمع التقارير حول مرة واحدة في الساعة في بداية كل ساعة.

السجلات والبيانات

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.

ومن اللافت للنظر أن العدد نفسه 26766 يظهر في كل عملية تشغيل قمت بفحصها.

محتويات قاعدة البيانات

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.

يبدو أن هذا يؤدي إلى إعادة معالجة نفس مجموعة الصفوف كل ساعة. يمكنني إضافة أثر المكدس (Stack Trace) إذا لزم الأمر.

كحل مؤقت، قمت بتعطيل discourse_reactions_like_sync_enabled وأراقب ما إذا كانت الأخطاء ستتوقف عن الحدوث كل ساعة.

تم التعديل لإصلاح الأخطاء الإملائية وتصحيح الاستعلام في قسم “ملاحظات”.